@memberjunction/ng-filter-builder 5.7.0 → 5.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/filter-builder/filter-builder.component.js +2 -2
- package/dist/lib/filter-rule/filter-rule.component.d.ts +12 -0
- package/dist/lib/filter-rule/filter-rule.component.d.ts.map +1 -1
- package/dist/lib/filter-rule/filter-rule.component.js +53 -9
- package/dist/lib/filter-rule/filter-rule.component.js.map +1 -1
- package/package.json +2 -2
|
@@ -458,11 +458,11 @@ export class FilterBuilderComponent {
|
|
|
458
458
|
i0.ɵɵconditional(!ctx.hasActiveFilters ? 12 : -1);
|
|
459
459
|
i0.ɵɵadvance();
|
|
460
460
|
i0.ɵɵconditional(ctx.showSummary && ctx.hasActiveFilters ? 13 : -1);
|
|
461
|
-
} }, dependencies: [i2.FilterGroupComponent], styles: [".filter-builder[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.filter-builder.disabled[_ngcontent-%COMP%] {\n opacity: 0.6;\n pointer-events: none;\n}\n\n\n\n.filter-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding-bottom: 12px;\n border-bottom: 1px solid #eee;\n}\n\n.filter-summary[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.filter-icon[_ngcontent-%COMP%] {\n color: #1976d2;\n font-size: 14px;\n}\n\n.filter-count[_ngcontent-%COMP%] {\n font-size: 13px;\n color: #333;\n font-weight: 500;\n}\n\n.filter-count.no-filters[_ngcontent-%COMP%] {\n color: #999;\n font-weight: 400;\n}\n\n\n\n.filter-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n}\n\n.action-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.action-btn[_ngcontent-%COMP%]:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.action-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 11px;\n}\n\n.clear-btn[_ngcontent-%COMP%] {\n background: #f5f5f5;\n color: #666;\n}\n\n.clear-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #ffebee;\n color: #c62828;\n}\n\n.apply-btn[_ngcontent-%COMP%] {\n background: #1976d2;\n color: white;\n}\n\n.apply-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #1565c0;\n}\n\n\n\n.filter-content[_ngcontent-%COMP%] {\n min-height: 60px;\n}\n\n\n\n.filter-tips[_ngcontent-%COMP%] {\n padding: 12px;\n background: #f5f9ff;\n border-radius: 6px;\n border: 1px solid #e3edfa;\n}\n\n.filter-tips[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n color: #666;\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n\n.filter-tips[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #ffc107;\n margin-top: 2px;\n}\n\n.filter-tips[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n color: #333;\n}\n\n\n\n.filter-expression-summary[_ngcontent-%COMP%] {\n background: #f8f9fa;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n margin-top: 8px;\n
|
|
461
|
+
} }, dependencies: [i2.FilterGroupComponent], styles: [".filter-builder[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.filter-builder.disabled[_ngcontent-%COMP%] {\n opacity: 0.6;\n pointer-events: none;\n}\n\n\n\n.filter-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding-bottom: 12px;\n border-bottom: 1px solid #eee;\n}\n\n.filter-summary[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.filter-icon[_ngcontent-%COMP%] {\n color: #1976d2;\n font-size: 14px;\n}\n\n.filter-count[_ngcontent-%COMP%] {\n font-size: 13px;\n color: #333;\n font-weight: 500;\n}\n\n.filter-count.no-filters[_ngcontent-%COMP%] {\n color: #999;\n font-weight: 400;\n}\n\n\n\n.filter-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n}\n\n.action-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.action-btn[_ngcontent-%COMP%]:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.action-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 11px;\n}\n\n.clear-btn[_ngcontent-%COMP%] {\n background: #f5f5f5;\n color: #666;\n}\n\n.clear-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #ffebee;\n color: #c62828;\n}\n\n.apply-btn[_ngcontent-%COMP%] {\n background: #1976d2;\n color: white;\n}\n\n.apply-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #1565c0;\n}\n\n\n\n.filter-content[_ngcontent-%COMP%] {\n min-height: 60px;\n}\n\n\n\n.filter-tips[_ngcontent-%COMP%] {\n padding: 12px;\n background: #f5f9ff;\n border-radius: 6px;\n border: 1px solid #e3edfa;\n}\n\n.filter-tips[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n color: #666;\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n\n.filter-tips[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #ffc107;\n margin-top: 2px;\n}\n\n.filter-tips[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n color: #333;\n}\n\n\n\n.filter-expression-summary[_ngcontent-%COMP%] {\n background: #f8f9fa;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n margin-top: 8px;\n transition: all 0.2s ease;\n}\n\n.filter-expression-summary.expanded[_ngcontent-%COMP%] {\n background: white;\n border-color: #d1d5db;\n}\n\n\n\n.expression-toggle[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 12px 16px;\n border: none;\n background: transparent;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: #6b7280;\n text-align: left;\n transition: all 0.15s ease;\n}\n\n.expression-toggle[_ngcontent-%COMP%]:hover {\n background: rgba(0, 0, 0, 0.03);\n color: #374151;\n}\n\n.expression-toggle[_ngcontent-%COMP%] i[_ngcontent-%COMP%]:first-child {\n font-size: 10px;\n color: #9ca3af;\n width: 12px;\n transition: transform 0.2s ease;\n}\n\n.expression-icon[_ngcontent-%COMP%] {\n color: #6366f1;\n font-size: 13px;\n}\n\n.expression-label[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.expression-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n background: #e0e7ff;\n color: #4f46e5;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n}\n\n\n\n.expression-content[_ngcontent-%COMP%] {\n padding: 0 16px 16px 16px;\n animation: _ngcontent-%COMP%_slideDown 0.2s ease;\n}\n\n@keyframes _ngcontent-%COMP%_slideDown {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.expression-text[_ngcontent-%COMP%] {\n font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', monospace;\n font-size: 13px;\n line-height: 1.8;\n color: #374151;\n padding: 16px;\n margin: 0;\n background: linear-gradient(135deg, #fafbfc 0%, #f3f4f6 100%);\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n\n\n.expression-text[_ngcontent-%COMP%] .field-name[_ngcontent-%COMP%] {\n color: #0369a1;\n font-weight: 600;\n}\n\n.expression-text[_ngcontent-%COMP%] .operator[_ngcontent-%COMP%] {\n color: #6b7280;\n font-style: italic;\n}\n\n.expression-text[_ngcontent-%COMP%] .value[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.expression-text[_ngcontent-%COMP%] .value-string[_ngcontent-%COMP%] {\n color: #059669;\n}\n\n.expression-text[_ngcontent-%COMP%] .value-number[_ngcontent-%COMP%] {\n color: #7c3aed;\n}\n\n.expression-text[_ngcontent-%COMP%] .value-boolean[_ngcontent-%COMP%] {\n font-weight: 600;\n}\n\n.expression-text[_ngcontent-%COMP%] .value-true[_ngcontent-%COMP%] {\n color: #16a34a;\n}\n\n.expression-text[_ngcontent-%COMP%] .value-false[_ngcontent-%COMP%] {\n color: #dc2626;\n}\n\n.expression-text[_ngcontent-%COMP%] .value-date[_ngcontent-%COMP%] {\n color: #c2410c;\n font-weight: 500;\n}\n\n.expression-text[_ngcontent-%COMP%] .logic-keyword[_ngcontent-%COMP%] {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n\n.expression-text[_ngcontent-%COMP%] .logic-and[_ngcontent-%COMP%] {\n background: #dbeafe;\n color: #1d4ed8;\n}\n\n.expression-text[_ngcontent-%COMP%] .logic-or[_ngcontent-%COMP%] {\n background: #fef3c7;\n color: #b45309;\n}\n\n.expression-text[_ngcontent-%COMP%] .group-bracket[_ngcontent-%COMP%] {\n color: #9333ea;\n font-weight: 700;\n font-size: 15px;\n}\n\n.expression-text[_ngcontent-%COMP%] .no-filters[_ngcontent-%COMP%] {\n color: #9ca3af;\n font-style: italic;\n}\n\n\n\n@media (max-width: 600px) {\n .filter-header[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: stretch;\n gap: 12px;\n }\n\n .filter-summary[_ngcontent-%COMP%] {\n justify-content: center;\n }\n\n .filter-actions[_ngcontent-%COMP%] {\n justify-content: center;\n }\n\n .expression-toggle[_ngcontent-%COMP%] {\n padding: 10px 12px;\n }\n\n .expression-content[_ngcontent-%COMP%] {\n padding: 0 12px 12px 12px;\n }\n\n .expression-text[_ngcontent-%COMP%] {\n font-size: 12px;\n padding: 12px;\n }\n}"] });
|
|
462
462
|
}
|
|
463
463
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FilterBuilderComponent, [{
|
|
464
464
|
type: Component,
|
|
465
|
-
args: [{ standalone: false, selector: 'mj-filter-builder', template: "<div class=\"filter-builder\" [class.disabled]=\"disabled\">\n <!-- Header with summary and actions -->\n <div class=\"filter-header\">\n <div class=\"filter-summary\">\n <span class=\"filter-icon\">\n <i class=\"fa-solid fa-filter\"></i>\n </span>\n @if (hasActiveFilters) {\n <span class=\"filter-count\">{{ getFilterCount() }} condition{{ getFilterCount() !== 1 ? 's' : '' }}</span>\n } @else {\n <span class=\"filter-count no-filters\">No filters applied</span>\n }\n </div>\n\n <div class=\"filter-actions\">\n @if (mergedConfig.showClearButton && hasActiveFilters) {\n <button\n type=\"button\"\n class=\"action-btn clear-btn\"\n (click)=\"onClear()\"\n [disabled]=\"disabled\"\n title=\"Clear all filters\">\n <i class=\"fa-solid fa-times\"></i>\n <span>Clear All</span>\n </button>\n }\n @if (mergedConfig.showApplyButton) {\n <button\n type=\"button\"\n class=\"action-btn apply-btn\"\n (click)=\"onApply()\"\n [disabled]=\"disabled\"\n title=\"Apply filters\">\n <i class=\"fa-solid fa-check\"></i>\n <span>Apply</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Filter Builder Content -->\n <div class=\"filter-content\">\n <mj-filter-group\n [filter]=\"internalFilter\"\n [fields]=\"fields\"\n [isRoot]=\"true\"\n [depth]=\"0\"\n [maxDepth]=\"mergedConfig.maxDepth\"\n [disabled]=\"disabled\"\n (filterChange)=\"onFilterChange($event)\">\n </mj-filter-group>\n </div>\n\n <!-- Quick tips (shown when no filters) -->\n @if (!hasActiveFilters) {\n <div class=\"filter-tips\">\n <p>\n <i class=\"fa-solid fa-lightbulb\"></i>\n <strong>Tip:</strong> Click \"Add Condition\" to create filter rules.\n Use \"Add Group\" for complex AND/OR combinations.\n </p>\n </div>\n }\n\n <!-- Collapsible filter expression summary -->\n @if (showSummary && hasActiveFilters) {\n <div class=\"filter-expression-summary\" [class.expanded]=\"isSummaryExpanded\">\n <button type=\"button\" class=\"expression-toggle\" (click)=\"toggleSummary()\">\n <i class=\"fa-solid\" [class.fa-chevron-right]=\"!isSummaryExpanded\" [class.fa-chevron-down]=\"isSummaryExpanded\"></i>\n <i class=\"fa-solid fa-code expression-icon\"></i>\n <span class=\"expression-label\">View Filter Expression</span>\n <span class=\"expression-badge\">{{ getFilterCount() }}</span>\n </button>\n @if (isSummaryExpanded) {\n <div class=\"expression-content\">\n <pre class=\"expression-text\" [innerHTML]=\"getFilterSummaryHtml()\"></pre>\n </div>\n }\n </div>\n }\n</div>\n", styles: [".filter-builder {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.filter-builder.disabled {\n opacity: 0.6;\n pointer-events: none;\n}\n\n/* Header */\n.filter-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding-bottom: 12px;\n border-bottom: 1px solid #eee;\n}\n\n.filter-summary {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.filter-icon {\n color: #1976d2;\n font-size: 14px;\n}\n\n.filter-count {\n font-size: 13px;\n color: #333;\n font-weight: 500;\n}\n\n.filter-count.no-filters {\n color: #999;\n font-weight: 400;\n}\n\n/* Actions */\n.filter-actions {\n display: flex;\n gap: 8px;\n}\n\n.action-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.action-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.action-btn i {\n font-size: 11px;\n}\n\n.clear-btn {\n background: #f5f5f5;\n color: #666;\n}\n\n.clear-btn:hover:not(:disabled) {\n background: #ffebee;\n color: #c62828;\n}\n\n.apply-btn {\n background: #1976d2;\n color: white;\n}\n\n.apply-btn:hover:not(:disabled) {\n background: #1565c0;\n}\n\n/* Content */\n.filter-content {\n min-height: 60px;\n}\n\n/* Tips */\n.filter-tips {\n padding: 12px;\n background: #f5f9ff;\n border-radius: 6px;\n border: 1px solid #e3edfa;\n}\n\n.filter-tips p {\n margin: 0;\n font-size: 13px;\n color: #666;\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n\n.filter-tips i {\n color: #ffc107;\n margin-top: 2px;\n}\n\n.filter-tips strong {\n color: #333;\n}\n\n/* Collapsible Filter Expression Summary */\n.filter-expression-summary {\n background: #f8f9fa;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n margin-top: 8px;\n
|
|
465
|
+
args: [{ standalone: false, selector: 'mj-filter-builder', template: "<div class=\"filter-builder\" [class.disabled]=\"disabled\">\n <!-- Header with summary and actions -->\n <div class=\"filter-header\">\n <div class=\"filter-summary\">\n <span class=\"filter-icon\">\n <i class=\"fa-solid fa-filter\"></i>\n </span>\n @if (hasActiveFilters) {\n <span class=\"filter-count\">{{ getFilterCount() }} condition{{ getFilterCount() !== 1 ? 's' : '' }}</span>\n } @else {\n <span class=\"filter-count no-filters\">No filters applied</span>\n }\n </div>\n\n <div class=\"filter-actions\">\n @if (mergedConfig.showClearButton && hasActiveFilters) {\n <button\n type=\"button\"\n class=\"action-btn clear-btn\"\n (click)=\"onClear()\"\n [disabled]=\"disabled\"\n title=\"Clear all filters\">\n <i class=\"fa-solid fa-times\"></i>\n <span>Clear All</span>\n </button>\n }\n @if (mergedConfig.showApplyButton) {\n <button\n type=\"button\"\n class=\"action-btn apply-btn\"\n (click)=\"onApply()\"\n [disabled]=\"disabled\"\n title=\"Apply filters\">\n <i class=\"fa-solid fa-check\"></i>\n <span>Apply</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Filter Builder Content -->\n <div class=\"filter-content\">\n <mj-filter-group\n [filter]=\"internalFilter\"\n [fields]=\"fields\"\n [isRoot]=\"true\"\n [depth]=\"0\"\n [maxDepth]=\"mergedConfig.maxDepth\"\n [disabled]=\"disabled\"\n (filterChange)=\"onFilterChange($event)\">\n </mj-filter-group>\n </div>\n\n <!-- Quick tips (shown when no filters) -->\n @if (!hasActiveFilters) {\n <div class=\"filter-tips\">\n <p>\n <i class=\"fa-solid fa-lightbulb\"></i>\n <strong>Tip:</strong> Click \"Add Condition\" to create filter rules.\n Use \"Add Group\" for complex AND/OR combinations.\n </p>\n </div>\n }\n\n <!-- Collapsible filter expression summary -->\n @if (showSummary && hasActiveFilters) {\n <div class=\"filter-expression-summary\" [class.expanded]=\"isSummaryExpanded\">\n <button type=\"button\" class=\"expression-toggle\" (click)=\"toggleSummary()\">\n <i class=\"fa-solid\" [class.fa-chevron-right]=\"!isSummaryExpanded\" [class.fa-chevron-down]=\"isSummaryExpanded\"></i>\n <i class=\"fa-solid fa-code expression-icon\"></i>\n <span class=\"expression-label\">View Filter Expression</span>\n <span class=\"expression-badge\">{{ getFilterCount() }}</span>\n </button>\n @if (isSummaryExpanded) {\n <div class=\"expression-content\">\n <pre class=\"expression-text\" [innerHTML]=\"getFilterSummaryHtml()\"></pre>\n </div>\n }\n </div>\n }\n</div>\n", styles: [".filter-builder {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.filter-builder.disabled {\n opacity: 0.6;\n pointer-events: none;\n}\n\n/* Header */\n.filter-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding-bottom: 12px;\n border-bottom: 1px solid #eee;\n}\n\n.filter-summary {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.filter-icon {\n color: #1976d2;\n font-size: 14px;\n}\n\n.filter-count {\n font-size: 13px;\n color: #333;\n font-weight: 500;\n}\n\n.filter-count.no-filters {\n color: #999;\n font-weight: 400;\n}\n\n/* Actions */\n.filter-actions {\n display: flex;\n gap: 8px;\n}\n\n.action-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.action-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.action-btn i {\n font-size: 11px;\n}\n\n.clear-btn {\n background: #f5f5f5;\n color: #666;\n}\n\n.clear-btn:hover:not(:disabled) {\n background: #ffebee;\n color: #c62828;\n}\n\n.apply-btn {\n background: #1976d2;\n color: white;\n}\n\n.apply-btn:hover:not(:disabled) {\n background: #1565c0;\n}\n\n/* Content */\n.filter-content {\n min-height: 60px;\n}\n\n/* Tips */\n.filter-tips {\n padding: 12px;\n background: #f5f9ff;\n border-radius: 6px;\n border: 1px solid #e3edfa;\n}\n\n.filter-tips p {\n margin: 0;\n font-size: 13px;\n color: #666;\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n\n.filter-tips i {\n color: #ffc107;\n margin-top: 2px;\n}\n\n.filter-tips strong {\n color: #333;\n}\n\n/* Collapsible Filter Expression Summary */\n.filter-expression-summary {\n background: #f8f9fa;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n margin-top: 8px;\n transition: all 0.2s ease;\n}\n\n.filter-expression-summary.expanded {\n background: white;\n border-color: #d1d5db;\n}\n\n/* Toggle Button */\n.expression-toggle {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 12px 16px;\n border: none;\n background: transparent;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: #6b7280;\n text-align: left;\n transition: all 0.15s ease;\n}\n\n.expression-toggle:hover {\n background: rgba(0, 0, 0, 0.03);\n color: #374151;\n}\n\n.expression-toggle i:first-child {\n font-size: 10px;\n color: #9ca3af;\n width: 12px;\n transition: transform 0.2s ease;\n}\n\n.expression-icon {\n color: #6366f1;\n font-size: 13px;\n}\n\n.expression-label {\n flex: 1;\n}\n\n.expression-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n background: #e0e7ff;\n color: #4f46e5;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n}\n\n/* Expression Content */\n.expression-content {\n padding: 0 16px 16px 16px;\n animation: slideDown 0.2s ease;\n}\n\n@keyframes slideDown {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.expression-text {\n font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', monospace;\n font-size: 13px;\n line-height: 1.8;\n color: #374151;\n padding: 16px;\n margin: 0;\n background: linear-gradient(135deg, #fafbfc 0%, #f3f4f6 100%);\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* Syntax Highlighting */\n.expression-text .field-name {\n color: #0369a1;\n font-weight: 600;\n}\n\n.expression-text .operator {\n color: #6b7280;\n font-style: italic;\n}\n\n.expression-text .value {\n font-weight: 500;\n}\n\n.expression-text .value-string {\n color: #059669;\n}\n\n.expression-text .value-number {\n color: #7c3aed;\n}\n\n.expression-text .value-boolean {\n font-weight: 600;\n}\n\n.expression-text .value-true {\n color: #16a34a;\n}\n\n.expression-text .value-false {\n color: #dc2626;\n}\n\n.expression-text .value-date {\n color: #c2410c;\n font-weight: 500;\n}\n\n.expression-text .logic-keyword {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n\n.expression-text .logic-and {\n background: #dbeafe;\n color: #1d4ed8;\n}\n\n.expression-text .logic-or {\n background: #fef3c7;\n color: #b45309;\n}\n\n.expression-text .group-bracket {\n color: #9333ea;\n font-weight: 700;\n font-size: 15px;\n}\n\n.expression-text .no-filters {\n color: #9ca3af;\n font-style: italic;\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n .filter-header {\n flex-direction: column;\n align-items: stretch;\n gap: 12px;\n }\n\n .filter-summary {\n justify-content: center;\n }\n\n .filter-actions {\n justify-content: center;\n }\n\n .expression-toggle {\n padding: 10px 12px;\n }\n\n .expression-content {\n padding: 0 12px 12px 12px;\n }\n\n .expression-text {\n font-size: 12px;\n padding: 12px;\n }\n}\n"] }]
|
|
466
466
|
}], () => [{ type: i1.DomSanitizer }], { fields: [{
|
|
467
467
|
type: Input
|
|
468
468
|
}], filter: [{
|
|
@@ -46,6 +46,7 @@ export declare class FilterRuleComponent implements OnInit, OnChanges {
|
|
|
46
46
|
* Whether the current operator requires a value
|
|
47
47
|
*/
|
|
48
48
|
requiresValue: boolean;
|
|
49
|
+
dropdownMenuStyle: Record<string, string>;
|
|
49
50
|
fieldDropdownOpen: boolean;
|
|
50
51
|
operatorDropdownOpen: boolean;
|
|
51
52
|
valueDropdownOpen: boolean;
|
|
@@ -66,6 +67,17 @@ export declare class FilterRuleComponent implements OnInit, OnChanges {
|
|
|
66
67
|
* Update the selected field and available operators
|
|
67
68
|
*/
|
|
68
69
|
private updateFieldSelection;
|
|
70
|
+
/**
|
|
71
|
+
* Calculate fixed-position style for a dropdown menu based on its trigger button.
|
|
72
|
+
* Uses position:fixed so the menu escapes any overflow:hidden/auto ancestors.
|
|
73
|
+
* Accounts for CSS transform on ancestors (which change the containing block for fixed positioning).
|
|
74
|
+
*/
|
|
75
|
+
private calculateDropdownPosition;
|
|
76
|
+
/**
|
|
77
|
+
* Find the nearest ancestor with a CSS transform, which creates a new
|
|
78
|
+
* containing block for position:fixed elements per CSS spec.
|
|
79
|
+
*/
|
|
80
|
+
private getTransformAncestorOffset;
|
|
69
81
|
toggleFieldDropdown(): void;
|
|
70
82
|
toggleOperatorDropdown(): void;
|
|
71
83
|
toggleValueDropdown(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-rule.component.d.ts","sourceRoot":"","sources":["../../../src/lib/filter-rule/filter-rule.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAmC,UAAU,EAAa,MAAM,eAAe,CAAC;AACjK,OAAO,EACL,gBAAgB,EAChB,eAAe,EAEf,cAAc,EACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAuB,YAAY,EAAyB,MAAM,oBAAoB,CAAC;;AAE9F;;;;;GAKG;AACH,qBAOa,mBAAoB,YAAW,MAAM,EAAE,SAAS;
|
|
1
|
+
{"version":3,"file":"filter-rule.component.d.ts","sourceRoot":"","sources":["../../../src/lib/filter-rule/filter-rule.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Component, Input, Output, EventEmitter, ViewEncapsulation, HostListener, ViewChild } from '@angular/core';
|
|
2
2
|
import { getOperatorsForType, operatorRequiresValue } from '../types/operators';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
|
+
import * as i1 from "@angular/common";
|
|
4
5
|
const _c0 = ["fieldDropdownBtn"];
|
|
5
6
|
const _c1 = ["operatorDropdownBtn"];
|
|
6
7
|
const _c2 = ["valueDropdownBtn"];
|
|
@@ -32,6 +33,7 @@ function FilterRuleComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
|
32
33
|
i0.ɵɵelementEnd();
|
|
33
34
|
} if (rf & 2) {
|
|
34
35
|
const ctx_r3 = i0.ɵɵnextContext();
|
|
36
|
+
i0.ɵɵproperty("ngStyle", ctx_r3.dropdownMenuStyle);
|
|
35
37
|
i0.ɵɵadvance();
|
|
36
38
|
i0.ɵɵrepeater(ctx_r3.fields);
|
|
37
39
|
i0.ɵɵadvance(2);
|
|
@@ -57,6 +59,7 @@ function FilterRuleComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
|
57
59
|
i0.ɵɵelementEnd();
|
|
58
60
|
} if (rf & 2) {
|
|
59
61
|
const ctx_r3 = i0.ɵɵnextContext();
|
|
62
|
+
i0.ɵɵproperty("ngStyle", ctx_r3.dropdownMenuStyle);
|
|
60
63
|
i0.ɵɵadvance();
|
|
61
64
|
i0.ɵɵrepeater(ctx_r3.availableOperators);
|
|
62
65
|
} }
|
|
@@ -84,6 +87,7 @@ function FilterRuleComponent_Conditional_15_Conditional_0_Conditional_6_Template
|
|
|
84
87
|
i0.ɵɵelementEnd();
|
|
85
88
|
} if (rf & 2) {
|
|
86
89
|
const ctx_r3 = i0.ɵɵnextContext(3);
|
|
90
|
+
i0.ɵɵproperty("ngStyle", ctx_r3.dropdownMenuStyle);
|
|
87
91
|
i0.ɵɵadvance();
|
|
88
92
|
i0.ɵɵclassProp("selected", !ctx_r3.filter.value)("highlighted", 0 === ctx_r3.valueHighlightIndex);
|
|
89
93
|
i0.ɵɵadvance(2);
|
|
@@ -98,7 +102,7 @@ function FilterRuleComponent_Conditional_15_Conditional_0_Template(rf, ctx) { if
|
|
|
98
102
|
i0.ɵɵelementEnd();
|
|
99
103
|
i0.ɵɵelement(5, "i", 7);
|
|
100
104
|
i0.ɵɵelementEnd();
|
|
101
|
-
i0.ɵɵconditionalCreate(6, FilterRuleComponent_Conditional_15_Conditional_0_Conditional_6_Template, 5,
|
|
105
|
+
i0.ɵɵconditionalCreate(6, FilterRuleComponent_Conditional_15_Conditional_0_Conditional_6_Template, 5, 5, "div", 8);
|
|
102
106
|
i0.ɵɵelementEnd();
|
|
103
107
|
} if (rf & 2) {
|
|
104
108
|
const ctx_r3 = i0.ɵɵnextContext(2);
|
|
@@ -226,6 +230,8 @@ export class FilterRuleComponent {
|
|
|
226
230
|
*/
|
|
227
231
|
requiresValue = true;
|
|
228
232
|
// Dropdown state
|
|
233
|
+
// Fixed-position style for the active dropdown (escapes overflow clipping)
|
|
234
|
+
dropdownMenuStyle = {};
|
|
229
235
|
fieldDropdownOpen = false;
|
|
230
236
|
operatorDropdownOpen = false;
|
|
231
237
|
valueDropdownOpen = false;
|
|
@@ -273,6 +279,44 @@ export class FilterRuleComponent {
|
|
|
273
279
|
}
|
|
274
280
|
}
|
|
275
281
|
// ========================================
|
|
282
|
+
// DROPDOWN POSITIONING
|
|
283
|
+
// ========================================
|
|
284
|
+
/**
|
|
285
|
+
* Calculate fixed-position style for a dropdown menu based on its trigger button.
|
|
286
|
+
* Uses position:fixed so the menu escapes any overflow:hidden/auto ancestors.
|
|
287
|
+
* Accounts for CSS transform on ancestors (which change the containing block for fixed positioning).
|
|
288
|
+
*/
|
|
289
|
+
calculateDropdownPosition(triggerBtn) {
|
|
290
|
+
if (!triggerBtn?.nativeElement) {
|
|
291
|
+
this.dropdownMenuStyle = {};
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const rect = triggerBtn.nativeElement.getBoundingClientRect();
|
|
295
|
+
const offset = this.getTransformAncestorOffset(triggerBtn.nativeElement);
|
|
296
|
+
this.dropdownMenuStyle = {
|
|
297
|
+
position: 'fixed',
|
|
298
|
+
top: `${rect.bottom + 4 - offset.top}px`,
|
|
299
|
+
left: `${rect.left - offset.left}px`,
|
|
300
|
+
width: `${rect.width}px`
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Find the nearest ancestor with a CSS transform, which creates a new
|
|
305
|
+
* containing block for position:fixed elements per CSS spec.
|
|
306
|
+
*/
|
|
307
|
+
getTransformAncestorOffset(element) {
|
|
308
|
+
let el = element.parentElement;
|
|
309
|
+
while (el) {
|
|
310
|
+
const transform = getComputedStyle(el).transform;
|
|
311
|
+
if (transform && transform !== 'none') {
|
|
312
|
+
const ancestorRect = el.getBoundingClientRect();
|
|
313
|
+
return { top: ancestorRect.top, left: ancestorRect.left };
|
|
314
|
+
}
|
|
315
|
+
el = el.parentElement;
|
|
316
|
+
}
|
|
317
|
+
return { top: 0, left: 0 };
|
|
318
|
+
}
|
|
319
|
+
// ========================================
|
|
276
320
|
// DROPDOWN TOGGLE METHODS
|
|
277
321
|
// ========================================
|
|
278
322
|
toggleFieldDropdown() {
|
|
@@ -282,7 +326,7 @@ export class FilterRuleComponent {
|
|
|
282
326
|
this.closeAllDropdowns();
|
|
283
327
|
this.fieldDropdownOpen = !wasOpen;
|
|
284
328
|
if (this.fieldDropdownOpen) {
|
|
285
|
-
|
|
329
|
+
this.calculateDropdownPosition(this.fieldDropdownBtn);
|
|
286
330
|
setTimeout(() => this.fieldDropdownBtn?.nativeElement?.focus(), 0);
|
|
287
331
|
}
|
|
288
332
|
}
|
|
@@ -293,7 +337,7 @@ export class FilterRuleComponent {
|
|
|
293
337
|
this.closeAllDropdowns();
|
|
294
338
|
this.operatorDropdownOpen = !wasOpen;
|
|
295
339
|
if (this.operatorDropdownOpen) {
|
|
296
|
-
|
|
340
|
+
this.calculateDropdownPosition(this.operatorDropdownBtn);
|
|
297
341
|
setTimeout(() => this.operatorDropdownBtn?.nativeElement?.focus(), 0);
|
|
298
342
|
}
|
|
299
343
|
}
|
|
@@ -304,7 +348,7 @@ export class FilterRuleComponent {
|
|
|
304
348
|
this.closeAllDropdowns();
|
|
305
349
|
this.valueDropdownOpen = !wasOpen;
|
|
306
350
|
if (this.valueDropdownOpen) {
|
|
307
|
-
|
|
351
|
+
this.calculateDropdownPosition(this.valueDropdownBtn);
|
|
308
352
|
setTimeout(() => this.valueDropdownBtn?.nativeElement?.focus(), 0);
|
|
309
353
|
}
|
|
310
354
|
}
|
|
@@ -638,7 +682,7 @@ export class FilterRuleComponent {
|
|
|
638
682
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.valueDropdownBtn = _t.first);
|
|
639
683
|
} }, hostBindings: function FilterRuleComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
640
684
|
i0.ɵɵlistener("click", function FilterRuleComponent_click_HostBindingHandler($event) { return ctx.onDocumentClick($event); }, i0.ɵɵresolveDocument);
|
|
641
|
-
} }, inputs: { filter: "filter", fields: "fields", disabled: "disabled", showDelete: "showDelete" }, outputs: { filterChange: "filterChange", delete: "delete" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 18, vars: 18, consts: [["fieldDropdownBtn", ""], ["operatorDropdownBtn", ""], ["valueDropdownBtn", ""], [1, "filter-rule"], [1, "custom-dropdown", "field-dropdown"], ["type", "button", 1, "dropdown-trigger", 3, "click", "keydown", "disabled"], [1, "dropdown-value"], [1, "fa-solid", "fa-chevron-down", "dropdown-arrow"], [1, "dropdown-menu"], [1, "custom-dropdown", "operator-dropdown"], [1, "rule-actions"], ["type", "button", "title", "Remove filter", 1, "action-btn", "delete", 3, "disabled"], [1, "dropdown-item", 3, "selected", "highlighted"], [1, "dropdown-empty"], [1, "dropdown-item", 3, "click"], [1, "custom-dropdown", "value-dropdown", 3, "open", "disabled"], ["type", "text", "placeholder", "Enter value...", 1, "value-input", 3, "value", "disabled"], ["type", "number", "placeholder", "Enter number...", 1, "value-input", 3, "value", "disabled"], [1, "value-toggle"], ["type", "date", 1, "value-input", "value-date", 3, "value", "disabled"], ["type", "text", "placeholder", "Enter ID...", 1, "value-input", 3, "value", "disabled"], [1, "custom-dropdown", "value-dropdown"], ["type", "text", "placeholder", "Enter value...", 1, "value-input", 3, "input", "value", "disabled"], ["type", "number", "placeholder", "Enter number...", 1, "value-input", 3, "input", "value", "disabled"], ["type", "button", 1, "toggle-btn", "true", 3, "click", "disabled"], ["type", "button", 1, "toggle-btn", "false", 3, "click", "disabled"], ["type", "date", 1, "value-input", "value-date", 3, "change", "value", "disabled"], ["type", "text", "placeholder", "Enter ID...", 1, "value-input", 3, "input", "value", "disabled"], ["type", "button", "title", "Remove filter", 1, "action-btn", "delete", 3, "click", "disabled"], [1, "fa-solid", "fa-times"]], template: function FilterRuleComponent_Template(rf, ctx) { if (rf & 1) {
|
|
685
|
+
} }, inputs: { filter: "filter", fields: "fields", disabled: "disabled", showDelete: "showDelete" }, outputs: { filterChange: "filterChange", delete: "delete" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 18, vars: 18, consts: [["fieldDropdownBtn", ""], ["operatorDropdownBtn", ""], ["valueDropdownBtn", ""], [1, "filter-rule"], [1, "custom-dropdown", "field-dropdown"], ["type", "button", 1, "dropdown-trigger", 3, "click", "keydown", "disabled"], [1, "dropdown-value"], [1, "fa-solid", "fa-chevron-down", "dropdown-arrow"], [1, "dropdown-menu", 3, "ngStyle"], [1, "custom-dropdown", "operator-dropdown"], [1, "rule-actions"], ["type", "button", "title", "Remove filter", 1, "action-btn", "delete", 3, "disabled"], [1, "dropdown-item", 3, "selected", "highlighted"], [1, "dropdown-empty"], [1, "dropdown-item", 3, "click"], [1, "custom-dropdown", "value-dropdown", 3, "open", "disabled"], ["type", "text", "placeholder", "Enter value...", 1, "value-input", 3, "value", "disabled"], ["type", "number", "placeholder", "Enter number...", 1, "value-input", 3, "value", "disabled"], [1, "value-toggle"], ["type", "date", 1, "value-input", "value-date", 3, "value", "disabled"], ["type", "text", "placeholder", "Enter ID...", 1, "value-input", 3, "value", "disabled"], [1, "custom-dropdown", "value-dropdown"], ["type", "text", "placeholder", "Enter value...", 1, "value-input", 3, "input", "value", "disabled"], ["type", "number", "placeholder", "Enter number...", 1, "value-input", 3, "input", "value", "disabled"], ["type", "button", 1, "toggle-btn", "true", 3, "click", "disabled"], ["type", "button", 1, "toggle-btn", "false", 3, "click", "disabled"], ["type", "date", 1, "value-input", "value-date", 3, "change", "value", "disabled"], ["type", "text", "placeholder", "Enter ID...", 1, "value-input", 3, "input", "value", "disabled"], ["type", "button", "title", "Remove filter", 1, "action-btn", "delete", 3, "click", "disabled"], [1, "fa-solid", "fa-times"]], template: function FilterRuleComponent_Template(rf, ctx) { if (rf & 1) {
|
|
642
686
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
643
687
|
i0.ɵɵelementStart(0, "div", 3)(1, "div", 4)(2, "button", 5, 0);
|
|
644
688
|
i0.ɵɵlistener("click", function FilterRuleComponent_Template_button_click_2_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.toggleFieldDropdown()); })("keydown", function FilterRuleComponent_Template_button_keydown_2_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onFieldKeydown($event)); });
|
|
@@ -647,7 +691,7 @@ export class FilterRuleComponent {
|
|
|
647
691
|
i0.ɵɵelementEnd();
|
|
648
692
|
i0.ɵɵelement(6, "i", 7);
|
|
649
693
|
i0.ɵɵelementEnd();
|
|
650
|
-
i0.ɵɵconditionalCreate(7, FilterRuleComponent_Conditional_7_Template, 4,
|
|
694
|
+
i0.ɵɵconditionalCreate(7, FilterRuleComponent_Conditional_7_Template, 4, 2, "div", 8);
|
|
651
695
|
i0.ɵɵelementEnd();
|
|
652
696
|
i0.ɵɵelementStart(8, "div", 9)(9, "button", 5, 1);
|
|
653
697
|
i0.ɵɵlistener("click", function FilterRuleComponent_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.toggleOperatorDropdown()); })("keydown", function FilterRuleComponent_Template_button_keydown_9_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onOperatorKeydown($event)); });
|
|
@@ -656,7 +700,7 @@ export class FilterRuleComponent {
|
|
|
656
700
|
i0.ɵɵelementEnd();
|
|
657
701
|
i0.ɵɵelement(13, "i", 7);
|
|
658
702
|
i0.ɵɵelementEnd();
|
|
659
|
-
i0.ɵɵconditionalCreate(14, FilterRuleComponent_Conditional_14_Template, 3,
|
|
703
|
+
i0.ɵɵconditionalCreate(14, FilterRuleComponent_Conditional_14_Template, 3, 1, "div", 8);
|
|
660
704
|
i0.ɵɵelementEnd();
|
|
661
705
|
i0.ɵɵconditionalCreate(15, FilterRuleComponent_Conditional_15_Template, 6, 1);
|
|
662
706
|
i0.ɵɵelementStart(16, "div", 10);
|
|
@@ -684,11 +728,11 @@ export class FilterRuleComponent {
|
|
|
684
728
|
i0.ɵɵconditional(ctx.requiresValue && ctx.selectedField ? 15 : -1);
|
|
685
729
|
i0.ɵɵadvance(2);
|
|
686
730
|
i0.ɵɵconditional(ctx.showDelete ? 17 : -1);
|
|
687
|
-
} }, styles: ["/* Scoped to mj-filter-rule to prevent style leakage with ViewEncapsulation.None */\nmj-filter-rule .filter-rule {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: linear-gradient(to bottom, #ffffff, #fafafa);\n border: 1px solid #e5e7eb;\n border-radius: 10px;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n\nmj-filter-rule .filter-rule:hover {\n border-color: #d1d5db;\n background: linear-gradient(to bottom, #ffffff, #f5f5f5);\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);\n}\n\nmj-filter-rule .filter-rule.disabled {\n opacity: 0.6;\n pointer-events: none;\n box-shadow: none;\n}\n\n/* ========================================\n CUSTOM DROPDOWN STYLES\n ======================================== */\n\nmj-filter-rule .custom-dropdown {\n position: relative;\n min-width: 160px;\n}\n\nmj-filter-rule .custom-dropdown.field-dropdown {\n min-width: 180px;\n}\n\nmj-filter-rule .custom-dropdown.operator-dropdown {\n min-width: 160px;\n}\n\nmj-filter-rule .custom-dropdown.value-dropdown {\n min-width: 160px;\n flex: 1;\n}\n\nmj-filter-rule .custom-dropdown.disabled {\n opacity: 0.6;\n pointer-events: none;\n}\n\n/* Dropdown Trigger Button */\nmj-filter-rule .dropdown-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 10px 14px;\n border: 1px solid #d4d4d8;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background: white;\n color: #374151;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n text-align: left;\n}\n\nmj-filter-rule .dropdown-trigger:hover:not(:disabled) {\n border-color: #a1a1aa;\n background: #fafafa;\n}\n\nmj-filter-rule .dropdown-trigger:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n background: white;\n}\n\nmj-filter-rule .dropdown-trigger:disabled {\n background-color: #f4f4f5;\n color: #a1a1aa;\n cursor: not-allowed;\n box-shadow: none;\n}\n\nmj-filter-rule .custom-dropdown.open .dropdown-trigger {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n}\n\nmj-filter-rule .dropdown-value {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\nmj-filter-rule .dropdown-arrow {\n font-size: 10px;\n color: #6b7280;\n margin-left: 8px;\n transition: transform 0.2s ease;\n}\n\nmj-filter-rule .custom-dropdown.open .dropdown-arrow {\n transform: rotate(180deg);\n}\n\n/* Dropdown Menu */\nmj-filter-rule .dropdown-menu {\n
|
|
731
|
+
} }, dependencies: [i1.NgStyle], styles: ["/* Scoped to mj-filter-rule to prevent style leakage with ViewEncapsulation.None */\nmj-filter-rule .filter-rule {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: linear-gradient(to bottom, #ffffff, #fafafa);\n border: 1px solid #e5e7eb;\n border-radius: 10px;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n\nmj-filter-rule .filter-rule:hover {\n border-color: #d1d5db;\n background: linear-gradient(to bottom, #ffffff, #f5f5f5);\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);\n}\n\nmj-filter-rule .filter-rule.disabled {\n opacity: 0.6;\n pointer-events: none;\n box-shadow: none;\n}\n\n/* ========================================\n CUSTOM DROPDOWN STYLES\n ======================================== */\n\nmj-filter-rule .custom-dropdown {\n position: relative;\n min-width: 160px;\n}\n\nmj-filter-rule .custom-dropdown.field-dropdown {\n min-width: 180px;\n}\n\nmj-filter-rule .custom-dropdown.operator-dropdown {\n min-width: 160px;\n}\n\nmj-filter-rule .custom-dropdown.value-dropdown {\n min-width: 160px;\n flex: 1;\n}\n\nmj-filter-rule .custom-dropdown.disabled {\n opacity: 0.6;\n pointer-events: none;\n}\n\n/* Dropdown Trigger Button */\nmj-filter-rule .dropdown-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 10px 14px;\n border: 1px solid #d4d4d8;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background: white;\n color: #374151;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n text-align: left;\n}\n\nmj-filter-rule .dropdown-trigger:hover:not(:disabled) {\n border-color: #a1a1aa;\n background: #fafafa;\n}\n\nmj-filter-rule .dropdown-trigger:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n background: white;\n}\n\nmj-filter-rule .dropdown-trigger:disabled {\n background-color: #f4f4f5;\n color: #a1a1aa;\n cursor: not-allowed;\n box-shadow: none;\n}\n\nmj-filter-rule .custom-dropdown.open .dropdown-trigger {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n}\n\nmj-filter-rule .dropdown-value {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\nmj-filter-rule .dropdown-arrow {\n font-size: 10px;\n color: #6b7280;\n margin-left: 8px;\n transition: transform 0.2s ease;\n}\n\nmj-filter-rule .custom-dropdown.open .dropdown-arrow {\n transform: rotate(180deg);\n}\n\n/* Dropdown Menu - position:fixed is set via ngStyle to escape overflow clipping */\nmj-filter-rule .dropdown-menu {\n max-height: 280px;\n overflow-y: auto;\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 10px;\n box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.04);\n z-index: 9999;\n animation: dropdownFadeIn 0.15s ease;\n}\n\n@keyframes dropdownFadeIn {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Dropdown Items */\nmj-filter-rule .dropdown-item {\n padding: 10px 14px;\n font-size: 14px;\n color: #374151;\n cursor: pointer;\n transition: all 0.15s ease;\n border-bottom: 1px solid #f3f4f6;\n}\n\nmj-filter-rule .dropdown-item:last-child {\n border-bottom: none;\n}\n\nmj-filter-rule .dropdown-item:hover,\nmj-filter-rule .dropdown-item.highlighted {\n background: #f0f9ff;\n color: #1d4ed8;\n}\n\nmj-filter-rule .dropdown-item.highlighted {\n outline: 2px solid #3b82f6;\n outline-offset: -2px;\n}\n\nmj-filter-rule .dropdown-item.selected {\n background: #eff6ff;\n color: #1d4ed8;\n font-weight: 600;\n}\n\nmj-filter-rule .dropdown-item.selected::before {\n content: '\\f00c';\n font-family: 'Font Awesome 6 Free';\n font-weight: 900;\n font-size: 10px;\n margin-right: 8px;\n color: #3b82f6;\n}\n\nmj-filter-rule .dropdown-empty {\n padding: 16px;\n text-align: center;\n color: #9ca3af;\n font-size: 13px;\n font-style: italic;\n}\n\n/* Scrollbar styling for dropdown */\nmj-filter-rule .dropdown-menu::-webkit-scrollbar {\n width: 6px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-track {\n background: #f1f1f1;\n border-radius: 3px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-thumb {\n background: #c1c1c1;\n border-radius: 3px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-thumb:hover {\n background: #a1a1a1;\n}\n\n/* ========================================\n VALUE INPUT STYLES\n ======================================== */\n\nmj-filter-rule .value-input {\n padding: 10px 14px;\n border: 1px solid #d4d4d8;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background-color: white;\n color: #374151;\n transition: all 0.2s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n flex: 1;\n min-width: 150px;\n}\n\nmj-filter-rule .value-input:hover:not(:disabled) {\n border-color: #a1a1aa;\n background-color: #fafafa;\n}\n\nmj-filter-rule .value-input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n background-color: white;\n}\n\nmj-filter-rule .value-input:disabled {\n background-color: #f4f4f5;\n color: #a1a1aa;\n cursor: not-allowed;\n box-shadow: none;\n}\n\nmj-filter-rule .value-input::placeholder {\n color: #9ca3af;\n font-weight: 400;\n}\n\nmj-filter-rule .value-date {\n min-width: 160px;\n}\n\n/* ========================================\n BOOLEAN TOGGLE STYLES\n ======================================== */\n\nmj-filter-rule .value-toggle {\n display: flex;\n background: #f4f4f5;\n border-radius: 8px;\n padding: 3px;\n border: 1px solid #e5e7eb;\n}\n\nmj-filter-rule .toggle-btn {\n padding: 8px 18px;\n border: none;\n background: transparent;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n color: #71717a;\n}\n\nmj-filter-rule .toggle-btn:hover:not(:disabled) {\n color: #3f3f46;\n background: rgba(255, 255, 255, 0.5);\n}\n\nmj-filter-rule .toggle-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\nmj-filter-rule .toggle-btn.active {\n background: white;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n color: #18181b;\n}\n\nmj-filter-rule .toggle-btn.true.active {\n color: #16a34a;\n}\n\nmj-filter-rule .toggle-btn.false.active {\n color: #dc2626;\n}\n\n/* ========================================\n RULE ACTIONS\n ======================================== */\n\nmj-filter-rule .rule-actions {\n display: flex;\n gap: 4px;\n margin-left: auto;\n flex-shrink: 0;\n}\n\nmj-filter-rule .action-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 8px;\n cursor: pointer;\n color: #a1a1aa;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n}\n\nmj-filter-rule .action-btn:hover:not(:disabled) {\n background: #f4f4f5;\n color: #71717a;\n}\n\nmj-filter-rule .action-btn:disabled {\n cursor: not-allowed;\n opacity: 0.3;\n}\n\nmj-filter-rule .action-btn.delete:hover:not(:disabled) {\n background: #fef2f2;\n color: #ef4444;\n}\n\n/* ========================================\n RESPONSIVE\n ======================================== */\n\n@media (max-width: 768px) {\n mj-filter-rule .filter-rule {\n flex-wrap: wrap;\n }\n\n mj-filter-rule .custom-dropdown,\n mj-filter-rule .value-input {\n min-width: 100%;\n flex: 1 1 100%;\n }\n\n mj-filter-rule .custom-dropdown.field-dropdown,\n mj-filter-rule .custom-dropdown.operator-dropdown,\n mj-filter-rule .custom-dropdown.value-dropdown {\n min-width: 100%;\n }\n}\n"], encapsulation: 2 });
|
|
688
732
|
}
|
|
689
733
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FilterRuleComponent, [{
|
|
690
734
|
type: Component,
|
|
691
|
-
args: [{ standalone: false, selector: 'mj-filter-rule', encapsulation: ViewEncapsulation.None, template: "<div class=\"filter-rule\" [class.disabled]=\"disabled\">\n <!-- Field Selector - Custom Dropdown -->\n <div class=\"custom-dropdown field-dropdown\" [class.open]=\"fieldDropdownOpen\" [class.disabled]=\"disabled\">\n <button\n #fieldDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleFieldDropdown()\"\n (keydown)=\"onFieldKeydown($event)\"\n [disabled]=\"disabled\">\n <span class=\"dropdown-value\">{{ getSelectedFieldDisplayName() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (fieldDropdownOpen) {\n <div class=\"dropdown-menu\">\n @for (field of fields; track field.name; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"field.name === filter.field\"\n [class.highlighted]=\"i === fieldHighlightIndex\"\n (click)=\"selectField(field.name)\">\n {{ field.displayName }}\n </div>\n }\n @if (fields.length === 0) {\n <div class=\"dropdown-empty\">No fields available</div>\n }\n </div>\n }\n </div>\n\n <!-- Operator Selector - Custom Dropdown -->\n <div class=\"custom-dropdown operator-dropdown\" [class.open]=\"operatorDropdownOpen\" [class.disabled]=\"disabled || !selectedField\">\n <button\n #operatorDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleOperatorDropdown()\"\n (keydown)=\"onOperatorKeydown($event)\"\n [disabled]=\"disabled || !selectedField\">\n <span class=\"dropdown-value\">{{ getSelectedOperatorLabel() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (operatorDropdownOpen) {\n <div class=\"dropdown-menu\">\n @for (op of availableOperators; track op.value; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"op.value === filter.operator\"\n [class.highlighted]=\"i === operatorHighlightIndex\"\n (click)=\"selectOperator(op.value)\">\n {{ op.label }}\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Value Editor - varies by field type -->\n @if (requiresValue && selectedField) {\n <!-- String with value list (custom dropdown) -->\n @if (selectedField.type === 'string' && hasValueList()) {\n <div class=\"custom-dropdown value-dropdown\" [class.open]=\"valueDropdownOpen\" [class.disabled]=\"disabled\">\n <button\n #valueDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleValueDropdown()\"\n (keydown)=\"onValueKeydown($event)\"\n [disabled]=\"disabled\">\n <span class=\"dropdown-value\">{{ getSelectedValueLabel() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (valueDropdownOpen) {\n <div class=\"dropdown-menu\">\n <div\n class=\"dropdown-item\"\n [class.selected]=\"!filter.value\"\n [class.highlighted]=\"0 === valueHighlightIndex\"\n (click)=\"selectValue('')\">\n Select...\n </div>\n @for (option of selectedField.valueList; track option.value; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"option.value === filter.value\"\n [class.highlighted]=\"(i + 1) === valueHighlightIndex\"\n (click)=\"selectValueFromOption(option.value)\">\n {{ option.label }}\n </div>\n }\n </div>\n }\n </div>\n }\n <!-- String (text input) -->\n @else if (selectedField.type === 'string') {\n <input\n type=\"text\"\n class=\"value-input\"\n [value]=\"filter.value || ''\"\n (input)=\"onValueChange($any($event.target).value)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter value...\">\n }\n <!-- Number -->\n @else if (selectedField.type === 'number') {\n <input\n type=\"number\"\n class=\"value-input\"\n [value]=\"filter.value\"\n (input)=\"onValueChange($any($event.target).valueAsNumber)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter number...\">\n }\n <!-- Boolean -->\n @else if (selectedField.type === 'boolean') {\n <div class=\"value-toggle\">\n <button\n type=\"button\"\n class=\"toggle-btn true\"\n [class.active]=\"filter.value === true\"\n (click)=\"onBooleanChange(true)\"\n [disabled]=\"disabled\">\n True\n </button>\n <button\n type=\"button\"\n class=\"toggle-btn false\"\n [class.active]=\"filter.value === false\"\n (click)=\"onBooleanChange(false)\"\n [disabled]=\"disabled\">\n False\n </button>\n </div>\n }\n <!-- Date -->\n @else if (selectedField.type === 'date') {\n <input\n type=\"date\"\n class=\"value-input value-date\"\n [value]=\"getDateInputValue()\"\n (change)=\"onDateChange($event)\"\n [disabled]=\"disabled\">\n }\n <!-- Lookup (for now, simple text input - can be enhanced with record selector) -->\n @else if (selectedField.type === 'lookup') {\n <input\n type=\"text\"\n class=\"value-input\"\n [value]=\"filter.value || ''\"\n (input)=\"onValueChange($any($event.target).value)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter ID...\">\n }\n }\n\n <!-- Actions -->\n <div class=\"rule-actions\">\n @if (showDelete) {\n <button\n type=\"button\"\n class=\"action-btn delete\"\n (click)=\"onDelete()\"\n [disabled]=\"disabled\"\n title=\"Remove filter\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n</div>\n", styles: ["/* Scoped to mj-filter-rule to prevent style leakage with ViewEncapsulation.None */\nmj-filter-rule .filter-rule {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: linear-gradient(to bottom, #ffffff, #fafafa);\n border: 1px solid #e5e7eb;\n border-radius: 10px;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n\nmj-filter-rule .filter-rule:hover {\n border-color: #d1d5db;\n background: linear-gradient(to bottom, #ffffff, #f5f5f5);\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);\n}\n\nmj-filter-rule .filter-rule.disabled {\n opacity: 0.6;\n pointer-events: none;\n box-shadow: none;\n}\n\n/* ========================================\n CUSTOM DROPDOWN STYLES\n ======================================== */\n\nmj-filter-rule .custom-dropdown {\n position: relative;\n min-width: 160px;\n}\n\nmj-filter-rule .custom-dropdown.field-dropdown {\n min-width: 180px;\n}\n\nmj-filter-rule .custom-dropdown.operator-dropdown {\n min-width: 160px;\n}\n\nmj-filter-rule .custom-dropdown.value-dropdown {\n min-width: 160px;\n flex: 1;\n}\n\nmj-filter-rule .custom-dropdown.disabled {\n opacity: 0.6;\n pointer-events: none;\n}\n\n/* Dropdown Trigger Button */\nmj-filter-rule .dropdown-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 10px 14px;\n border: 1px solid #d4d4d8;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background: white;\n color: #374151;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n text-align: left;\n}\n\nmj-filter-rule .dropdown-trigger:hover:not(:disabled) {\n border-color: #a1a1aa;\n background: #fafafa;\n}\n\nmj-filter-rule .dropdown-trigger:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n background: white;\n}\n\nmj-filter-rule .dropdown-trigger:disabled {\n background-color: #f4f4f5;\n color: #a1a1aa;\n cursor: not-allowed;\n box-shadow: none;\n}\n\nmj-filter-rule .custom-dropdown.open .dropdown-trigger {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n}\n\nmj-filter-rule .dropdown-value {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\nmj-filter-rule .dropdown-arrow {\n font-size: 10px;\n color: #6b7280;\n margin-left: 8px;\n transition: transform 0.2s ease;\n}\n\nmj-filter-rule .custom-dropdown.open .dropdown-arrow {\n transform: rotate(180deg);\n}\n\n/* Dropdown Menu */\nmj-filter-rule .dropdown-menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n min-width: 100%;\n max-height: 280px;\n overflow-y: auto;\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 10px;\n box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.04);\n z-index: 9999;\n animation: dropdownFadeIn 0.15s ease;\n}\n\n@keyframes dropdownFadeIn {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Dropdown Items */\nmj-filter-rule .dropdown-item {\n padding: 10px 14px;\n font-size: 14px;\n color: #374151;\n cursor: pointer;\n transition: all 0.15s ease;\n border-bottom: 1px solid #f3f4f6;\n}\n\nmj-filter-rule .dropdown-item:last-child {\n border-bottom: none;\n}\n\nmj-filter-rule .dropdown-item:hover,\nmj-filter-rule .dropdown-item.highlighted {\n background: #f0f9ff;\n color: #1d4ed8;\n}\n\nmj-filter-rule .dropdown-item.highlighted {\n outline: 2px solid #3b82f6;\n outline-offset: -2px;\n}\n\nmj-filter-rule .dropdown-item.selected {\n background: #eff6ff;\n color: #1d4ed8;\n font-weight: 600;\n}\n\nmj-filter-rule .dropdown-item.selected::before {\n content: '\\f00c';\n font-family: 'Font Awesome 6 Free';\n font-weight: 900;\n font-size: 10px;\n margin-right: 8px;\n color: #3b82f6;\n}\n\nmj-filter-rule .dropdown-empty {\n padding: 16px;\n text-align: center;\n color: #9ca3af;\n font-size: 13px;\n font-style: italic;\n}\n\n/* Scrollbar styling for dropdown */\nmj-filter-rule .dropdown-menu::-webkit-scrollbar {\n width: 6px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-track {\n background: #f1f1f1;\n border-radius: 3px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-thumb {\n background: #c1c1c1;\n border-radius: 3px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-thumb:hover {\n background: #a1a1a1;\n}\n\n/* ========================================\n VALUE INPUT STYLES\n ======================================== */\n\nmj-filter-rule .value-input {\n padding: 10px 14px;\n border: 1px solid #d4d4d8;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background-color: white;\n color: #374151;\n transition: all 0.2s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n flex: 1;\n min-width: 150px;\n}\n\nmj-filter-rule .value-input:hover:not(:disabled) {\n border-color: #a1a1aa;\n background-color: #fafafa;\n}\n\nmj-filter-rule .value-input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n background-color: white;\n}\n\nmj-filter-rule .value-input:disabled {\n background-color: #f4f4f5;\n color: #a1a1aa;\n cursor: not-allowed;\n box-shadow: none;\n}\n\nmj-filter-rule .value-input::placeholder {\n color: #9ca3af;\n font-weight: 400;\n}\n\nmj-filter-rule .value-date {\n min-width: 160px;\n}\n\n/* ========================================\n BOOLEAN TOGGLE STYLES\n ======================================== */\n\nmj-filter-rule .value-toggle {\n display: flex;\n background: #f4f4f5;\n border-radius: 8px;\n padding: 3px;\n border: 1px solid #e5e7eb;\n}\n\nmj-filter-rule .toggle-btn {\n padding: 8px 18px;\n border: none;\n background: transparent;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n color: #71717a;\n}\n\nmj-filter-rule .toggle-btn:hover:not(:disabled) {\n color: #3f3f46;\n background: rgba(255, 255, 255, 0.5);\n}\n\nmj-filter-rule .toggle-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\nmj-filter-rule .toggle-btn.active {\n background: white;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n color: #18181b;\n}\n\nmj-filter-rule .toggle-btn.true.active {\n color: #16a34a;\n}\n\nmj-filter-rule .toggle-btn.false.active {\n color: #dc2626;\n}\n\n/* ========================================\n RULE ACTIONS\n ======================================== */\n\nmj-filter-rule .rule-actions {\n display: flex;\n gap: 4px;\n margin-left: auto;\n flex-shrink: 0;\n}\n\nmj-filter-rule .action-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 8px;\n cursor: pointer;\n color: #a1a1aa;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n}\n\nmj-filter-rule .action-btn:hover:not(:disabled) {\n background: #f4f4f5;\n color: #71717a;\n}\n\nmj-filter-rule .action-btn:disabled {\n cursor: not-allowed;\n opacity: 0.3;\n}\n\nmj-filter-rule .action-btn.delete:hover:not(:disabled) {\n background: #fef2f2;\n color: #ef4444;\n}\n\n/* ========================================\n RESPONSIVE\n ======================================== */\n\n@media (max-width: 768px) {\n mj-filter-rule .filter-rule {\n flex-wrap: wrap;\n }\n\n mj-filter-rule .custom-dropdown,\n mj-filter-rule .value-input {\n min-width: 100%;\n flex: 1 1 100%;\n }\n\n mj-filter-rule .custom-dropdown.field-dropdown,\n mj-filter-rule .custom-dropdown.operator-dropdown,\n mj-filter-rule .custom-dropdown.value-dropdown {\n min-width: 100%;\n }\n}\n"] }]
|
|
735
|
+
args: [{ standalone: false, selector: 'mj-filter-rule', encapsulation: ViewEncapsulation.None, template: "<div class=\"filter-rule\" [class.disabled]=\"disabled\">\n <!-- Field Selector - Custom Dropdown -->\n <div class=\"custom-dropdown field-dropdown\" [class.open]=\"fieldDropdownOpen\" [class.disabled]=\"disabled\">\n <button\n #fieldDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleFieldDropdown()\"\n (keydown)=\"onFieldKeydown($event)\"\n [disabled]=\"disabled\">\n <span class=\"dropdown-value\">{{ getSelectedFieldDisplayName() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (fieldDropdownOpen) {\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownMenuStyle\">\n @for (field of fields; track field.name; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"field.name === filter.field\"\n [class.highlighted]=\"i === fieldHighlightIndex\"\n (click)=\"selectField(field.name)\">\n {{ field.displayName }}\n </div>\n }\n @if (fields.length === 0) {\n <div class=\"dropdown-empty\">No fields available</div>\n }\n </div>\n }\n </div>\n\n <!-- Operator Selector - Custom Dropdown -->\n <div class=\"custom-dropdown operator-dropdown\" [class.open]=\"operatorDropdownOpen\" [class.disabled]=\"disabled || !selectedField\">\n <button\n #operatorDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleOperatorDropdown()\"\n (keydown)=\"onOperatorKeydown($event)\"\n [disabled]=\"disabled || !selectedField\">\n <span class=\"dropdown-value\">{{ getSelectedOperatorLabel() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (operatorDropdownOpen) {\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownMenuStyle\">\n @for (op of availableOperators; track op.value; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"op.value === filter.operator\"\n [class.highlighted]=\"i === operatorHighlightIndex\"\n (click)=\"selectOperator(op.value)\">\n {{ op.label }}\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Value Editor - varies by field type -->\n @if (requiresValue && selectedField) {\n <!-- String with value list (custom dropdown) -->\n @if (selectedField.type === 'string' && hasValueList()) {\n <div class=\"custom-dropdown value-dropdown\" [class.open]=\"valueDropdownOpen\" [class.disabled]=\"disabled\">\n <button\n #valueDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleValueDropdown()\"\n (keydown)=\"onValueKeydown($event)\"\n [disabled]=\"disabled\">\n <span class=\"dropdown-value\">{{ getSelectedValueLabel() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (valueDropdownOpen) {\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownMenuStyle\">\n <div\n class=\"dropdown-item\"\n [class.selected]=\"!filter.value\"\n [class.highlighted]=\"0 === valueHighlightIndex\"\n (click)=\"selectValue('')\">\n Select...\n </div>\n @for (option of selectedField.valueList; track option.value; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"option.value === filter.value\"\n [class.highlighted]=\"(i + 1) === valueHighlightIndex\"\n (click)=\"selectValueFromOption(option.value)\">\n {{ option.label }}\n </div>\n }\n </div>\n }\n </div>\n }\n <!-- String (text input) -->\n @else if (selectedField.type === 'string') {\n <input\n type=\"text\"\n class=\"value-input\"\n [value]=\"filter.value || ''\"\n (input)=\"onValueChange($any($event.target).value)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter value...\">\n }\n <!-- Number -->\n @else if (selectedField.type === 'number') {\n <input\n type=\"number\"\n class=\"value-input\"\n [value]=\"filter.value\"\n (input)=\"onValueChange($any($event.target).valueAsNumber)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter number...\">\n }\n <!-- Boolean -->\n @else if (selectedField.type === 'boolean') {\n <div class=\"value-toggle\">\n <button\n type=\"button\"\n class=\"toggle-btn true\"\n [class.active]=\"filter.value === true\"\n (click)=\"onBooleanChange(true)\"\n [disabled]=\"disabled\">\n True\n </button>\n <button\n type=\"button\"\n class=\"toggle-btn false\"\n [class.active]=\"filter.value === false\"\n (click)=\"onBooleanChange(false)\"\n [disabled]=\"disabled\">\n False\n </button>\n </div>\n }\n <!-- Date -->\n @else if (selectedField.type === 'date') {\n <input\n type=\"date\"\n class=\"value-input value-date\"\n [value]=\"getDateInputValue()\"\n (change)=\"onDateChange($event)\"\n [disabled]=\"disabled\">\n }\n <!-- Lookup (for now, simple text input - can be enhanced with record selector) -->\n @else if (selectedField.type === 'lookup') {\n <input\n type=\"text\"\n class=\"value-input\"\n [value]=\"filter.value || ''\"\n (input)=\"onValueChange($any($event.target).value)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter ID...\">\n }\n }\n\n <!-- Actions -->\n <div class=\"rule-actions\">\n @if (showDelete) {\n <button\n type=\"button\"\n class=\"action-btn delete\"\n (click)=\"onDelete()\"\n [disabled]=\"disabled\"\n title=\"Remove filter\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n</div>\n", styles: ["/* Scoped to mj-filter-rule to prevent style leakage with ViewEncapsulation.None */\nmj-filter-rule .filter-rule {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: linear-gradient(to bottom, #ffffff, #fafafa);\n border: 1px solid #e5e7eb;\n border-radius: 10px;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n\nmj-filter-rule .filter-rule:hover {\n border-color: #d1d5db;\n background: linear-gradient(to bottom, #ffffff, #f5f5f5);\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);\n}\n\nmj-filter-rule .filter-rule.disabled {\n opacity: 0.6;\n pointer-events: none;\n box-shadow: none;\n}\n\n/* ========================================\n CUSTOM DROPDOWN STYLES\n ======================================== */\n\nmj-filter-rule .custom-dropdown {\n position: relative;\n min-width: 160px;\n}\n\nmj-filter-rule .custom-dropdown.field-dropdown {\n min-width: 180px;\n}\n\nmj-filter-rule .custom-dropdown.operator-dropdown {\n min-width: 160px;\n}\n\nmj-filter-rule .custom-dropdown.value-dropdown {\n min-width: 160px;\n flex: 1;\n}\n\nmj-filter-rule .custom-dropdown.disabled {\n opacity: 0.6;\n pointer-events: none;\n}\n\n/* Dropdown Trigger Button */\nmj-filter-rule .dropdown-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 10px 14px;\n border: 1px solid #d4d4d8;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background: white;\n color: #374151;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n text-align: left;\n}\n\nmj-filter-rule .dropdown-trigger:hover:not(:disabled) {\n border-color: #a1a1aa;\n background: #fafafa;\n}\n\nmj-filter-rule .dropdown-trigger:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n background: white;\n}\n\nmj-filter-rule .dropdown-trigger:disabled {\n background-color: #f4f4f5;\n color: #a1a1aa;\n cursor: not-allowed;\n box-shadow: none;\n}\n\nmj-filter-rule .custom-dropdown.open .dropdown-trigger {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n}\n\nmj-filter-rule .dropdown-value {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\nmj-filter-rule .dropdown-arrow {\n font-size: 10px;\n color: #6b7280;\n margin-left: 8px;\n transition: transform 0.2s ease;\n}\n\nmj-filter-rule .custom-dropdown.open .dropdown-arrow {\n transform: rotate(180deg);\n}\n\n/* Dropdown Menu - position:fixed is set via ngStyle to escape overflow clipping */\nmj-filter-rule .dropdown-menu {\n max-height: 280px;\n overflow-y: auto;\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 10px;\n box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.04);\n z-index: 9999;\n animation: dropdownFadeIn 0.15s ease;\n}\n\n@keyframes dropdownFadeIn {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Dropdown Items */\nmj-filter-rule .dropdown-item {\n padding: 10px 14px;\n font-size: 14px;\n color: #374151;\n cursor: pointer;\n transition: all 0.15s ease;\n border-bottom: 1px solid #f3f4f6;\n}\n\nmj-filter-rule .dropdown-item:last-child {\n border-bottom: none;\n}\n\nmj-filter-rule .dropdown-item:hover,\nmj-filter-rule .dropdown-item.highlighted {\n background: #f0f9ff;\n color: #1d4ed8;\n}\n\nmj-filter-rule .dropdown-item.highlighted {\n outline: 2px solid #3b82f6;\n outline-offset: -2px;\n}\n\nmj-filter-rule .dropdown-item.selected {\n background: #eff6ff;\n color: #1d4ed8;\n font-weight: 600;\n}\n\nmj-filter-rule .dropdown-item.selected::before {\n content: '\\f00c';\n font-family: 'Font Awesome 6 Free';\n font-weight: 900;\n font-size: 10px;\n margin-right: 8px;\n color: #3b82f6;\n}\n\nmj-filter-rule .dropdown-empty {\n padding: 16px;\n text-align: center;\n color: #9ca3af;\n font-size: 13px;\n font-style: italic;\n}\n\n/* Scrollbar styling for dropdown */\nmj-filter-rule .dropdown-menu::-webkit-scrollbar {\n width: 6px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-track {\n background: #f1f1f1;\n border-radius: 3px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-thumb {\n background: #c1c1c1;\n border-radius: 3px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-thumb:hover {\n background: #a1a1a1;\n}\n\n/* ========================================\n VALUE INPUT STYLES\n ======================================== */\n\nmj-filter-rule .value-input {\n padding: 10px 14px;\n border: 1px solid #d4d4d8;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background-color: white;\n color: #374151;\n transition: all 0.2s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n flex: 1;\n min-width: 150px;\n}\n\nmj-filter-rule .value-input:hover:not(:disabled) {\n border-color: #a1a1aa;\n background-color: #fafafa;\n}\n\nmj-filter-rule .value-input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n background-color: white;\n}\n\nmj-filter-rule .value-input:disabled {\n background-color: #f4f4f5;\n color: #a1a1aa;\n cursor: not-allowed;\n box-shadow: none;\n}\n\nmj-filter-rule .value-input::placeholder {\n color: #9ca3af;\n font-weight: 400;\n}\n\nmj-filter-rule .value-date {\n min-width: 160px;\n}\n\n/* ========================================\n BOOLEAN TOGGLE STYLES\n ======================================== */\n\nmj-filter-rule .value-toggle {\n display: flex;\n background: #f4f4f5;\n border-radius: 8px;\n padding: 3px;\n border: 1px solid #e5e7eb;\n}\n\nmj-filter-rule .toggle-btn {\n padding: 8px 18px;\n border: none;\n background: transparent;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n color: #71717a;\n}\n\nmj-filter-rule .toggle-btn:hover:not(:disabled) {\n color: #3f3f46;\n background: rgba(255, 255, 255, 0.5);\n}\n\nmj-filter-rule .toggle-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\nmj-filter-rule .toggle-btn.active {\n background: white;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n color: #18181b;\n}\n\nmj-filter-rule .toggle-btn.true.active {\n color: #16a34a;\n}\n\nmj-filter-rule .toggle-btn.false.active {\n color: #dc2626;\n}\n\n/* ========================================\n RULE ACTIONS\n ======================================== */\n\nmj-filter-rule .rule-actions {\n display: flex;\n gap: 4px;\n margin-left: auto;\n flex-shrink: 0;\n}\n\nmj-filter-rule .action-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 8px;\n cursor: pointer;\n color: #a1a1aa;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n}\n\nmj-filter-rule .action-btn:hover:not(:disabled) {\n background: #f4f4f5;\n color: #71717a;\n}\n\nmj-filter-rule .action-btn:disabled {\n cursor: not-allowed;\n opacity: 0.3;\n}\n\nmj-filter-rule .action-btn.delete:hover:not(:disabled) {\n background: #fef2f2;\n color: #ef4444;\n}\n\n/* ========================================\n RESPONSIVE\n ======================================== */\n\n@media (max-width: 768px) {\n mj-filter-rule .filter-rule {\n flex-wrap: wrap;\n }\n\n mj-filter-rule .custom-dropdown,\n mj-filter-rule .value-input {\n min-width: 100%;\n flex: 1 1 100%;\n }\n\n mj-filter-rule .custom-dropdown.field-dropdown,\n mj-filter-rule .custom-dropdown.operator-dropdown,\n mj-filter-rule .custom-dropdown.value-dropdown {\n min-width: 100%;\n }\n}\n"] }]
|
|
692
736
|
}], () => [{ type: i0.ElementRef }], { filter: [{
|
|
693
737
|
type: Input
|
|
694
738
|
}], fields: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-rule.component.js","sourceRoot":"","sources":["../../../src/lib/filter-rule/filter-rule.component.ts","../../../src/lib/filter-rule/filter-rule.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, ViewEncapsulation, HostListener, ElementRef, ViewChild } from '@angular/core';\nimport {\n FilterDescriptor,\n FilterFieldInfo,\n FilterFieldType,\n FilterOperator\n} from '../types/filter.types';\nimport { getOperatorsForType, OperatorInfo, operatorRequiresValue } from '../types/operators';\n\n/**\n * FilterRuleComponent - A single filter condition row\n *\n * Displays field selector, operator selector, and value editor\n * based on the field type.\n */\n@Component({\n standalone: false,\n selector: 'mj-filter-rule',\n templateUrl: './filter-rule.component.html',\n styleUrls: ['./filter-rule.component.css'],\n encapsulation: ViewEncapsulation.None\n})\nexport class FilterRuleComponent implements OnInit, OnChanges {\n /**\n * The filter descriptor for this rule\n */\n @Input() filter!: FilterDescriptor;\n\n /**\n * Available fields to filter on\n */\n @Input() fields: FilterFieldInfo[] = [];\n\n /**\n * Whether the component is disabled\n */\n @Input() disabled: boolean = false;\n\n /**\n * Whether to show the delete button\n */\n @Input() showDelete: boolean = true;\n\n /**\n * Emitted when the filter changes\n */\n @Output() filterChange = new EventEmitter<FilterDescriptor>();\n\n /**\n * Emitted when the delete button is clicked\n */\n @Output() delete = new EventEmitter<void>();\n\n /**\n * Currently selected field info\n */\n public selectedField: FilterFieldInfo | null = null;\n\n /**\n * Available operators for the selected field type\n */\n public availableOperators: OperatorInfo[] = [];\n\n /**\n * Whether the current operator requires a value\n */\n public requiresValue: boolean = true;\n\n // Dropdown state\n public fieldDropdownOpen = false;\n public operatorDropdownOpen = false;\n public valueDropdownOpen = false;\n\n // Keyboard navigation state\n public fieldHighlightIndex = -1;\n public operatorHighlightIndex = -1;\n public valueHighlightIndex = -1;\n\n // ViewChild references for dropdown buttons (Safari focus fix)\n @ViewChild('fieldDropdownBtn') fieldDropdownBtn!: ElementRef<HTMLButtonElement>;\n @ViewChild('operatorDropdownBtn') operatorDropdownBtn!: ElementRef<HTMLButtonElement>;\n @ViewChild('valueDropdownBtn') valueDropdownBtn!: ElementRef<HTMLButtonElement>;\n\n constructor(private elementRef: ElementRef) {}\n\n /**\n * Close dropdowns when clicking outside the component\n */\n @HostListener('document:click', ['$event'])\n onDocumentClick(event: MouseEvent): void {\n if (!this.elementRef.nativeElement.contains(event.target)) {\n this.closeAllDropdowns();\n }\n }\n\n ngOnInit(): void {\n this.updateFieldSelection();\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['filter'] || changes['fields']) {\n this.updateFieldSelection();\n }\n }\n\n /**\n * Update the selected field and available operators\n */\n private updateFieldSelection(): void {\n if (!this.filter || !this.fields.length) return;\n\n this.selectedField = this.fields.find(f => f.name === this.filter.field) || null;\n\n if (this.selectedField) {\n this.availableOperators = getOperatorsForType(this.selectedField.type);\n this.requiresValue = operatorRequiresValue(this.filter.operator);\n } else {\n this.availableOperators = [];\n this.requiresValue = true;\n }\n }\n\n // ========================================\n // DROPDOWN TOGGLE METHODS\n // ========================================\n\n toggleFieldDropdown(): void {\n if (this.disabled) return;\n const wasOpen = this.fieldDropdownOpen;\n this.closeAllDropdowns();\n this.fieldDropdownOpen = !wasOpen;\n if (this.fieldDropdownOpen) {\n // Ensure button retains focus for keyboard events (Safari fix)\n setTimeout(() => this.fieldDropdownBtn?.nativeElement?.focus(), 0);\n }\n }\n\n toggleOperatorDropdown(): void {\n if (this.disabled || !this.selectedField) return;\n const wasOpen = this.operatorDropdownOpen;\n this.closeAllDropdowns();\n this.operatorDropdownOpen = !wasOpen;\n if (this.operatorDropdownOpen) {\n // Ensure button retains focus for keyboard events (Safari fix)\n setTimeout(() => this.operatorDropdownBtn?.nativeElement?.focus(), 0);\n }\n }\n\n toggleValueDropdown(): void {\n if (this.disabled) return;\n const wasOpen = this.valueDropdownOpen;\n this.closeAllDropdowns();\n this.valueDropdownOpen = !wasOpen;\n if (this.valueDropdownOpen) {\n // Ensure button retains focus for keyboard events (Safari fix)\n setTimeout(() => this.valueDropdownBtn?.nativeElement?.focus(), 0);\n }\n }\n\n closeFieldDropdown(): void {\n this.fieldDropdownOpen = false;\n }\n\n closeOperatorDropdown(): void {\n this.operatorDropdownOpen = false;\n }\n\n closeValueDropdown(): void {\n this.valueDropdownOpen = false;\n }\n\n closeAllDropdowns(): void {\n this.fieldDropdownOpen = false;\n this.operatorDropdownOpen = false;\n this.valueDropdownOpen = false;\n this.resetHighlightIndices();\n }\n\n resetHighlightIndices(): void {\n this.fieldHighlightIndex = -1;\n this.operatorHighlightIndex = -1;\n this.valueHighlightIndex = -1;\n }\n\n // ========================================\n // KEYBOARD NAVIGATION\n // ========================================\n\n /**\n * Handle keyboard events on the field dropdown trigger\n */\n onFieldKeydown(event: KeyboardEvent): void {\n if (this.disabled) return;\n\n if (!this.fieldDropdownOpen) {\n // Open dropdown on Enter, Space, or arrow keys\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n this.toggleFieldDropdown();\n this.fieldHighlightIndex = this.fields.findIndex(f => f.name === this.filter.field);\n if (this.fieldHighlightIndex < 0) this.fieldHighlightIndex = 0;\n }\n return;\n }\n\n this.handleDropdownKeydown(\n event,\n this.fields,\n this.fieldHighlightIndex,\n (index) => this.fieldHighlightIndex = index,\n (item) => this.selectField(item.name),\n (item) => item.displayName,\n () => this.closeFieldDropdown()\n );\n }\n\n /**\n * Handle keyboard events on the operator dropdown trigger\n */\n onOperatorKeydown(event: KeyboardEvent): void {\n if (this.disabled || !this.selectedField) return;\n\n if (!this.operatorDropdownOpen) {\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n this.toggleOperatorDropdown();\n this.operatorHighlightIndex = this.availableOperators.findIndex(o => o.value === this.filter.operator);\n if (this.operatorHighlightIndex < 0) this.operatorHighlightIndex = 0;\n }\n return;\n }\n\n this.handleDropdownKeydown(\n event,\n this.availableOperators,\n this.operatorHighlightIndex,\n (index) => this.operatorHighlightIndex = index,\n (item) => this.selectOperator(item.value),\n (item) => item.label,\n () => this.closeOperatorDropdown()\n );\n }\n\n /**\n * Handle keyboard events on the value dropdown trigger\n */\n onValueKeydown(event: KeyboardEvent): void {\n if (this.disabled || !this.selectedField?.valueList) return;\n\n if (!this.valueDropdownOpen) {\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n this.toggleValueDropdown();\n const valueList = this.selectedField.valueList;\n this.valueHighlightIndex = valueList.findIndex(v => v.value === this.filter.value);\n if (this.valueHighlightIndex < 0) this.valueHighlightIndex = 0;\n }\n return;\n }\n\n this.handleDropdownKeydown(\n event,\n this.selectedField.valueList,\n this.valueHighlightIndex,\n (index) => this.valueHighlightIndex = index,\n (item) => this.selectValueFromOption(item.value),\n (item) => item.label,\n () => this.closeValueDropdown()\n );\n }\n\n /**\n * Generic handler for dropdown keyboard navigation\n */\n private handleDropdownKeydown<T>(\n event: KeyboardEvent,\n items: T[],\n currentIndex: number,\n setIndex: (index: number) => void,\n onSelect: (item: T) => void,\n getLabel: (item: T) => string,\n onClose: () => void\n ): void {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n setIndex(Math.min(currentIndex + 1, items.length - 1));\n this.scrollHighlightedItemIntoView();\n break;\n\n case 'ArrowUp':\n event.preventDefault();\n setIndex(Math.max(currentIndex - 1, 0));\n this.scrollHighlightedItemIntoView();\n break;\n\n case 'Enter':\n case ' ':\n event.preventDefault();\n if (currentIndex >= 0 && currentIndex < items.length) {\n onSelect(items[currentIndex]);\n }\n break;\n\n case 'Escape':\n case 'Tab':\n event.preventDefault();\n onClose();\n break;\n\n case 'Home':\n event.preventDefault();\n setIndex(0);\n this.scrollHighlightedItemIntoView();\n break;\n\n case 'End':\n event.preventDefault();\n setIndex(items.length - 1);\n this.scrollHighlightedItemIntoView();\n break;\n\n default:\n // Type-ahead: jump to first item starting with typed character\n if (event.key.length === 1 && /[a-zA-Z0-9]/.test(event.key)) {\n const char = event.key.toLowerCase();\n const startIndex = currentIndex + 1;\n\n // Search from current position to end, then from start\n for (let i = 0; i < items.length; i++) {\n const idx = (startIndex + i) % items.length;\n const label = getLabel(items[idx]).toLowerCase();\n if (label.startsWith(char)) {\n setIndex(idx);\n this.scrollHighlightedItemIntoView();\n break;\n }\n }\n }\n break;\n }\n }\n\n /**\n * Scroll the highlighted dropdown item into view\n */\n private scrollHighlightedItemIntoView(): void {\n // Use setTimeout to let Angular update the DOM first\n setTimeout(() => {\n const highlightedEl = this.elementRef.nativeElement.querySelector('.dropdown-item.highlighted');\n if (highlightedEl) {\n highlightedEl.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n }\n }, 0);\n }\n\n // ========================================\n // DISPLAY HELPERS\n // ========================================\n\n /**\n * Get the display name for the currently selected field\n */\n getSelectedFieldDisplayName(): string {\n if (!this.filter.field) return 'Select field...';\n const field = this.fields.find(f => f.name === this.filter.field);\n return field?.displayName || this.filter.field;\n }\n\n /**\n * Get the label for the currently selected operator\n */\n getSelectedOperatorLabel(): string {\n if (!this.filter.operator) return 'Select...';\n const op = this.availableOperators.find(o => o.value === this.filter.operator);\n return op?.label || this.filter.operator;\n }\n\n /**\n * Get the label for the currently selected value (for value list dropdowns)\n */\n getSelectedValueLabel(): string {\n if (!this.filter.value || !this.selectedField?.valueList) return 'Select...';\n const option = this.selectedField.valueList.find(o => o.value === this.filter.value);\n return option?.label || String(this.filter.value);\n }\n\n // ========================================\n // SELECTION HANDLERS\n // ========================================\n\n /**\n * Handle field selection from custom dropdown\n */\n selectField(fieldName: string): void {\n this.closeFieldDropdown();\n this.onFieldChange(fieldName);\n }\n\n /**\n * Handle operator selection from custom dropdown\n */\n selectOperator(operator: string): void {\n this.closeOperatorDropdown();\n this.onOperatorChange(operator as FilterOperator);\n }\n\n /**\n * Handle value selection from custom dropdown\n */\n selectValue(value: string): void {\n this.closeValueDropdown();\n this.onValueChange(value);\n }\n\n /**\n * Handle value selection from value list option (supports union type)\n */\n selectValueFromOption(value: string | number | boolean): void {\n this.closeValueDropdown();\n this.onValueChange(value);\n }\n\n /**\n * Handle field selection change\n */\n onFieldChange(fieldName: string): void {\n const field = this.fields.find(f => f.name === fieldName);\n if (!field) return;\n\n this.selectedField = field;\n this.availableOperators = getOperatorsForType(field.type);\n\n // Get default operator for the new field type\n const defaultOperator = this.availableOperators[0]?.value || 'eq';\n this.requiresValue = operatorRequiresValue(defaultOperator);\n\n // Emit updated filter with new field and reset value\n this.emitChange({\n field: fieldName,\n operator: defaultOperator,\n value: this.getDefaultValue(field.type)\n });\n }\n\n /**\n * Handle operator selection change\n */\n onOperatorChange(operator: FilterOperator): void {\n this.requiresValue = operatorRequiresValue(operator);\n\n // If operator doesn't require value, clear it\n const value = this.requiresValue ? this.filter.value : null;\n\n this.emitChange({\n ...this.filter,\n operator,\n value\n });\n }\n\n /**\n * Handle value change\n */\n onValueChange(value: unknown): void {\n this.emitChange({\n ...this.filter,\n value\n });\n }\n\n /**\n * Handle boolean toggle\n */\n onBooleanChange(value: boolean): void {\n this.emitChange({\n ...this.filter,\n value\n });\n }\n\n /**\n * Handle date change\n */\n onDateChange(event: Event): void {\n const input = event.target as HTMLInputElement;\n const value = input.value ? new Date(input.value).toISOString() : null;\n this.emitChange({\n ...this.filter,\n value\n });\n }\n\n /**\n * Handle delete button click\n */\n onDelete(): void {\n this.delete.emit();\n }\n\n /**\n * Emit the filter change event\n */\n private emitChange(filter: FilterDescriptor): void {\n this.filterChange.emit(filter);\n }\n\n /**\n * Get default value for a field type\n */\n private getDefaultValue(type: FilterFieldType): unknown {\n switch (type) {\n case 'string':\n return '';\n case 'number':\n return null;\n case 'boolean':\n return true;\n case 'date':\n return null;\n case 'lookup':\n return null;\n default:\n return null;\n }\n }\n\n /**\n * Get the date value formatted for the date input\n */\n getDateInputValue(): string {\n if (!this.filter.value) return '';\n try {\n const date = new Date(this.filter.value as string);\n return date.toISOString().split('T')[0];\n } catch {\n return '';\n }\n }\n\n /**\n * Check if the current field has a value list (dropdown options)\n */\n hasValueList(): boolean {\n return !!(this.selectedField?.valueList && this.selectedField.valueList.length > 0);\n }\n}\n","<div class=\"filter-rule\" [class.disabled]=\"disabled\">\n <!-- Field Selector - Custom Dropdown -->\n <div class=\"custom-dropdown field-dropdown\" [class.open]=\"fieldDropdownOpen\" [class.disabled]=\"disabled\">\n <button\n #fieldDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleFieldDropdown()\"\n (keydown)=\"onFieldKeydown($event)\"\n [disabled]=\"disabled\">\n <span class=\"dropdown-value\">{{ getSelectedFieldDisplayName() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (fieldDropdownOpen) {\n <div class=\"dropdown-menu\">\n @for (field of fields; track field.name; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"field.name === filter.field\"\n [class.highlighted]=\"i === fieldHighlightIndex\"\n (click)=\"selectField(field.name)\">\n {{ field.displayName }}\n </div>\n }\n @if (fields.length === 0) {\n <div class=\"dropdown-empty\">No fields available</div>\n }\n </div>\n }\n </div>\n\n <!-- Operator Selector - Custom Dropdown -->\n <div class=\"custom-dropdown operator-dropdown\" [class.open]=\"operatorDropdownOpen\" [class.disabled]=\"disabled || !selectedField\">\n <button\n #operatorDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleOperatorDropdown()\"\n (keydown)=\"onOperatorKeydown($event)\"\n [disabled]=\"disabled || !selectedField\">\n <span class=\"dropdown-value\">{{ getSelectedOperatorLabel() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (operatorDropdownOpen) {\n <div class=\"dropdown-menu\">\n @for (op of availableOperators; track op.value; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"op.value === filter.operator\"\n [class.highlighted]=\"i === operatorHighlightIndex\"\n (click)=\"selectOperator(op.value)\">\n {{ op.label }}\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Value Editor - varies by field type -->\n @if (requiresValue && selectedField) {\n <!-- String with value list (custom dropdown) -->\n @if (selectedField.type === 'string' && hasValueList()) {\n <div class=\"custom-dropdown value-dropdown\" [class.open]=\"valueDropdownOpen\" [class.disabled]=\"disabled\">\n <button\n #valueDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleValueDropdown()\"\n (keydown)=\"onValueKeydown($event)\"\n [disabled]=\"disabled\">\n <span class=\"dropdown-value\">{{ getSelectedValueLabel() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (valueDropdownOpen) {\n <div class=\"dropdown-menu\">\n <div\n class=\"dropdown-item\"\n [class.selected]=\"!filter.value\"\n [class.highlighted]=\"0 === valueHighlightIndex\"\n (click)=\"selectValue('')\">\n Select...\n </div>\n @for (option of selectedField.valueList; track option.value; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"option.value === filter.value\"\n [class.highlighted]=\"(i + 1) === valueHighlightIndex\"\n (click)=\"selectValueFromOption(option.value)\">\n {{ option.label }}\n </div>\n }\n </div>\n }\n </div>\n }\n <!-- String (text input) -->\n @else if (selectedField.type === 'string') {\n <input\n type=\"text\"\n class=\"value-input\"\n [value]=\"filter.value || ''\"\n (input)=\"onValueChange($any($event.target).value)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter value...\">\n }\n <!-- Number -->\n @else if (selectedField.type === 'number') {\n <input\n type=\"number\"\n class=\"value-input\"\n [value]=\"filter.value\"\n (input)=\"onValueChange($any($event.target).valueAsNumber)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter number...\">\n }\n <!-- Boolean -->\n @else if (selectedField.type === 'boolean') {\n <div class=\"value-toggle\">\n <button\n type=\"button\"\n class=\"toggle-btn true\"\n [class.active]=\"filter.value === true\"\n (click)=\"onBooleanChange(true)\"\n [disabled]=\"disabled\">\n True\n </button>\n <button\n type=\"button\"\n class=\"toggle-btn false\"\n [class.active]=\"filter.value === false\"\n (click)=\"onBooleanChange(false)\"\n [disabled]=\"disabled\">\n False\n </button>\n </div>\n }\n <!-- Date -->\n @else if (selectedField.type === 'date') {\n <input\n type=\"date\"\n class=\"value-input value-date\"\n [value]=\"getDateInputValue()\"\n (change)=\"onDateChange($event)\"\n [disabled]=\"disabled\">\n }\n <!-- Lookup (for now, simple text input - can be enhanced with record selector) -->\n @else if (selectedField.type === 'lookup') {\n <input\n type=\"text\"\n class=\"value-input\"\n [value]=\"filter.value || ''\"\n (input)=\"onValueChange($any($event.target).value)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter ID...\">\n }\n }\n\n <!-- Actions -->\n <div class=\"rule-actions\">\n @if (showDelete) {\n <button\n type=\"button\"\n class=\"action-btn delete\"\n (click)=\"onDelete()\"\n [disabled]=\"disabled\"\n title=\"Remove filter\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"filter-rule.component.js","sourceRoot":"","sources":["../../../src/lib/filter-rule/filter-rule.component.ts","../../../src/lib/filter-rule/filter-rule.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, ViewEncapsulation, HostListener, ElementRef, ViewChild } from '@angular/core';\nimport {\n FilterDescriptor,\n FilterFieldInfo,\n FilterFieldType,\n FilterOperator\n} from '../types/filter.types';\nimport { getOperatorsForType, OperatorInfo, operatorRequiresValue } from '../types/operators';\n\n/**\n * FilterRuleComponent - A single filter condition row\n *\n * Displays field selector, operator selector, and value editor\n * based on the field type.\n */\n@Component({\n standalone: false,\n selector: 'mj-filter-rule',\n templateUrl: './filter-rule.component.html',\n styleUrls: ['./filter-rule.component.css'],\n encapsulation: ViewEncapsulation.None\n})\nexport class FilterRuleComponent implements OnInit, OnChanges {\n /**\n * The filter descriptor for this rule\n */\n @Input() filter!: FilterDescriptor;\n\n /**\n * Available fields to filter on\n */\n @Input() fields: FilterFieldInfo[] = [];\n\n /**\n * Whether the component is disabled\n */\n @Input() disabled: boolean = false;\n\n /**\n * Whether to show the delete button\n */\n @Input() showDelete: boolean = true;\n\n /**\n * Emitted when the filter changes\n */\n @Output() filterChange = new EventEmitter<FilterDescriptor>();\n\n /**\n * Emitted when the delete button is clicked\n */\n @Output() delete = new EventEmitter<void>();\n\n /**\n * Currently selected field info\n */\n public selectedField: FilterFieldInfo | null = null;\n\n /**\n * Available operators for the selected field type\n */\n public availableOperators: OperatorInfo[] = [];\n\n /**\n * Whether the current operator requires a value\n */\n public requiresValue: boolean = true;\n\n // Dropdown state\n\n // Fixed-position style for the active dropdown (escapes overflow clipping)\n public dropdownMenuStyle: Record<string, string> = {};\n\n public fieldDropdownOpen = false;\n public operatorDropdownOpen = false;\n public valueDropdownOpen = false;\n\n // Keyboard navigation state\n public fieldHighlightIndex = -1;\n public operatorHighlightIndex = -1;\n public valueHighlightIndex = -1;\n\n // ViewChild references for dropdown buttons (Safari focus fix)\n @ViewChild('fieldDropdownBtn') fieldDropdownBtn!: ElementRef<HTMLButtonElement>;\n @ViewChild('operatorDropdownBtn') operatorDropdownBtn!: ElementRef<HTMLButtonElement>;\n @ViewChild('valueDropdownBtn') valueDropdownBtn!: ElementRef<HTMLButtonElement>;\n\n constructor(private elementRef: ElementRef) {}\n\n /**\n * Close dropdowns when clicking outside the component\n */\n @HostListener('document:click', ['$event'])\n onDocumentClick(event: MouseEvent): void {\n if (!this.elementRef.nativeElement.contains(event.target)) {\n this.closeAllDropdowns();\n }\n }\n\n ngOnInit(): void {\n this.updateFieldSelection();\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['filter'] || changes['fields']) {\n this.updateFieldSelection();\n }\n }\n\n /**\n * Update the selected field and available operators\n */\n private updateFieldSelection(): void {\n if (!this.filter || !this.fields.length) return;\n\n this.selectedField = this.fields.find(f => f.name === this.filter.field) || null;\n\n if (this.selectedField) {\n this.availableOperators = getOperatorsForType(this.selectedField.type);\n this.requiresValue = operatorRequiresValue(this.filter.operator);\n } else {\n this.availableOperators = [];\n this.requiresValue = true;\n }\n }\n\n // ========================================\n // DROPDOWN POSITIONING\n // ========================================\n\n /**\n * Calculate fixed-position style for a dropdown menu based on its trigger button.\n * Uses position:fixed so the menu escapes any overflow:hidden/auto ancestors.\n * Accounts for CSS transform on ancestors (which change the containing block for fixed positioning).\n */\n private calculateDropdownPosition(triggerBtn: ElementRef<HTMLButtonElement> | undefined): void {\n if (!triggerBtn?.nativeElement) {\n this.dropdownMenuStyle = {};\n return;\n }\n const rect = triggerBtn.nativeElement.getBoundingClientRect();\n const offset = this.getTransformAncestorOffset(triggerBtn.nativeElement);\n this.dropdownMenuStyle = {\n position: 'fixed',\n top: `${rect.bottom + 4 - offset.top}px`,\n left: `${rect.left - offset.left}px`,\n width: `${rect.width}px`\n };\n }\n\n /**\n * Find the nearest ancestor with a CSS transform, which creates a new\n * containing block for position:fixed elements per CSS spec.\n */\n private getTransformAncestorOffset(element: HTMLElement): { top: number; left: number } {\n let el: HTMLElement | null = element.parentElement;\n while (el) {\n const transform = getComputedStyle(el).transform;\n if (transform && transform !== 'none') {\n const ancestorRect = el.getBoundingClientRect();\n return { top: ancestorRect.top, left: ancestorRect.left };\n }\n el = el.parentElement;\n }\n return { top: 0, left: 0 };\n }\n\n // ========================================\n // DROPDOWN TOGGLE METHODS\n // ========================================\n\n toggleFieldDropdown(): void {\n if (this.disabled) return;\n const wasOpen = this.fieldDropdownOpen;\n this.closeAllDropdowns();\n this.fieldDropdownOpen = !wasOpen;\n if (this.fieldDropdownOpen) {\n this.calculateDropdownPosition(this.fieldDropdownBtn);\n setTimeout(() => this.fieldDropdownBtn?.nativeElement?.focus(), 0);\n }\n }\n\n toggleOperatorDropdown(): void {\n if (this.disabled || !this.selectedField) return;\n const wasOpen = this.operatorDropdownOpen;\n this.closeAllDropdowns();\n this.operatorDropdownOpen = !wasOpen;\n if (this.operatorDropdownOpen) {\n this.calculateDropdownPosition(this.operatorDropdownBtn);\n setTimeout(() => this.operatorDropdownBtn?.nativeElement?.focus(), 0);\n }\n }\n\n toggleValueDropdown(): void {\n if (this.disabled) return;\n const wasOpen = this.valueDropdownOpen;\n this.closeAllDropdowns();\n this.valueDropdownOpen = !wasOpen;\n if (this.valueDropdownOpen) {\n this.calculateDropdownPosition(this.valueDropdownBtn);\n setTimeout(() => this.valueDropdownBtn?.nativeElement?.focus(), 0);\n }\n }\n\n closeFieldDropdown(): void {\n this.fieldDropdownOpen = false;\n }\n\n closeOperatorDropdown(): void {\n this.operatorDropdownOpen = false;\n }\n\n closeValueDropdown(): void {\n this.valueDropdownOpen = false;\n }\n\n closeAllDropdowns(): void {\n this.fieldDropdownOpen = false;\n this.operatorDropdownOpen = false;\n this.valueDropdownOpen = false;\n this.resetHighlightIndices();\n }\n\n resetHighlightIndices(): void {\n this.fieldHighlightIndex = -1;\n this.operatorHighlightIndex = -1;\n this.valueHighlightIndex = -1;\n }\n\n // ========================================\n // KEYBOARD NAVIGATION\n // ========================================\n\n /**\n * Handle keyboard events on the field dropdown trigger\n */\n onFieldKeydown(event: KeyboardEvent): void {\n if (this.disabled) return;\n\n if (!this.fieldDropdownOpen) {\n // Open dropdown on Enter, Space, or arrow keys\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n this.toggleFieldDropdown();\n this.fieldHighlightIndex = this.fields.findIndex(f => f.name === this.filter.field);\n if (this.fieldHighlightIndex < 0) this.fieldHighlightIndex = 0;\n }\n return;\n }\n\n this.handleDropdownKeydown(\n event,\n this.fields,\n this.fieldHighlightIndex,\n (index) => this.fieldHighlightIndex = index,\n (item) => this.selectField(item.name),\n (item) => item.displayName,\n () => this.closeFieldDropdown()\n );\n }\n\n /**\n * Handle keyboard events on the operator dropdown trigger\n */\n onOperatorKeydown(event: KeyboardEvent): void {\n if (this.disabled || !this.selectedField) return;\n\n if (!this.operatorDropdownOpen) {\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n this.toggleOperatorDropdown();\n this.operatorHighlightIndex = this.availableOperators.findIndex(o => o.value === this.filter.operator);\n if (this.operatorHighlightIndex < 0) this.operatorHighlightIndex = 0;\n }\n return;\n }\n\n this.handleDropdownKeydown(\n event,\n this.availableOperators,\n this.operatorHighlightIndex,\n (index) => this.operatorHighlightIndex = index,\n (item) => this.selectOperator(item.value),\n (item) => item.label,\n () => this.closeOperatorDropdown()\n );\n }\n\n /**\n * Handle keyboard events on the value dropdown trigger\n */\n onValueKeydown(event: KeyboardEvent): void {\n if (this.disabled || !this.selectedField?.valueList) return;\n\n if (!this.valueDropdownOpen) {\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n this.toggleValueDropdown();\n const valueList = this.selectedField.valueList;\n this.valueHighlightIndex = valueList.findIndex(v => v.value === this.filter.value);\n if (this.valueHighlightIndex < 0) this.valueHighlightIndex = 0;\n }\n return;\n }\n\n this.handleDropdownKeydown(\n event,\n this.selectedField.valueList,\n this.valueHighlightIndex,\n (index) => this.valueHighlightIndex = index,\n (item) => this.selectValueFromOption(item.value),\n (item) => item.label,\n () => this.closeValueDropdown()\n );\n }\n\n /**\n * Generic handler for dropdown keyboard navigation\n */\n private handleDropdownKeydown<T>(\n event: KeyboardEvent,\n items: T[],\n currentIndex: number,\n setIndex: (index: number) => void,\n onSelect: (item: T) => void,\n getLabel: (item: T) => string,\n onClose: () => void\n ): void {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n setIndex(Math.min(currentIndex + 1, items.length - 1));\n this.scrollHighlightedItemIntoView();\n break;\n\n case 'ArrowUp':\n event.preventDefault();\n setIndex(Math.max(currentIndex - 1, 0));\n this.scrollHighlightedItemIntoView();\n break;\n\n case 'Enter':\n case ' ':\n event.preventDefault();\n if (currentIndex >= 0 && currentIndex < items.length) {\n onSelect(items[currentIndex]);\n }\n break;\n\n case 'Escape':\n case 'Tab':\n event.preventDefault();\n onClose();\n break;\n\n case 'Home':\n event.preventDefault();\n setIndex(0);\n this.scrollHighlightedItemIntoView();\n break;\n\n case 'End':\n event.preventDefault();\n setIndex(items.length - 1);\n this.scrollHighlightedItemIntoView();\n break;\n\n default:\n // Type-ahead: jump to first item starting with typed character\n if (event.key.length === 1 && /[a-zA-Z0-9]/.test(event.key)) {\n const char = event.key.toLowerCase();\n const startIndex = currentIndex + 1;\n\n // Search from current position to end, then from start\n for (let i = 0; i < items.length; i++) {\n const idx = (startIndex + i) % items.length;\n const label = getLabel(items[idx]).toLowerCase();\n if (label.startsWith(char)) {\n setIndex(idx);\n this.scrollHighlightedItemIntoView();\n break;\n }\n }\n }\n break;\n }\n }\n\n /**\n * Scroll the highlighted dropdown item into view\n */\n private scrollHighlightedItemIntoView(): void {\n // Use setTimeout to let Angular update the DOM first\n setTimeout(() => {\n const highlightedEl = this.elementRef.nativeElement.querySelector('.dropdown-item.highlighted');\n if (highlightedEl) {\n highlightedEl.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n }\n }, 0);\n }\n\n // ========================================\n // DISPLAY HELPERS\n // ========================================\n\n /**\n * Get the display name for the currently selected field\n */\n getSelectedFieldDisplayName(): string {\n if (!this.filter.field) return 'Select field...';\n const field = this.fields.find(f => f.name === this.filter.field);\n return field?.displayName || this.filter.field;\n }\n\n /**\n * Get the label for the currently selected operator\n */\n getSelectedOperatorLabel(): string {\n if (!this.filter.operator) return 'Select...';\n const op = this.availableOperators.find(o => o.value === this.filter.operator);\n return op?.label || this.filter.operator;\n }\n\n /**\n * Get the label for the currently selected value (for value list dropdowns)\n */\n getSelectedValueLabel(): string {\n if (!this.filter.value || !this.selectedField?.valueList) return 'Select...';\n const option = this.selectedField.valueList.find(o => o.value === this.filter.value);\n return option?.label || String(this.filter.value);\n }\n\n // ========================================\n // SELECTION HANDLERS\n // ========================================\n\n /**\n * Handle field selection from custom dropdown\n */\n selectField(fieldName: string): void {\n this.closeFieldDropdown();\n this.onFieldChange(fieldName);\n }\n\n /**\n * Handle operator selection from custom dropdown\n */\n selectOperator(operator: string): void {\n this.closeOperatorDropdown();\n this.onOperatorChange(operator as FilterOperator);\n }\n\n /**\n * Handle value selection from custom dropdown\n */\n selectValue(value: string): void {\n this.closeValueDropdown();\n this.onValueChange(value);\n }\n\n /**\n * Handle value selection from value list option (supports union type)\n */\n selectValueFromOption(value: string | number | boolean): void {\n this.closeValueDropdown();\n this.onValueChange(value);\n }\n\n /**\n * Handle field selection change\n */\n onFieldChange(fieldName: string): void {\n const field = this.fields.find(f => f.name === fieldName);\n if (!field) return;\n\n this.selectedField = field;\n this.availableOperators = getOperatorsForType(field.type);\n\n // Get default operator for the new field type\n const defaultOperator = this.availableOperators[0]?.value || 'eq';\n this.requiresValue = operatorRequiresValue(defaultOperator);\n\n // Emit updated filter with new field and reset value\n this.emitChange({\n field: fieldName,\n operator: defaultOperator,\n value: this.getDefaultValue(field.type)\n });\n }\n\n /**\n * Handle operator selection change\n */\n onOperatorChange(operator: FilterOperator): void {\n this.requiresValue = operatorRequiresValue(operator);\n\n // If operator doesn't require value, clear it\n const value = this.requiresValue ? this.filter.value : null;\n\n this.emitChange({\n ...this.filter,\n operator,\n value\n });\n }\n\n /**\n * Handle value change\n */\n onValueChange(value: unknown): void {\n this.emitChange({\n ...this.filter,\n value\n });\n }\n\n /**\n * Handle boolean toggle\n */\n onBooleanChange(value: boolean): void {\n this.emitChange({\n ...this.filter,\n value\n });\n }\n\n /**\n * Handle date change\n */\n onDateChange(event: Event): void {\n const input = event.target as HTMLInputElement;\n const value = input.value ? new Date(input.value).toISOString() : null;\n this.emitChange({\n ...this.filter,\n value\n });\n }\n\n /**\n * Handle delete button click\n */\n onDelete(): void {\n this.delete.emit();\n }\n\n /**\n * Emit the filter change event\n */\n private emitChange(filter: FilterDescriptor): void {\n this.filterChange.emit(filter);\n }\n\n /**\n * Get default value for a field type\n */\n private getDefaultValue(type: FilterFieldType): unknown {\n switch (type) {\n case 'string':\n return '';\n case 'number':\n return null;\n case 'boolean':\n return true;\n case 'date':\n return null;\n case 'lookup':\n return null;\n default:\n return null;\n }\n }\n\n /**\n * Get the date value formatted for the date input\n */\n getDateInputValue(): string {\n if (!this.filter.value) return '';\n try {\n const date = new Date(this.filter.value as string);\n return date.toISOString().split('T')[0];\n } catch {\n return '';\n }\n }\n\n /**\n * Check if the current field has a value list (dropdown options)\n */\n hasValueList(): boolean {\n return !!(this.selectedField?.valueList && this.selectedField.valueList.length > 0);\n }\n}\n","<div class=\"filter-rule\" [class.disabled]=\"disabled\">\n <!-- Field Selector - Custom Dropdown -->\n <div class=\"custom-dropdown field-dropdown\" [class.open]=\"fieldDropdownOpen\" [class.disabled]=\"disabled\">\n <button\n #fieldDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleFieldDropdown()\"\n (keydown)=\"onFieldKeydown($event)\"\n [disabled]=\"disabled\">\n <span class=\"dropdown-value\">{{ getSelectedFieldDisplayName() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (fieldDropdownOpen) {\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownMenuStyle\">\n @for (field of fields; track field.name; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"field.name === filter.field\"\n [class.highlighted]=\"i === fieldHighlightIndex\"\n (click)=\"selectField(field.name)\">\n {{ field.displayName }}\n </div>\n }\n @if (fields.length === 0) {\n <div class=\"dropdown-empty\">No fields available</div>\n }\n </div>\n }\n </div>\n\n <!-- Operator Selector - Custom Dropdown -->\n <div class=\"custom-dropdown operator-dropdown\" [class.open]=\"operatorDropdownOpen\" [class.disabled]=\"disabled || !selectedField\">\n <button\n #operatorDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleOperatorDropdown()\"\n (keydown)=\"onOperatorKeydown($event)\"\n [disabled]=\"disabled || !selectedField\">\n <span class=\"dropdown-value\">{{ getSelectedOperatorLabel() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (operatorDropdownOpen) {\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownMenuStyle\">\n @for (op of availableOperators; track op.value; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"op.value === filter.operator\"\n [class.highlighted]=\"i === operatorHighlightIndex\"\n (click)=\"selectOperator(op.value)\">\n {{ op.label }}\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Value Editor - varies by field type -->\n @if (requiresValue && selectedField) {\n <!-- String with value list (custom dropdown) -->\n @if (selectedField.type === 'string' && hasValueList()) {\n <div class=\"custom-dropdown value-dropdown\" [class.open]=\"valueDropdownOpen\" [class.disabled]=\"disabled\">\n <button\n #valueDropdownBtn\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleValueDropdown()\"\n (keydown)=\"onValueKeydown($event)\"\n [disabled]=\"disabled\">\n <span class=\"dropdown-value\">{{ getSelectedValueLabel() }}</span>\n <i class=\"fa-solid fa-chevron-down dropdown-arrow\"></i>\n </button>\n @if (valueDropdownOpen) {\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownMenuStyle\">\n <div\n class=\"dropdown-item\"\n [class.selected]=\"!filter.value\"\n [class.highlighted]=\"0 === valueHighlightIndex\"\n (click)=\"selectValue('')\">\n Select...\n </div>\n @for (option of selectedField.valueList; track option.value; let i = $index) {\n <div\n class=\"dropdown-item\"\n [class.selected]=\"option.value === filter.value\"\n [class.highlighted]=\"(i + 1) === valueHighlightIndex\"\n (click)=\"selectValueFromOption(option.value)\">\n {{ option.label }}\n </div>\n }\n </div>\n }\n </div>\n }\n <!-- String (text input) -->\n @else if (selectedField.type === 'string') {\n <input\n type=\"text\"\n class=\"value-input\"\n [value]=\"filter.value || ''\"\n (input)=\"onValueChange($any($event.target).value)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter value...\">\n }\n <!-- Number -->\n @else if (selectedField.type === 'number') {\n <input\n type=\"number\"\n class=\"value-input\"\n [value]=\"filter.value\"\n (input)=\"onValueChange($any($event.target).valueAsNumber)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter number...\">\n }\n <!-- Boolean -->\n @else if (selectedField.type === 'boolean') {\n <div class=\"value-toggle\">\n <button\n type=\"button\"\n class=\"toggle-btn true\"\n [class.active]=\"filter.value === true\"\n (click)=\"onBooleanChange(true)\"\n [disabled]=\"disabled\">\n True\n </button>\n <button\n type=\"button\"\n class=\"toggle-btn false\"\n [class.active]=\"filter.value === false\"\n (click)=\"onBooleanChange(false)\"\n [disabled]=\"disabled\">\n False\n </button>\n </div>\n }\n <!-- Date -->\n @else if (selectedField.type === 'date') {\n <input\n type=\"date\"\n class=\"value-input value-date\"\n [value]=\"getDateInputValue()\"\n (change)=\"onDateChange($event)\"\n [disabled]=\"disabled\">\n }\n <!-- Lookup (for now, simple text input - can be enhanced with record selector) -->\n @else if (selectedField.type === 'lookup') {\n <input\n type=\"text\"\n class=\"value-input\"\n [value]=\"filter.value || ''\"\n (input)=\"onValueChange($any($event.target).value)\"\n [disabled]=\"disabled\"\n placeholder=\"Enter ID...\">\n }\n }\n\n <!-- Actions -->\n <div class=\"rule-actions\">\n @if (showDelete) {\n <button\n type=\"button\"\n class=\"action-btn delete\"\n (click)=\"onDelete()\"\n [disabled]=\"disabled\"\n title=\"Remove filter\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n</div>\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-filter-builder",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.9.0",
|
|
4
4
|
"description": "MemberJunction: Angular Filter Builder Component - a modern, intuitive filter builder for creating complex boolean filter expressions with portable JSON format.",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@angular/platform-browser": "21.1.3",
|
|
36
|
-
"@memberjunction/core": "5.
|
|
36
|
+
"@memberjunction/core": "5.9.0",
|
|
37
37
|
"tslib": "^2.8.1"
|
|
38
38
|
},
|
|
39
39
|
"sideEffects": false,
|