@memberjunction/ng-filter-builder 5.11.0 → 5.13.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.
|
@@ -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
|
|
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 var(--mj-border-default);\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: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n.filter-count[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-primary);\n font-weight: 500;\n}\n\n.filter-count.no-filters[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\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: var(--mj-bg-surface-card);\n color: var(--mj-text-secondary);\n}\n\n.clear-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.apply-btn[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, white);\n}\n\n.apply-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\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: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-radius: 6px;\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n}\n\n.filter-tips[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n\n.filter-tips[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-warning);\n margin-top: 2px;\n}\n\n.filter-tips[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n}\n\n\n\n.filter-expression-summary[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\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: var(--mj-bg-surface);\n border-color: var(--mj-border-strong);\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: var(--mj-text-muted);\n text-align: left;\n transition: all 0.15s ease;\n}\n\n.expression-toggle[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n}\n\n.expression-toggle[_ngcontent-%COMP%] i[_ngcontent-%COMP%]:first-child {\n font-size: 10px;\n color: var(--mj-text-disabled);\n width: 12px;\n transition: transform 0.2s ease;\n}\n\n.expression-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\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: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\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: var(--mj-text-primary);\n padding: 16px;\n margin: 0;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\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: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.expression-text[_ngcontent-%COMP%] .operator[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\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: var(--mj-status-success);\n}\n\n.expression-text[_ngcontent-%COMP%] .value-number[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\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: var(--mj-status-success);\n}\n\n.expression-text[_ngcontent-%COMP%] .value-false[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.expression-text[_ngcontent-%COMP%] .value-date[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\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: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.expression-text[_ngcontent-%COMP%] .logic-or[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.expression-text[_ngcontent-%COMP%] .group-bracket[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-weight: 700;\n font-size: 15px;\n}\n\n.expression-text[_ngcontent-%COMP%] .no-filters[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\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
|
|
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 var(--mj-border-default);\n}\n\n.filter-summary {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.filter-icon {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n.filter-count {\n font-size: 13px;\n color: var(--mj-text-primary);\n font-weight: 500;\n}\n\n.filter-count.no-filters {\n color: var(--mj-text-disabled);\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: var(--mj-bg-surface-card);\n color: var(--mj-text-secondary);\n}\n\n.clear-btn:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.apply-btn {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, white);\n}\n\n.apply-btn:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n/* Content */\n.filter-content {\n min-height: 60px;\n}\n\n/* Tips */\n.filter-tips {\n padding: 12px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-radius: 6px;\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n}\n\n.filter-tips p {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n\n.filter-tips i {\n color: var(--mj-status-warning);\n margin-top: 2px;\n}\n\n.filter-tips strong {\n color: var(--mj-text-primary);\n}\n\n/* Collapsible Filter Expression Summary */\n.filter-expression-summary {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n margin-top: 8px;\n transition: all 0.2s ease;\n}\n\n.filter-expression-summary.expanded {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-strong);\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: var(--mj-text-muted);\n text-align: left;\n transition: all 0.15s ease;\n}\n\n.expression-toggle:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n}\n\n.expression-toggle i:first-child {\n font-size: 10px;\n color: var(--mj-text-disabled);\n width: 12px;\n transition: transform 0.2s ease;\n}\n\n.expression-icon {\n color: var(--mj-brand-primary);\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: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\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: var(--mj-text-primary);\n padding: 16px;\n margin: 0;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\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: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.expression-text .operator {\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n.expression-text .value {\n font-weight: 500;\n}\n\n.expression-text .value-string {\n color: var(--mj-status-success);\n}\n\n.expression-text .value-number {\n color: var(--mj-brand-primary);\n}\n\n.expression-text .value-boolean {\n font-weight: 600;\n}\n\n.expression-text .value-true {\n color: var(--mj-status-success);\n}\n\n.expression-text .value-false {\n color: var(--mj-status-error);\n}\n\n.expression-text .value-date {\n color: var(--mj-status-error);\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: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.expression-text .logic-or {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.expression-text .group-bracket {\n color: var(--mj-brand-primary);\n font-weight: 700;\n font-size: 15px;\n}\n\n.expression-text .no-filters {\n color: var(--mj-text-disabled);\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: [{
|
|
@@ -304,11 +304,11 @@ export class FilterGroupComponent {
|
|
|
304
304
|
i0.ɵɵrepeater(ctx.filter.filters);
|
|
305
305
|
i0.ɵɵadvance(2);
|
|
306
306
|
i0.ɵɵconditional(!ctx.filter.filters || ctx.filter.filters.length === 0 ? 17 : -1);
|
|
307
|
-
} }, dependencies: [FilterGroupComponent, i1.FilterRuleComponent, i2.UpperCasePipe], styles: ["/* Scoped to mj-filter-group to prevent style leakage with ViewEncapsulation.None */\nmj-filter-group .filter-group {\n border: 1px solid
|
|
307
|
+
} }, dependencies: [FilterGroupComponent, i1.FilterRuleComponent, i2.UpperCasePipe], styles: ["/* Scoped to mj-filter-group to prevent style leakage with ViewEncapsulation.None */\nmj-filter-group .filter-group {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 12px;\n background: var(--mj-bg-surface-card);\n transition: all 0.15s ease;\n}\n\nmj-filter-group .filter-group.root {\n background: transparent;\n border: none;\n padding: 0;\n}\n\nmj-filter-group .filter-group:not(.root) {\n margin-left: 16px;\n border-left: 3px solid var(--mj-brand-primary);\n}\n\nmj-filter-group .filter-group[data-depth=\"1\"]:not(.root) {\n border-left-color: #7b1fa2;\n}\n\nmj-filter-group .filter-group[data-depth=\"2\"]:not(.root) {\n border-left-color: #388e3c;\n}\n\nmj-filter-group .filter-group[data-depth=\"3\"]:not(.root) {\n border-left-color: #f57c00;\n}\n\nmj-filter-group .filter-group.disabled {\n opacity: 0.6;\n pointer-events: none;\n}\n\n/* Group Header */\nmj-filter-group .group-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 12px;\n flex-wrap: wrap;\n}\n\n/* Logic Toggle */\nmj-filter-group .logic-toggle {\n display: flex;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n padding: 3px;\n border: 1px solid var(--mj-border-default);\n}\n\nmj-filter-group .logic-btn {\n padding: 8px 20px;\n border: none;\n background: transparent;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n letter-spacing: 0.5px;\n cursor: pointer;\n transition: all 0.2s ease;\n color: var(--mj-text-muted);\n}\n\nmj-filter-group .logic-btn:hover:not(:disabled) {\n color: var(--mj-text-secondary);\n background: color-mix(in srgb, var(--mj-bg-surface) 50%, transparent);\n}\n\nmj-filter-group .logic-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\nmj-filter-group .logic-btn.active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n font-weight: 700;\n}\n\n/* Group Actions */\nmj-filter-group .group-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\nmj-filter-group .add-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 14px;\n border: 1px dashed var(--mj-border-strong);\n background: var(--mj-bg-surface);\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\nmj-filter-group .add-btn:hover:not(:disabled) {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\nmj-filter-group .add-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\nmj-filter-group .add-btn.primary {\n border-style: solid;\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, white);\n}\n\nmj-filter-group .add-btn.primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n}\n\nmj-filter-group .add-btn i {\n font-size: 12px;\n}\n\nmj-filter-group .add-group {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\nmj-filter-group .add-group:hover:not(:disabled) {\n border-color: var(--mj-brand-primary-hover);\n color: var(--mj-brand-primary-hover);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\nmj-filter-group .delete-group-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: transparent;\n border-radius: 8px;\n color: var(--mj-text-disabled);\n cursor: pointer;\n transition: all 0.2s ease;\n font-size: 14px;\n}\n\nmj-filter-group .delete-group-btn:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\nmj-filter-group .delete-group-btn:disabled {\n cursor: not-allowed;\n opacity: 0.3;\n}\n\n/* Filters Container */\nmj-filter-group .filters-container {\n display: flex;\n flex-direction: column;\n gap: 0;\n}\n\nmj-filter-group .filter-item {\n display: flex;\n flex-direction: column;\n}\n\n/* Logic Connector */\nmj-filter-group .logic-connector {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n margin: 4px 0;\n}\n\nmj-filter-group .connector-line {\n flex: 1;\n height: 1px;\n background: var(--mj-border-default);\n}\n\nmj-filter-group .connector-label {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n letter-spacing: 0.5px;\n}\n\n/* Empty State */\nmj-filter-group .empty-state {\n text-align: center;\n padding: 24px;\n color: var(--mj-text-disabled);\n}\n\nmj-filter-group .empty-state p {\n margin: 0 0 12px 0;\n font-size: 14px;\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n mj-filter-group .group-header {\n flex-direction: column;\n align-items: stretch;\n }\n\n mj-filter-group .logic-toggle {\n justify-content: center;\n }\n\n mj-filter-group .group-actions {\n justify-content: center;\n flex-wrap: wrap;\n }\n\n mj-filter-group .filter-group:not(.root) {\n margin-left: 8px;\n }\n}\n"], encapsulation: 2 });
|
|
308
308
|
}
|
|
309
309
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FilterGroupComponent, [{
|
|
310
310
|
type: Component,
|
|
311
|
-
args: [{ standalone: false, selector: 'mj-filter-group', encapsulation: ViewEncapsulation.None, template: "<div class=\"filter-group\" [class.root]=\"isRoot\" [class.disabled]=\"disabled\" [attr.data-depth]=\"depth\">\n <!-- Group Header -->\n <div class=\"group-header\">\n <div class=\"logic-toggle\">\n <button\n type=\"button\"\n class=\"logic-btn\"\n [class.active]=\"filter.logic === 'and'\"\n (click)=\"setLogic('and')\"\n [disabled]=\"disabled\"\n title=\"Match ALL conditions\">\n AND\n </button>\n <button\n type=\"button\"\n class=\"logic-btn\"\n [class.active]=\"filter.logic === 'or'\"\n (click)=\"setLogic('or')\"\n [disabled]=\"disabled\"\n title=\"Match ANY condition\">\n OR\n </button>\n </div>\n\n <div class=\"group-actions\">\n <button\n type=\"button\"\n class=\"add-btn\"\n (click)=\"addRule()\"\n [disabled]=\"disabled\"\n title=\"Add filter condition\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Add Condition</span>\n </button>\n @if (canAddGroup()) {\n <button\n type=\"button\"\n class=\"add-btn add-group\"\n (click)=\"addGroup()\"\n [disabled]=\"disabled\"\n title=\"Add nested group\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>Add Group</span>\n </button>\n }\n @if (!isRoot) {\n <button\n type=\"button\"\n class=\"delete-group-btn\"\n (click)=\"onDelete()\"\n [disabled]=\"disabled\"\n title=\"Remove this group\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n }\n </div>\n </div>\n\n <!-- Filters Container -->\n <div class=\"filters-container\">\n @for (item of filter.filters; track trackByIndex($index); let i = $index; let isLast = $last) {\n <div class=\"filter-item\">\n <!-- Logic connector between items -->\n @if (i > 0) {\n <div class=\"logic-connector\">\n <span class=\"connector-line\"></span>\n <span class=\"connector-label\">{{ filter.logic | uppercase }}</span>\n <span class=\"connector-line\"></span>\n </div>\n }\n\n @if (isGroup(item)) {\n <!-- Nested Group -->\n <mj-filter-group\n [filter]=\"asGroup(item)\"\n [fields]=\"fields\"\n [isRoot]=\"false\"\n [depth]=\"depth + 1\"\n [maxDepth]=\"maxDepth\"\n [disabled]=\"disabled\"\n (filterChange)=\"onGroupChange(i, $event)\"\n (delete)=\"deleteFilter(i)\">\n </mj-filter-group>\n } @else {\n <!-- Simple Rule -->\n <mj-filter-rule\n [filter]=\"asRule(item)\"\n [fields]=\"fields\"\n [disabled]=\"disabled\"\n [showDelete]=\"filter.filters.length > 1 || !isRoot\"\n (filterChange)=\"onRuleChange(i, $event)\"\n (delete)=\"deleteFilter(i)\">\n </mj-filter-rule>\n }\n </div>\n }\n </div>\n\n <!-- Empty State -->\n @if (!filter.filters || filter.filters.length === 0) {\n <div class=\"empty-state\">\n <p>No filter conditions defined.</p>\n <button\n type=\"button\"\n class=\"add-btn primary\"\n (click)=\"addRule()\"\n [disabled]=\"disabled\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Add your first condition</span>\n </button>\n </div>\n }\n</div>\n", styles: ["/* Scoped to mj-filter-group to prevent style leakage with ViewEncapsulation.None */\nmj-filter-group .filter-group {\n border: 1px solid
|
|
311
|
+
args: [{ standalone: false, selector: 'mj-filter-group', encapsulation: ViewEncapsulation.None, template: "<div class=\"filter-group\" [class.root]=\"isRoot\" [class.disabled]=\"disabled\" [attr.data-depth]=\"depth\">\n <!-- Group Header -->\n <div class=\"group-header\">\n <div class=\"logic-toggle\">\n <button\n type=\"button\"\n class=\"logic-btn\"\n [class.active]=\"filter.logic === 'and'\"\n (click)=\"setLogic('and')\"\n [disabled]=\"disabled\"\n title=\"Match ALL conditions\">\n AND\n </button>\n <button\n type=\"button\"\n class=\"logic-btn\"\n [class.active]=\"filter.logic === 'or'\"\n (click)=\"setLogic('or')\"\n [disabled]=\"disabled\"\n title=\"Match ANY condition\">\n OR\n </button>\n </div>\n\n <div class=\"group-actions\">\n <button\n type=\"button\"\n class=\"add-btn\"\n (click)=\"addRule()\"\n [disabled]=\"disabled\"\n title=\"Add filter condition\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Add Condition</span>\n </button>\n @if (canAddGroup()) {\n <button\n type=\"button\"\n class=\"add-btn add-group\"\n (click)=\"addGroup()\"\n [disabled]=\"disabled\"\n title=\"Add nested group\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>Add Group</span>\n </button>\n }\n @if (!isRoot) {\n <button\n type=\"button\"\n class=\"delete-group-btn\"\n (click)=\"onDelete()\"\n [disabled]=\"disabled\"\n title=\"Remove this group\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n }\n </div>\n </div>\n\n <!-- Filters Container -->\n <div class=\"filters-container\">\n @for (item of filter.filters; track trackByIndex($index); let i = $index; let isLast = $last) {\n <div class=\"filter-item\">\n <!-- Logic connector between items -->\n @if (i > 0) {\n <div class=\"logic-connector\">\n <span class=\"connector-line\"></span>\n <span class=\"connector-label\">{{ filter.logic | uppercase }}</span>\n <span class=\"connector-line\"></span>\n </div>\n }\n\n @if (isGroup(item)) {\n <!-- Nested Group -->\n <mj-filter-group\n [filter]=\"asGroup(item)\"\n [fields]=\"fields\"\n [isRoot]=\"false\"\n [depth]=\"depth + 1\"\n [maxDepth]=\"maxDepth\"\n [disabled]=\"disabled\"\n (filterChange)=\"onGroupChange(i, $event)\"\n (delete)=\"deleteFilter(i)\">\n </mj-filter-group>\n } @else {\n <!-- Simple Rule -->\n <mj-filter-rule\n [filter]=\"asRule(item)\"\n [fields]=\"fields\"\n [disabled]=\"disabled\"\n [showDelete]=\"filter.filters.length > 1 || !isRoot\"\n (filterChange)=\"onRuleChange(i, $event)\"\n (delete)=\"deleteFilter(i)\">\n </mj-filter-rule>\n }\n </div>\n }\n </div>\n\n <!-- Empty State -->\n @if (!filter.filters || filter.filters.length === 0) {\n <div class=\"empty-state\">\n <p>No filter conditions defined.</p>\n <button\n type=\"button\"\n class=\"add-btn primary\"\n (click)=\"addRule()\"\n [disabled]=\"disabled\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Add your first condition</span>\n </button>\n </div>\n }\n</div>\n", styles: ["/* Scoped to mj-filter-group to prevent style leakage with ViewEncapsulation.None */\nmj-filter-group .filter-group {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 12px;\n background: var(--mj-bg-surface-card);\n transition: all 0.15s ease;\n}\n\nmj-filter-group .filter-group.root {\n background: transparent;\n border: none;\n padding: 0;\n}\n\nmj-filter-group .filter-group:not(.root) {\n margin-left: 16px;\n border-left: 3px solid var(--mj-brand-primary);\n}\n\nmj-filter-group .filter-group[data-depth=\"1\"]:not(.root) {\n border-left-color: #7b1fa2;\n}\n\nmj-filter-group .filter-group[data-depth=\"2\"]:not(.root) {\n border-left-color: #388e3c;\n}\n\nmj-filter-group .filter-group[data-depth=\"3\"]:not(.root) {\n border-left-color: #f57c00;\n}\n\nmj-filter-group .filter-group.disabled {\n opacity: 0.6;\n pointer-events: none;\n}\n\n/* Group Header */\nmj-filter-group .group-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 12px;\n flex-wrap: wrap;\n}\n\n/* Logic Toggle */\nmj-filter-group .logic-toggle {\n display: flex;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n padding: 3px;\n border: 1px solid var(--mj-border-default);\n}\n\nmj-filter-group .logic-btn {\n padding: 8px 20px;\n border: none;\n background: transparent;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n letter-spacing: 0.5px;\n cursor: pointer;\n transition: all 0.2s ease;\n color: var(--mj-text-muted);\n}\n\nmj-filter-group .logic-btn:hover:not(:disabled) {\n color: var(--mj-text-secondary);\n background: color-mix(in srgb, var(--mj-bg-surface) 50%, transparent);\n}\n\nmj-filter-group .logic-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\nmj-filter-group .logic-btn.active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n font-weight: 700;\n}\n\n/* Group Actions */\nmj-filter-group .group-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\nmj-filter-group .add-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 14px;\n border: 1px dashed var(--mj-border-strong);\n background: var(--mj-bg-surface);\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\nmj-filter-group .add-btn:hover:not(:disabled) {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\nmj-filter-group .add-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\nmj-filter-group .add-btn.primary {\n border-style: solid;\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, white);\n}\n\nmj-filter-group .add-btn.primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n}\n\nmj-filter-group .add-btn i {\n font-size: 12px;\n}\n\nmj-filter-group .add-group {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\nmj-filter-group .add-group:hover:not(:disabled) {\n border-color: var(--mj-brand-primary-hover);\n color: var(--mj-brand-primary-hover);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\nmj-filter-group .delete-group-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: transparent;\n border-radius: 8px;\n color: var(--mj-text-disabled);\n cursor: pointer;\n transition: all 0.2s ease;\n font-size: 14px;\n}\n\nmj-filter-group .delete-group-btn:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\nmj-filter-group .delete-group-btn:disabled {\n cursor: not-allowed;\n opacity: 0.3;\n}\n\n/* Filters Container */\nmj-filter-group .filters-container {\n display: flex;\n flex-direction: column;\n gap: 0;\n}\n\nmj-filter-group .filter-item {\n display: flex;\n flex-direction: column;\n}\n\n/* Logic Connector */\nmj-filter-group .logic-connector {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n margin: 4px 0;\n}\n\nmj-filter-group .connector-line {\n flex: 1;\n height: 1px;\n background: var(--mj-border-default);\n}\n\nmj-filter-group .connector-label {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n letter-spacing: 0.5px;\n}\n\n/* Empty State */\nmj-filter-group .empty-state {\n text-align: center;\n padding: 24px;\n color: var(--mj-text-disabled);\n}\n\nmj-filter-group .empty-state p {\n margin: 0 0 12px 0;\n font-size: 14px;\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n mj-filter-group .group-header {\n flex-direction: column;\n align-items: stretch;\n }\n\n mj-filter-group .logic-toggle {\n justify-content: center;\n }\n\n mj-filter-group .group-actions {\n justify-content: center;\n flex-wrap: wrap;\n }\n\n mj-filter-group .filter-group:not(.root) {\n margin-left: 8px;\n }\n}\n"] }]
|
|
312
312
|
}], null, { filter: [{
|
|
313
313
|
type: Input
|
|
314
314
|
}], fields: [{
|
|
@@ -728,11 +728,11 @@ export class FilterRuleComponent {
|
|
|
728
728
|
i0.ɵɵconditional(ctx.requiresValue && ctx.selectedField ? 15 : -1);
|
|
729
729
|
i0.ɵɵadvance(2);
|
|
730
730
|
i0.ɵɵconditional(ctx.showDelete ? 17 : -1);
|
|
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:
|
|
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: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\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: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\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 var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\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: var(--mj-text-disabled);\n background: var(--mj-bg-surface-card);\n}\n\nmj-filter-rule .dropdown-trigger:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n background: var(--mj-bg-surface);\n}\n\nmj-filter-rule .dropdown-trigger:disabled {\n background-color: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n cursor: not-allowed;\n box-shadow: none;\n}\n\nmj-filter-rule .custom-dropdown.open .dropdown-trigger {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\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: var(--mj-text-muted);\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\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: var(--mj-text-primary);\n cursor: pointer;\n transition: all 0.15s ease;\n border-bottom: 1px solid var(--mj-border-default);\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: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\nmj-filter-rule .dropdown-item.highlighted {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: -2px;\n}\n\nmj-filter-rule .dropdown-item.selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\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: var(--mj-brand-primary);\n}\n\nmj-filter-rule .dropdown-empty {\n padding: 16px;\n text-align: center;\n color: var(--mj-text-disabled);\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: var(--mj-bg-surface-sunken);\n border-radius: 3px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 3px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n/* ========================================\n VALUE INPUT STYLES\n ======================================== */\n\nmj-filter-rule .value-input {\n padding: 10px 14px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-primary);\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: var(--mj-text-disabled);\n background-color: var(--mj-bg-surface-card);\n}\n\nmj-filter-rule .value-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n background-color: var(--mj-bg-surface);\n}\n\nmj-filter-rule .value-input:disabled {\n background-color: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n cursor: not-allowed;\n box-shadow: none;\n}\n\nmj-filter-rule .value-input::placeholder {\n color: var(--mj-text-disabled);\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: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n padding: 3px;\n border: 1px solid var(--mj-border-default);\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: var(--mj-text-muted);\n}\n\nmj-filter-rule .toggle-btn:hover:not(:disabled) {\n color: var(--mj-text-secondary);\n background: color-mix(in srgb, var(--mj-bg-surface) 50%, transparent);\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: var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n color: var(--mj-text-primary);\n}\n\nmj-filter-rule .toggle-btn.true.active {\n color: var(--mj-status-success);\n}\n\nmj-filter-rule .toggle-btn.false.active {\n color: var(--mj-status-error);\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: var(--mj-text-disabled);\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: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\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: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\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 });
|
|
732
732
|
}
|
|
733
733
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FilterRuleComponent, [{
|
|
734
734
|
type: Component,
|
|
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"] }]
|
|
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: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\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: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\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 var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\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: var(--mj-text-disabled);\n background: var(--mj-bg-surface-card);\n}\n\nmj-filter-rule .dropdown-trigger:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n background: var(--mj-bg-surface);\n}\n\nmj-filter-rule .dropdown-trigger:disabled {\n background-color: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n cursor: not-allowed;\n box-shadow: none;\n}\n\nmj-filter-rule .custom-dropdown.open .dropdown-trigger {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\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: var(--mj-text-muted);\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\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: var(--mj-text-primary);\n cursor: pointer;\n transition: all 0.15s ease;\n border-bottom: 1px solid var(--mj-border-default);\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: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\nmj-filter-rule .dropdown-item.highlighted {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: -2px;\n}\n\nmj-filter-rule .dropdown-item.selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\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: var(--mj-brand-primary);\n}\n\nmj-filter-rule .dropdown-empty {\n padding: 16px;\n text-align: center;\n color: var(--mj-text-disabled);\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: var(--mj-bg-surface-sunken);\n border-radius: 3px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 3px;\n}\n\nmj-filter-rule .dropdown-menu::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n/* ========================================\n VALUE INPUT STYLES\n ======================================== */\n\nmj-filter-rule .value-input {\n padding: 10px 14px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-primary);\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: var(--mj-text-disabled);\n background-color: var(--mj-bg-surface-card);\n}\n\nmj-filter-rule .value-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n background-color: var(--mj-bg-surface);\n}\n\nmj-filter-rule .value-input:disabled {\n background-color: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n cursor: not-allowed;\n box-shadow: none;\n}\n\nmj-filter-rule .value-input::placeholder {\n color: var(--mj-text-disabled);\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: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n padding: 3px;\n border: 1px solid var(--mj-border-default);\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: var(--mj-text-muted);\n}\n\nmj-filter-rule .toggle-btn:hover:not(:disabled) {\n color: var(--mj-text-secondary);\n background: color-mix(in srgb, var(--mj-bg-surface) 50%, transparent);\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: var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n color: var(--mj-text-primary);\n}\n\nmj-filter-rule .toggle-btn.true.active {\n color: var(--mj-status-success);\n}\n\nmj-filter-rule .toggle-btn.false.active {\n color: var(--mj-status-error);\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: var(--mj-text-disabled);\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: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\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: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\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"] }]
|
|
736
736
|
}], () => [{ type: i0.ElementRef }], { filter: [{
|
|
737
737
|
type: Input
|
|
738
738
|
}], fields: [{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-filter-builder",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.13.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.13.0",
|
|
37
37
|
"tslib": "^2.8.1"
|
|
38
38
|
},
|
|
39
39
|
"sideEffects": false,
|