Add form value persistence across cache revalidation re-renders 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
7.3 KiB
Executable File
7.3 KiB
Executable File
name, description
| name | description |
|---|---|
| modals | Creating modal dialogs in RSX including alerts, confirms, prompts, selects, and form modals. Use when implementing Modal.alert, Modal.confirm, Modal.prompt, Modal.form, creating modal classes extending Modal_Abstract, or building dialog-based user interactions. |
RSX Modal System
Built-in Dialog Types
All modal methods are async and return appropriate values:
| Method | Returns | Description |
|---|---|---|
Modal.alert(body) |
void |
Simple notification |
Modal.alert(title, body, buttonLabel?) |
void |
Alert with title |
Modal.confirm(body) |
boolean |
Yes/no confirmation |
Modal.confirm(title, body, confirmLabel?, cancelLabel?) |
boolean |
Confirmation with labels |
Modal.prompt(body) |
string|false |
Text input |
Modal.prompt(title, body, default?, multiline?) |
string|false |
Prompt with options |
Modal.select(body, options) |
string|false |
Dropdown selection |
Modal.select(title, body, options, default?, placeholder?) |
string|false |
Select with options |
Modal.error(error, title?) |
void |
Error with red styling |
Modal.unclosable(title, body) |
void |
Modal user cannot close |
Basic Usage Examples
// Simple alert
await Modal.alert("File saved successfully");
// Alert with title
await Modal.alert("Success", "Your changes have been saved.");
// Confirmation
if (await Modal.confirm("Are you sure you want to delete this item?")) {
await Controller.delete(id);
}
// Confirmation with custom labels
const confirmed = await Modal.confirm(
"Delete Project",
"This will permanently delete the project.\n\nThis action cannot be undone.",
"Delete", // confirm button label
"Keep Project" // cancel button label
);
// Text prompt
const name = await Modal.prompt("Enter your name:");
if (name) {
// User entered something
}
// Multiline prompt
const notes = await Modal.prompt("Notes", "Enter description:", "", true);
// Selection dropdown
const choice = await Modal.select("Choose an option:", [
{value: 'a', label: 'Option A'},
{value: 'b', label: 'Option B'}
]);
// Unclosable modal (for critical operations)
Modal.unclosable("Processing", "Please wait...");
await long_operation();
await Modal.close(); // Must close programmatically
Text formatting: Use \n\n for paragraph breaks in modal body text.
Form Modals
For complex data entry, use Modal.form():
const result = await Modal.form({
title: "Edit User",
component: "User_Form", // Component name (must implement vals())
component_args: {data: user}, // Args passed to component
max_width: 800, // Width in pixels (default: 800)
on_submit: async (form) => {
const response = await User_Controller.save(form.vals());
if (response.errors) {
Form_Utils.apply_form_errors(form.$, response.errors);
return false; // Keep modal open
}
return response.data; // Close modal and return data
}
});
if (result) {
// Modal closed with data
console.log(result.id);
}
Form Component Requirements
The component used in Modal.form() must:
- Implement
vals()method (get/set form values) - Include
<div $sid="error_container"></div>for validation errors
class User_Form extends Component {
vals(values) {
if (values) {
this.$sid('name').val(values.name || '');
this.$sid('email').val(values.email || '');
return null;
} else {
return {
name: this.$sid('name').val(),
email: this.$sid('email').val()
};
}
}
}
Modal Classes (Reusable Modals)
For complex or frequently-used modals, create dedicated classes:
class Add_User_Modal extends Modal_Abstract {
static async show(initial_data = {}) {
return await Modal.form({
title: 'Add User',
component: 'User_Form',
component_args: {data: initial_data},
on_submit: async (form) => {
const response = await User_Controller.create(form.vals());
if (response.errors) {
Form_Utils.apply_form_errors(form.$, response.errors);
return false;
}
return response.data;
}
}) || false;
}
}
class Edit_User_Modal extends Modal_Abstract {
static async show(user_id) {
// Load data first
const user = await User_Model.fetch(user_id);
return await Modal.form({
title: 'Edit User',
component: 'User_Form',
component_args: {data: user},
on_submit: async (form) => {
const response = await User_Controller.update(form.vals());
if (response.errors) {
Form_Utils.apply_form_errors(form.$, response.errors);
return false;
}
return response.data;
}
}) || false;
}
}
Usage Pattern
// Create new user
const new_user = await Add_User_Modal.show();
if (new_user) {
grid.reload();
}
// Edit existing user
const updated_user = await Edit_User_Modal.show(user_id);
if (updated_user) {
// Refresh display
}
// Chain modals (page JS orchestrates, not modals)
const user = await Add_User_Modal.show();
if (user) {
await Assign_Role_Modal.show(user.id);
}
Pattern: Extend Modal_Abstract, implement static show(), return data or false.
Modal Options
Options for Modal.form():
await Modal.form({
title: "Form Title",
component: "Form_Component",
component_args: {},
max_width: 800, // Width in pixels (default: 800)
closable: true, // Allow ESC/backdrop/X to close (default: true)
submit_label: "Save", // Submit button text
cancel_label: "Cancel", // Cancel button text
on_submit: async (form) => { /* ... */ }
});
Options for Modal.show() (custom modals):
await Modal.show({
title: "Choose Action",
body: "What would you like to do?", // String, HTML, or jQuery element
max_width: 500, // Width in pixels
closable: true,
buttons: [
{label: "Cancel", value: false, class: "btn-secondary"},
{label: "Continue", value: true, class: "btn-primary", default: true}
]
});
Modal Queuing
Multiple simultaneous modal requests are queued and shown sequentially:
// All three modals queued and shown one after another
const p1 = Modal.alert("First");
const p2 = Modal.alert("Second");
const p3 = Modal.alert("Third");
await Promise.all([p1, p2, p3]);
Backdrop persists across queued modals with 500ms delay between.
Best Practices
- Use appropriate type:
alert()for info,confirm()for decisions,form()for complex input - Handle cancellations: Always check for
falsereturn value - Modal classes don't chain: Page JS orchestrates sequences, not modal classes
- No UI updates in modals: Page JS handles post-modal UI updates
- Loading states: Use
Modal.unclosable()+Modal.close()for long operations
More Information
Details: php artisan rsx:man modals