Implement JQHTML function cache ID system and fix bundle compilation Implement underscore prefix for system tables Fix JS syntax linter to support decorators and grant exception to Task system SPA: Update planning docs and wishlists with remaining features SPA: Document Navigation API abandonment and future enhancements Implement SPA browser integration with History API (Phase 1) Convert contacts view page to SPA action Convert clients pages to SPA actions and document conversion procedure SPA: Merge GET parameters and update documentation Implement SPA route URL generation in JavaScript and PHP Implement SPA bootstrap controller architecture Add SPA routing manual page (rsx:man spa) Add SPA routing documentation to CLAUDE.md Phase 4 Complete: Client-side SPA routing implementation Update get_routes() consumers for unified route structure Complete SPA Phase 3: PHP-side route type detection and is_spa flag Restore unified routes structure and Manifest_Query class Refactor route indexing and add SPA infrastructure Phase 3 Complete: SPA route registration in manifest Implement SPA Phase 2: Extract router code and test decorators Rename Jqhtml_Component to Component and complete SPA foundation setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
122 lines
11 KiB
JavaScript
Executable File
122 lines
11 KiB
JavaScript
Executable File
"use strict";
|
|
|
|
/**
|
|
* Modal_Abstract - Base class for modal orchestration classes
|
|
*
|
|
* **Philosophy**:
|
|
* Modal classes are orchestration layers that manage the lifecycle of showing
|
|
* a modal, collecting user input, and returning results. They do NOT contain
|
|
* form validation or business logic - that belongs in jqhtml components and
|
|
* controller endpoints.
|
|
*
|
|
* **Purpose**:
|
|
* - Provides a common base class for type identification
|
|
* - Enforces file naming conventions (modal classes end with _Modal)
|
|
* - Documents the modal class pattern
|
|
* - Enables framework-level features (future: discovery, validation)
|
|
*
|
|
* **Responsibilities of Modal Classes**:
|
|
* - Invoke Modal.form() / Modal.show() / Modal.confirm() with appropriate configuration
|
|
* - Handle modal lifecycle (show, submit, cancel, errors)
|
|
* - Return Promise that resolves with data or false
|
|
* - Encapsulate modal-specific UI logic
|
|
*
|
|
* **Contract**:
|
|
* All modal classes extending Modal_Abstract must implement:
|
|
* - `static async show(params)`: Primary entry point, returns Promise
|
|
*
|
|
* **Return Values**:
|
|
* - Success: Resolve with data object (e.g., created user record)
|
|
* - Cancel/Close: Resolve with false
|
|
* - Error: Show error in modal, keep open, don't resolve until user acts
|
|
*
|
|
* **Integration**:
|
|
* Modal classes use Modal.js static API (Modal.form(), Modal.show(), etc.)
|
|
* as building blocks. Form validation handled by Rsx_Form and Form_Utils.
|
|
* Page JS orchestrates modal flow but doesn't contain modal UI logic.
|
|
*
|
|
* **Pattern Examples**:
|
|
*
|
|
* Simple form modal:
|
|
* ```
|
|
* class Add_User_Modal extends Modal_Abstract {
|
|
* static async show() {
|
|
* const result = await Modal.form({
|
|
* title: 'Add User',
|
|
* component: 'Add_User_Modal_Form',
|
|
* on_submit: async (form) => {
|
|
* try {
|
|
* const values = form.vals();
|
|
* const result = await Controller.add_user(values);
|
|
* return result; // Close modal, return data
|
|
* } catch (error) {
|
|
* await form.render_error(error);
|
|
* return false; // Keep modal open
|
|
* }
|
|
* },
|
|
* });
|
|
* return result || false;
|
|
* }
|
|
* }
|
|
* ```
|
|
*
|
|
* Custom content modal:
|
|
* ```
|
|
* class Confirm_Delete_Modal extends Modal_Abstract {
|
|
* static async show({item_name}) {
|
|
* return await Modal.confirm(
|
|
* 'Confirm Delete',
|
|
* `Are you sure you want to delete ${item_name}?`
|
|
* );
|
|
* }
|
|
* }
|
|
* ```
|
|
*
|
|
* Modal with backend call:
|
|
* ```
|
|
* class Send_Invite_Modal extends Modal_Abstract {
|
|
* static async show(user_id) {
|
|
* const result = await Controller.send_invite({user_id});
|
|
* if (result.invite_url) {
|
|
* await Modal.alert('Invite Sent', result.invite_url);
|
|
* }
|
|
* return result;
|
|
* }
|
|
* }
|
|
* ```
|
|
*
|
|
* **Usage Pattern**:
|
|
* ```
|
|
* // Page JS orchestrates flow, modals handle UI
|
|
* const user = await Add_User_Modal.show();
|
|
* if (user) {
|
|
* $('.Users_DataGrid').component().reload();
|
|
* await Send_User_Invite_Modal.show(user.id);
|
|
* }
|
|
* ```
|
|
*
|
|
* **Best Practices**:
|
|
* - Keep modal classes focused: one modal = one class
|
|
* - Page JS orchestrates sequence, modal classes handle individual modals
|
|
* - Modal classes don't call each other directly
|
|
* - Modal classes don't update UI (grids, lists) - page JS does that
|
|
* - Use descriptive names ending in _Modal (Add_User_Modal, Send_Invite_Modal)
|
|
* - Place feature-specific modals in feature directory
|
|
* - Place reusable modals in theme/components/modal/
|
|
*
|
|
* **When to Use Modal Classes**:
|
|
* - Multi-step forms
|
|
* - Forms with complex validation
|
|
* - Modals called from multiple places
|
|
* - Modals with backend interactions
|
|
*
|
|
* **When NOT to Use Modal Classes**:
|
|
* - Simple alerts: `await Modal.alert('Saved!')`
|
|
* - Simple confirmations: `if (await Modal.confirm('Delete?')) {...}`
|
|
* - One-off prompts: `const name = await Modal.prompt('Enter name:')`
|
|
*/
|
|
class Modal_Abstract {
|
|
// This class provides structure and documentation for modal patterns.
|
|
// Concrete modal classes extend this and implement static show() method.
|
|
}
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJNb2RhbF9BYnN0cmFjdCJdLCJzb3VyY2VzIjpbInJzeC90aGVtZS9jb21wb25lbnRzL21vZGFsL21vZGFsX2Fic3RyYWN0LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTW9kYWxfQWJzdHJhY3QgLSBCYXNlIGNsYXNzIGZvciBtb2RhbCBvcmNoZXN0cmF0aW9uIGNsYXNzZXNcbiAqXG4gKiAqKlBoaWxvc29waHkqKjpcbiAqIE1vZGFsIGNsYXNzZXMgYXJlIG9yY2hlc3RyYXRpb24gbGF5ZXJzIHRoYXQgbWFuYWdlIHRoZSBsaWZlY3ljbGUgb2Ygc2hvd2luZ1xuICogYSBtb2RhbCwgY29sbGVjdGluZyB1c2VyIGlucHV0LCBhbmQgcmV0dXJuaW5nIHJlc3VsdHMuIFRoZXkgZG8gTk9UIGNvbnRhaW5cbiAqIGZvcm0gdmFsaWRhdGlvbiBvciBidXNpbmVzcyBsb2dpYyAtIHRoYXQgYmVsb25ncyBpbiBqcWh0bWwgY29tcG9uZW50cyBhbmRcbiAqIGNvbnRyb2xsZXIgZW5kcG9pbnRzLlxuICpcbiAqICoqUHVycG9zZSoqOlxuICogLSBQcm92aWRlcyBhIGNvbW1vbiBiYXNlIGNsYXNzIGZvciB0eXBlIGlkZW50aWZpY2F0aW9uXG4gKiAtIEVuZm9yY2VzIGZpbGUgbmFtaW5nIGNvbnZlbnRpb25zIChtb2RhbCBjbGFzc2VzIGVuZCB3aXRoIF9Nb2RhbClcbiAqIC0gRG9jdW1lbnRzIHRoZSBtb2RhbCBjbGFzcyBwYXR0ZXJuXG4gKiAtIEVuYWJsZXMgZnJhbWV3b3JrLWxldmVsIGZlYXR1cmVzIChmdXR1cmU6IGRpc2NvdmVyeSwgdmFsaWRhdGlvbilcbiAqXG4gKiAqKlJlc3BvbnNpYmlsaXRpZXMgb2YgTW9kYWwgQ2xhc3NlcyoqOlxuICogLSBJbnZva2UgTW9kYWwuZm9ybSgpIC8gTW9kYWwuc2hvdygpIC8gTW9kYWwuY29uZmlybSgpIHdpdGggYXBwcm9wcmlhdGUgY29uZmlndXJhdGlvblxuICogLSBIYW5kbGUgbW9kYWwgbGlmZWN5Y2xlIChzaG93LCBzdWJtaXQsIGNhbmNlbCwgZXJyb3JzKVxuICogLSBSZXR1cm4gUHJvbWlzZSB0aGF0IHJlc29sdmVzIHdpdGggZGF0YSBvciBmYWxzZVxuICogLSBFbmNhcHN1bGF0ZSBtb2RhbC1zcGVjaWZpYyBVSSBsb2dpY1xuICpcbiAqICoqQ29udHJhY3QqKjpcbiAqIEFsbCBtb2RhbCBjbGFzc2VzIGV4dGVuZGluZyBNb2RhbF9BYnN0cmFjdCBtdXN0IGltcGxlbWVudDpcbiAqIC0gYHN0YXRpYyBhc3luYyBzaG93KHBhcmFtcylgOiBQcmltYXJ5IGVudHJ5IHBvaW50LCByZXR1cm5zIFByb21pc2VcbiAqXG4gKiAqKlJldHVybiBWYWx1ZXMqKjpcbiAqIC0gU3VjY2VzczogUmVzb2x2ZSB3aXRoIGRhdGEgb2JqZWN0IChlLmcuLCBjcmVhdGVkIHVzZXIgcmVjb3JkKVxuICogLSBDYW5jZWwvQ2xvc2U6IFJlc29sdmUgd2l0aCBmYWxzZVxuICogLSBFcnJvcjogU2hvdyBlcnJvciBpbiBtb2RhbCwga2VlcCBvcGVuLCBkb24ndCByZXNvbHZlIHVudGlsIHVzZXIgYWN0c1xuICpcbiAqICoqSW50ZWdyYXRpb24qKjpcbiAqIE1vZGFsIGNsYXNzZXMgdXNlIE1vZGFsLmpzIHN0YXRpYyBBUEkgKE1vZGFsLmZvcm0oKSwgTW9kYWwuc2hvdygpLCBldGMuKVxuICogYXMgYnVpbGRpbmcgYmxvY2tzLiBGb3JtIHZhbGlkYXRpb24gaGFuZGxlZCBieSBSc3hfRm9ybSBhbmQgRm9ybV9VdGlscy5cbiAqIFBhZ2UgSlMgb3JjaGVzdHJhdGVzIG1vZGFsIGZsb3cgYnV0IGRvZXNuJ3QgY29udGFpbiBtb2RhbCBVSSBsb2dpYy5cbiAqXG4gKiAqKlBhdHRlcm4gRXhhbXBsZXMqKjpcbiAqXG4gKiBTaW1wbGUgZm9ybSBtb2RhbDpcbiAqIGBgYFxuICogY2xhc3MgQWRkX1VzZXJfTW9kYWwgZXh0ZW5kcyBNb2RhbF9BYnN0cmFjdCB7XG4gKiAgICAgc3RhdGljIGFzeW5jIHNob3coKSB7XG4gKiAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IE1vZGFsLmZvcm0oe1xuICogICAgICAgICAgICAgdGl0bGU6ICdBZGQgVXNlcicsXG4gKiAgICAgICAgICAgICBjb21wb25lbnQ6ICdBZGRfVXNlcl9Nb2RhbF9Gb3JtJyxcbiAqICAgICAgICAgICAgIG9uX3N1Ym1pdDogYXN5bmMgKGZvcm0pID0+IHtcbiAqICAgICAgICAgICAgICAgICB0cnkge1xuICogICAgICAgICAgICAgICAgICAgICBjb25zdCB2YWx1ZXMgPSBmb3JtLnZhbHMoKTtcbiAqICAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgQ29udHJvbGxlci5hZGRfdXNlcih2YWx1ZXMpO1xuICogICAgICAgICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0OyAvLyBDbG9zZSBtb2RhbCwgcmV0dXJuIGRhdGFcbiAqICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICogICAgICAgICAgICAgICAgICAgICBhd2FpdCBmb3JtLnJlbmRlcl9lcnJvcihlcnJvcik7XG4gKiAgICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTsgLy8gS2VlcCBtb2RhbCBvcGVuXG4gKiAgICAgICAgICAgICAgICAgfVxuICogICAgICAgICAgICAgfSxcbiAqICAgICAgICAgfSk7XG4gKiAgICAgICAgIHJldHVybiByZXN1bHQgfHwgZmFsc2U7XG4gKiAgICAgfVxuICogfVxuICogYGBgXG4gKlxuICogQ3VzdG9tIGNvbnRlbnQgbW9kYWw6XG4gKiBgYGBcbiAqIGNsYXNzIENvbmZpcm1fRGVsZXRlX01vZGFsIGV4dGVuZHMgTW9kYWxfQWJzdHJhY3Qge1xuICogICAgIHN0YXRpYyBhc3luYyBzaG93KHtpdGVtX25hbWV9KSB7XG4gKiAgICAgICAgIHJldHVybiBhd2FpdCBNb2RhbC5jb25maXJtKFxuICogICAgICAgICAgICAgJ0NvbmZpcm0gRGVsZXRlJyxcbiAqICAgICAgICAgICAgIGBBcmUgeW91IHN1cmUgeW91IHdhbnQgdG8gZGVsZXRlICR7aXRlbV9uYW1lfT9gXG4gKiAgICAgICAgICk7XG4gKiAgICAgfVxuICogfVxuICogYGBgXG4gKlxuICogTW9kYWwgd2l0aCBiYWNrZW5kIGNhbGw6XG4gKiBgYGBcbiAqIGNsYXNzIFNlbmRfSW52aXRlX01vZGFsIGV4dGVuZHMgTW9kYWxfQWJzdHJhY3Qge1xuICogICAgIHN0YXRpYyBhc3luYyBzaG93KHVzZXJfaWQpIHtcbiAqICAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgQ29udHJvbGxlci5zZW5kX2ludml0ZSh7dXNlcl9pZH0pO1xuICogICAgICAgICBpZiAocmVzdWx0Lmludml0ZV91cmwpIHtcbiAqICAgICAgICAgICAgIGF3YWl0IE1vZGFsLmFsZXJ0KCdJbnZpdGUgU2VudCcsIHJlc3VsdC5pbnZpdGVfdXJsKTtcbiAqICAgICAgICAgfVxuICogICAgICAgICByZXR1cm4gcmVzdWx0O1xuICogICAgIH1cbiAqIH1cbiAqIGBgYFxuICpcbiAqICoqVXNhZ2UgUGF0dGVybioqOlxuICogYGBgXG4gKiAvLyBQYWdlIEpTIG9yY2hlc3RyYXRlcyBmbG93LCBtb2RhbHMgaGFuZGxlIFVJXG4gKiBjb25zdCB1c2VyID0gYXdhaXQgQWRkX1VzZXJfTW9kYWwuc2hvdygpO1xuICogaWYgKHVzZXIpIHtcbiAqICAgICAkKCcuVXNlcnNfRGF0YUdyaWQnKS5jb21wb25lbnQoKS5yZWxvYWQoKTtcbiAqICAgICBhd2FpdCBTZW5kX1VzZXJfSW52aXRlX01vZGFsLnNob3codXNlci5pZCk7XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiAqKkJlc3QgUHJhY3RpY2VzKio6XG4gKiAtIEtlZXAgbW9kYWwgY2xhc3NlcyBmb2N1c2VkOiBvbmUgbW9kYWwgPSBvbmUgY2xhc3NcbiAqIC0gUGFnZSBKUyBvcmNoZXN0cmF0ZXMgc2VxdWVuY2UsIG1vZGFsIGNsYXNzZXMgaGFuZGxlIGluZGl2aWR1YWwgbW9kYWxzXG4gKiAtIE1vZGFsIGNsYXNzZXMgZG9uJ3QgY2FsbCBlYWNoIG90aGVyIGRpcmVjdGx5XG4gKiAtIE1vZGFsIGNsYXNzZXMgZG9uJ3QgdXBkYXRlIFVJIChncmlkcywgbGlzdHMpIC0gcGFnZSBKUyBkb2VzIHRoYXRcbiAqIC0gVXNlIGRlc2NyaXB0aXZlIG5hbWVzIGVuZGluZyBpbiBfTW9kYWwgKEFkZF9Vc2VyX01vZGFsLCBTZW5kX0ludml0ZV9Nb2RhbClcbiAqIC0gUGxhY2UgZmVhdHVyZS1zcGVjaWZpYyBtb2RhbHMgaW4gZmVhdHVyZSBkaXJlY3RvcnlcbiAqIC0gUGxhY2UgcmV1c2FibGUgbW9kYWxzIGluIHRoZW1lL2NvbXBvbmVudHMvbW9kYWwvXG4gKlxuICogKipXaGVuIHRvIFVzZSBNb2RhbCBDbGFzc2VzKio6XG4gKiAtIE11bHRpLXN0ZXAgZm9ybXNcbiAqIC0gRm9ybXMgd2l0aCBjb21wbGV4IHZhbGlkYXRpb25cbiAqIC0gTW9kYWxzIGNhbGxlZCBmcm9tIG11bHRpcGxlIHBsYWNlc1xuICogLSBNb2RhbHMgd2l0aCBiYWNrZW5kIGludGVyYWN0aW9uc1xuICpcbiAqICoqV2hlbiBOT1QgdG8gVXNlIE1vZGFsIENsYXNzZXMqKjpcbiAqIC0gU2ltcGxlIGFsZXJ0czogYGF3YWl0IE1vZGFsLmFsZXJ0KCdTYXZlZCEnKWBcbiAqIC0gU2ltcGxlIGNvbmZpcm1hdGlvbnM6IGBpZiAoYXdhaXQgTW9kYWwuY29uZmlybSgnRGVsZXRlPycpKSB7Li4ufWBcbiAqIC0gT25lLW9mZiBwcm9tcHRzOiBgY29uc3QgbmFtZSA9IGF3YWl0IE1vZGFsLnByb21wdCgnRW50ZXIgbmFtZTonKWBcbiAqL1xuY2xhc3MgTW9kYWxfQWJzdHJhY3Qge1xuICAgIC8vIFRoaXMgY2xhc3MgcHJvdmlkZXMgc3RydWN0dXJlIGFuZCBkb2N1bWVudGF0aW9uIGZvciBtb2RhbCBwYXR0ZXJucy5cbiAgICAvLyBDb25jcmV0ZSBtb2RhbCBjbGFzc2VzIGV4dGVuZCB0aGlzIGFuZCBpbXBsZW1lbnQgc3RhdGljIHNob3coKSBtZXRob2QuXG59XG4iXSwibWFwcGluZ3MiOiI7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNQSxjQUFjLENBQUM7RUFDakI7RUFDQTtBQUFBIiwiaWdub3JlTGlzdCI6W119
|