Fix code quality violations and exclude Manifest from checks
Document application modes (development/debug/production) Add global file drop handler, order column normalization, SPA hash fix Serve CDN assets via /_vendor/ URLs instead of merging into bundles Add production minification with license preservation Improve JSON formatting for debugging and production optimization Add CDN asset caching with CSS URL inlining for production builds Add three-mode system (development, debug, production) Update Manifest CLAUDE.md to reflect helper class architecture Refactor Manifest.php into helper classes for better organization Pre-manifest-refactor checkpoint: Add app_mode documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,10 @@
|
||||
*
|
||||
* If conditions fail, falls back to flash alert.
|
||||
* If layout display throws, falls back to flash alert.
|
||||
*
|
||||
* Error Hints:
|
||||
* Common error patterns are detected and helpful hints are appended:
|
||||
* - "this.$ is not a function" / "that.$ is not a function" → suggest this.$.find()
|
||||
*/
|
||||
class Exception_Handler {
|
||||
|
||||
@@ -51,6 +55,39 @@ class Exception_Handler {
|
||||
|
||||
Exception_Handler.display_unhandled_exception(exception, meta);
|
||||
});
|
||||
|
||||
// Patch console.error to add helpful hints for common JQHTML errors
|
||||
Exception_Handler._patch_console_error();
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch console.error to detect common error patterns and add helpful hints
|
||||
* This helps developers quickly understand common mistakes like:
|
||||
* - Using this.$ or that.$ as a function (should use this.$.find())
|
||||
*/
|
||||
static _patch_console_error() {
|
||||
const original_console_error = console.error.bind(console);
|
||||
|
||||
console.error = function(...args) {
|
||||
// Call original console.error first
|
||||
original_console_error(...args);
|
||||
|
||||
// Check if this looks like a JQHTML callback error
|
||||
if (args.length >= 2 && typeof args[0] === 'string' && args[0].includes('[JQHTML]')) {
|
||||
const error = args[1];
|
||||
const error_message = error instanceof Error ? error.message : String(error);
|
||||
|
||||
// Check for common "this.$ is not a function" or "that.$ is not a function" error
|
||||
if (error_message.includes('this.$ is not a function') ||
|
||||
error_message.includes('that.$ is not a function')) {
|
||||
original_console_error(
|
||||
'%c[Hint] Did you mean this.$.find() or that.$.find()? ' +
|
||||
'The component element (this.$) is a jQuery object, not a function.',
|
||||
'color: #0dcaf0; font-style: italic;'
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@ class Rsx_Behaviors {
|
||||
static _on_framework_core_init() {
|
||||
Rsx_Behaviors._init_ignore_invalid_anchor_links();
|
||||
Rsx_Behaviors._trim_copied_text();
|
||||
Rsx_Behaviors._init_file_drop_handler();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,4 +106,164 @@ class Rsx_Behaviors {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Global file drop handler
|
||||
*
|
||||
* Intercepts all file drag/drop operations and routes them to components
|
||||
* marked with the `rsx-droppable` class. Components receive a `file-drop`
|
||||
* event with the dropped files.
|
||||
*
|
||||
* CSS Classes:
|
||||
* - `rsx-droppable` - Marks element as a valid drop target
|
||||
* - `rsx-drop-active` - Added to all droppables during file drag
|
||||
* - `rsx-drop-target` - Added to the specific element that will receive drop
|
||||
*
|
||||
* Behavior:
|
||||
* - Single visible droppable: auto-becomes target
|
||||
* - Multiple visible droppables: must hover over specific element
|
||||
* - No-drop cursor when not over valid target
|
||||
* - Widgets handle their own file type filtering
|
||||
*/
|
||||
static _init_file_drop_handler() {
|
||||
let drag_counter = 0;
|
||||
let current_target = null;
|
||||
|
||||
// Get all visible droppable elements
|
||||
const get_visible_droppables = () => {
|
||||
return $('.rsx-droppable').filter(function() {
|
||||
const $el = $(this);
|
||||
// Must be visible and not hidden by CSS
|
||||
return $el.is(':visible') && $el.css('visibility') !== 'hidden';
|
||||
});
|
||||
};
|
||||
|
||||
// Check if event contains files
|
||||
const has_files = (e) => {
|
||||
if (e.originalEvent && e.originalEvent.dataTransfer) {
|
||||
const types = e.originalEvent.dataTransfer.types;
|
||||
return types && (types.includes('Files') || types.indexOf('Files') >= 0);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Update which element is the current target
|
||||
const update_target = ($new_target) => {
|
||||
if (current_target) {
|
||||
$(current_target).removeClass('rsx-drop-target');
|
||||
}
|
||||
current_target = $new_target ? $new_target[0] : null;
|
||||
if (current_target) {
|
||||
$(current_target).addClass('rsx-drop-target');
|
||||
}
|
||||
};
|
||||
|
||||
// Clear all drag state
|
||||
const clear_drag_state = () => {
|
||||
drag_counter = 0;
|
||||
$('.rsx-drop-active').removeClass('rsx-drop-active');
|
||||
update_target(null);
|
||||
};
|
||||
|
||||
// dragenter - file enters the window
|
||||
$(document).on('dragenter', function(e) {
|
||||
if (!has_files(e)) return;
|
||||
|
||||
drag_counter++;
|
||||
|
||||
if (drag_counter === 1) {
|
||||
// First entry - activate all droppables
|
||||
const $droppables = get_visible_droppables();
|
||||
$droppables.addClass('rsx-drop-active');
|
||||
|
||||
// If only one droppable, auto-target it
|
||||
if ($droppables.length === 1) {
|
||||
update_target($droppables.first());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// dragleave - file leaves an element
|
||||
$(document).on('dragleave', function(e) {
|
||||
if (!has_files(e)) return;
|
||||
|
||||
drag_counter--;
|
||||
|
||||
if (drag_counter <= 0) {
|
||||
clear_drag_state();
|
||||
}
|
||||
});
|
||||
|
||||
// dragover - file is over an element (fires continuously)
|
||||
$(document).on('dragover', function(e) {
|
||||
if (!has_files(e)) return;
|
||||
|
||||
e.preventDefault(); // Required to allow drop
|
||||
|
||||
const $droppables = get_visible_droppables();
|
||||
|
||||
if ($droppables.length === 0) {
|
||||
// No drop targets - show no-drop cursor
|
||||
e.originalEvent.dataTransfer.dropEffect = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
if ($droppables.length === 1) {
|
||||
// Single target - already set, allow copy
|
||||
e.originalEvent.dataTransfer.dropEffect = 'copy';
|
||||
return;
|
||||
}
|
||||
|
||||
// Multiple targets - find if we're over one
|
||||
const $hovered = $(e.target).closest('.rsx-droppable');
|
||||
|
||||
if ($hovered.length && $hovered.hasClass('rsx-drop-active')) {
|
||||
// Over a valid droppable
|
||||
update_target($hovered);
|
||||
e.originalEvent.dataTransfer.dropEffect = 'copy';
|
||||
} else {
|
||||
// Not over any droppable - show no-drop cursor
|
||||
update_target(null);
|
||||
e.originalEvent.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// drop - file is dropped
|
||||
$(document).on('drop', function(e) {
|
||||
if (!has_files(e)) return;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const files = e.originalEvent.dataTransfer.files;
|
||||
|
||||
if (current_target && files.length > 0) {
|
||||
// Trigger file-drop event on the component
|
||||
const $target = $(current_target);
|
||||
const component = $target.component();
|
||||
|
||||
if (component) {
|
||||
component.trigger('file-drop', {
|
||||
files: files,
|
||||
dataTransfer: e.originalEvent.dataTransfer,
|
||||
originalEvent: e.originalEvent
|
||||
});
|
||||
} else {
|
||||
// No component - trigger jQuery event on element directly
|
||||
$target.trigger('file-drop', {
|
||||
files: files,
|
||||
dataTransfer: e.originalEvent.dataTransfer,
|
||||
originalEvent: e.originalEvent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
clear_drag_state();
|
||||
});
|
||||
|
||||
// Handle drag end (e.g., user presses Escape)
|
||||
$(document).on('dragend', function(e) {
|
||||
clear_drag_state();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user