--- name: modals description: 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 ```javascript // 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()`: ```javascript 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: 1. Implement `vals()` method (get/set form values) 2. Include `
` for validation errors ```javascript 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: ```javascript 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 ```javascript // 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()`: ```javascript 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): ```javascript 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: ```javascript // 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 1. **Use appropriate type**: `alert()` for info, `confirm()` for decisions, `form()` for complex input 2. **Handle cancellations**: Always check for `false` return value 3. **Modal classes don't chain**: Page JS orchestrates sequences, not modal classes 4. **No UI updates in modals**: Page JS handles post-modal UI updates 5. **Loading states**: Use `Modal.unclosable()` + `Modal.close()` for long operations ## More Information Details: `php artisan rsx:man modals`