@gp-grid/angular 0.22.0 → 0.23.1

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/styles.css CHANGED
@@ -1 +1 @@
1
- .gp-grid-container{--gp-grid-bg:#fff;--gp-grid-bg-alt:#f8f9fa;--gp-grid-text:#212529;--gp-grid-text-secondary:#6c757d;--gp-grid-text-muted:#adb5bd;--gp-grid-border:#dee2e6;--gp-grid-border-light:#e9ecef;--gp-grid-header-bg:#f1f3f5;--gp-grid-header-text:#212529;--gp-grid-header-z-index:10;--gp-grid-primary:#228be6;--gp-grid-primary-light:#e7f5ff;--gp-grid-primary-border:#74c0fc;--gp-grid-hover:#f1f3f5;--gp-grid-filter-bg:#f8f9fa;--gp-grid-input-bg:#fff;--gp-grid-input-border:#ced4da;--gp-grid-error-bg:#fff5f5;--gp-grid-error-text:#c92a2a;--gp-grid-loading-bg:#fffffff2;--gp-grid-loading-text:#495057;--gp-grid-scrollbar-track:#f1f3f5;--gp-grid-scrollbar-thumb:#ced4da;--gp-grid-scrollbar-thumb-hover:#adb5bd}.gp-grid-container--dark{--gp-grid-bg:#1a1b1e;--gp-grid-bg-alt:#25262b;--gp-grid-text:#c1c2c5;--gp-grid-text-secondary:#909296;--gp-grid-text-muted:#5c5f66;--gp-grid-border:#373a40;--gp-grid-border-light:#2c2e33;--gp-grid-header-bg:#25262b;--gp-grid-header-text:#c1c2c5;--gp-grid-header-z-index:10;--gp-grid-primary:#339af0;--gp-grid-primary-light:#1c3d5a;--gp-grid-primary-border:#1c7ed6;--gp-grid-hover:#2c2e33;--gp-grid-filter-bg:#25262b;--gp-grid-input-bg:#1a1b1e;--gp-grid-input-border:#373a40;--gp-grid-error-bg:#2c1a1a;--gp-grid-error-text:#ff6b6b;--gp-grid-loading-bg:#1a1b1ef2;--gp-grid-loading-text:#c1c2c5;--gp-grid-scrollbar-track:#25262b;--gp-grid-scrollbar-thumb:#373a40;--gp-grid-scrollbar-thumb-hover:#4a4d52}.gp-grid-container{-webkit-user-select:none;user-select:none;outline:none}:where(.gp-grid-container){color:var(--gp-grid-text);background-color:var(--gp-grid-bg);border:1px solid var(--gp-grid-border);border-radius:6px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:13px;line-height:1.5}.gp-grid-container:focus{outline:none}:where(.gp-grid-container:focus){border-color:var(--gp-grid-primary)}.gp-grid-header{z-index:11;flex-shrink:0;position:sticky;top:0;left:0;overflow:hidden}:where(.gp-grid-header){background-color:var(--gp-grid-header-bg);border-bottom:1px solid var(--gp-grid-border)}.gp-grid-container .gp-grid-header-cell{box-sizing:border-box;user-select:none;touch-action:none;display:flex;position:absolute;top:0}:where(.gp-grid-header-cell){border-right:1px solid var(--gp-grid-border);letter-spacing:.5px;color:var(--gp-grid-header-text);cursor:pointer;background-color:#0000;align-items:center;padding:0 12px;font-size:12px;font-weight:600;transition:background-color .1s}:where(.gp-grid-header-cell:hover){background-color:var(--gp-grid-hover)}:where(.gp-grid-header-cell:active){background-color:var(--gp-grid-border-light)}.gp-grid-container .gp-grid-header-text{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}:where(.gp-grid-header-text){color:var(--gp-grid-header-text)}.gp-grid-header-icons{align-items:center;gap:4px;margin-left:auto;display:flex}.gp-grid-sort-arrows{flex-direction:row;align-items:center;gap:2px;margin-left:6px;display:flex}.gp-grid-sort-arrows-stack{flex-direction:column;align-items:center;gap:2px;display:flex}:where(.gp-grid-sort-arrow-up),:where(.gp-grid-sort-arrow-down){opacity:.35;color:var(--gp-grid-text);transition:opacity .15s,color .15s}:where(.gp-grid-sort-arrow-up.active),:where(.gp-grid-sort-arrow-down.active){opacity:1;color:var(--gp-grid-primary)}:where(.gp-grid-sort-index){color:var(--gp-grid-primary);font-size:9px;font-weight:600}.gp-grid-filter-icon{justify-content:center;align-items:center;width:20px;height:20px;display:flex}:where(.gp-grid-filter-icon){cursor:pointer;color:var(--gp-grid-text-secondary);border-radius:4px;margin-left:2px;transition:background-color .15s,color .15s}:where(.gp-grid-filter-icon:hover){background-color:var(--gp-grid-hover);color:var(--gp-grid-primary)}:where(.gp-grid-filter-icon.active){color:var(--gp-grid-primary);background-color:var(--gp-grid-primary-light)}@media (pointer:coarse) and (hover:none) and (width<=768px){.gp-grid-filter-icon{width:44px;min-width:44px;height:44px;min-height:44px}:where(.gp-grid-filter-icon){margin-left:0}.gp-grid-filter-icon svg{width:20px;height:20px}}.gp-grid-header--loading .gp-grid-header-cell{pointer-events:none}:where(.gp-grid-header--loading .gp-grid-header-cell){opacity:.6;cursor:default}.gp-grid-header-resize-handle{cursor:col-resize;z-index:10;touch-action:none;width:6px;height:100%;position:absolute;top:0;right:-3px}:where(.gp-grid-header-resize-handle:hover),:where(.gp-grid-header-resize-handle--active){background-color:var(--gp-grid-primary);opacity:.4}@media (pointer:coarse) and (hover:none) and (width<=768px){.gp-grid-header-resize-handle{width:24px;right:-12px}}.gp-grid-column-resize-line{z-index:1000;pointer-events:none;width:2px;height:100%;position:absolute;top:0}:where(.gp-grid-column-resize-line){background-color:var(--gp-grid-primary)}.gp-grid-column-move-ghost{pointer-events:none;z-index:2000;box-sizing:border-box;white-space:nowrap;text-overflow:ellipsis;align-items:center;display:flex;position:fixed;overflow:hidden}:where(.gp-grid-column-move-ghost){opacity:.7;background-color:var(--gp-grid-header-bg);border:2px solid var(--gp-grid-primary);letter-spacing:.5px;color:var(--gp-grid-header-text);border-radius:4px;padding:0 12px;font-size:12px;font-weight:600;box-shadow:0 4px 12px #00000026}.gp-grid-column-drop-indicator{z-index:1000;pointer-events:none;width:3px;height:100%;position:absolute;top:0;left:0}:where(.gp-grid-column-drop-indicator){background-color:var(--gp-grid-primary);border-radius:2px}.gp-grid-rows-wrapper{z-index:1;will-change:transform;position:absolute;top:0;left:0}.gp-grid-row{position:absolute;top:0;left:0}:where(.gp-grid-row){background-color:var(--gp-grid-bg)}.gp-grid-cell{box-sizing:border-box;white-space:nowrap;-webkit-user-select:none;user-select:none;display:flex;position:absolute;top:0;overflow:hidden}.gp-grid-cell-content{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}.gp-grid-cell--wrap{align-items:flex-start}.gp-grid-cell--wrap .gp-grid-cell-content{white-space:normal;overflow-wrap:anywhere;text-overflow:clip}:where(.gp-grid-cell){cursor:cell;color:var(--gp-grid-text);border-right:1px solid var(--gp-grid-border-light);border-bottom:1px solid var(--gp-grid-border-light);background-color:#0000;align-items:center;padding:0 12px}.gp-grid-cell--active{z-index:5;outline:none}:where(.gp-grid-cell--active){background-color:var(--gp-grid-primary-light);border:2px solid var(--gp-grid-primary);padding:0 11px}:where(.gp-grid-cell--selected){background-color:var(--gp-grid-primary-light)}.gp-grid-cell--editing{z-index:10}:where(.gp-grid-cell--editing){background-color:var(--gp-grid-bg);border:2px solid var(--gp-grid-primary);padding:0}.gp-grid-fill-handle{z-index:11;pointer-events:auto;box-sizing:border-box;touch-action:none;width:8px;height:8px;position:absolute}:where(.gp-grid-fill-handle){background-color:var(--gp-grid-primary);border:2px solid var(--gp-grid-bg);cursor:crosshair;border-radius:1px}:where(.gp-grid-fill-handle:hover){transform:scale(1.2)}@media (pointer:coarse) and (hover:none) and (width<=768px){.gp-grid-fill-handle{width:24px;height:24px}:where(.gp-grid-fill-handle){border-width:3px;border-radius:3px}}:where(.gp-grid-cell.gp-grid-cell--fill-preview){background-color:var(--gp-grid-primary-light);border:1px dashed var(--gp-grid-primary)}.gp-grid-edit-input{width:100%;height:100%}:where(.gp-grid-edit-input){font-family:inherit;font-size:inherit;color:var(--gp-grid-text);background-color:#0000;border:none;padding:0 11px}.gp-grid-edit-input:focus{outline:none}.gp-grid-cell-peek{z-index:20;box-sizing:border-box;max-height:320px;position:absolute;overflow:auto}:where(.gp-grid-cell-peek){background-color:var(--gp-grid-bg);color:var(--gp-grid-text);border:2px solid var(--gp-grid-primary);font-family:inherit;font-size:inherit;white-space:pre-wrap;overflow-wrap:anywhere;cursor:text;-webkit-user-select:text;user-select:text;border-radius:3px;padding:8px 11px;line-height:1.4;box-shadow:0 4px 12px #00000026}.gp-grid-cell-peek .gp-grid-cell-content{white-space:inherit;text-overflow:clip;overflow:visible}.gp-grid-loading-anchor{z-index:900;pointer-events:none;height:0;position:sticky;top:0;left:0;overflow:visible}.gp-grid-loading-overlay{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0}:where(.gp-grid-loading-overlay){background-color:var(--gp-grid-loading-overlay-bg,#fff6)}:where(.gp-grid-container--dark .gp-grid-loading-overlay){background-color:var(--gp-grid-loading-overlay-bg,#0000004d)}.gp-grid-loading{z-index:1000;pointer-events:auto;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}:where(.gp-grid-loading){background-color:var(--gp-grid-loading-bg);color:var(--gp-grid-loading-text);border:1px solid var(--gp-grid-border);border-radius:6px;align-items:center;gap:10px;padding:12px 20px;font-size:13px;font-weight:500}.gp-grid-loading-spinner{width:16px;height:16px;animation:.7s linear infinite gp-grid-spin}:where(.gp-grid-loading-spinner){border:2px solid var(--gp-grid-border);border-top-color:var(--gp-grid-primary);border-radius:50%}@keyframes gp-grid-spin{to{transform:rotate(360deg)}}.gp-grid-error{z-index:1000;-webkit-user-select:text;user-select:text;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}:where(.gp-grid-error){background-color:var(--gp-grid-error-bg);color:var(--gp-grid-error-text);border:1px solid var(--gp-grid-error-text);text-align:center;cursor:text;border-radius:6px;max-width:80%;padding:12px 20px;font-size:13px;font-weight:500}.gp-grid-empty{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}:where(.gp-grid-empty){color:var(--gp-grid-text-muted);text-align:center;font-size:14px}.gp-grid-container::-webkit-scrollbar{width:8px;height:8px}:where(.gp-grid-container)::-webkit-scrollbar-track{background-color:var(--gp-grid-scrollbar-track)}:where(.gp-grid-container)::-webkit-scrollbar-thumb{background-color:var(--gp-grid-scrollbar-thumb);border-radius:4px}:where(.gp-grid-container)::-webkit-scrollbar-thumb:hover{background-color:var(--gp-grid-scrollbar-thumb-hover)}:where(.gp-grid-container)::-webkit-scrollbar-corner{background-color:var(--gp-grid-scrollbar-track)}.gp-grid-filter-popup{flex-direction:column;max-width:min(360px,100vw - 16px);max-height:400px;display:flex}:where(.gp-grid-filter-popup){background-color:var(--gp-grid-bg);border:1px solid var(--gp-grid-border);border-radius:6px;font-size:13px;box-shadow:0 4px 12px #00000026}:where(.gp-grid-filter-header){border-bottom:1px solid var(--gp-grid-border-light);color:var(--gp-grid-header-text);padding:10px 12px;font-weight:600}.gp-grid-filter-content{flex-direction:column;min-height:0;display:flex;overflow-y:auto}:where(.gp-grid-filter-content){gap:10px;padding:10px 12px}.gp-grid-filter-mode-toggle{display:flex}:where(.gp-grid-filter-mode-toggle){background-color:var(--gp-grid-bg-alt);border-radius:4px;gap:4px;padding:2px}.gp-grid-filter-mode-toggle button{flex:1}:where(.gp-grid-filter-mode-toggle button){color:var(--gp-grid-text-secondary);cursor:pointer;background-color:#0000;border:none;border-radius:3px;padding:6px 10px;font-family:inherit;font-size:11px;transition:background-color .15s,color .15s}:where(.gp-grid-filter-mode-toggle button:hover){color:var(--gp-grid-text)}:where(.gp-grid-filter-mode-toggle button.active){background-color:var(--gp-grid-bg);color:var(--gp-grid-text);box-shadow:0 1px 2px #0000001a}:where(.gp-grid-filter-info){color:var(--gp-grid-text-secondary);background-color:var(--gp-grid-bg-alt);border-radius:4px;padding:6px 8px;font-size:11px}.gp-grid-filter-search{box-sizing:border-box;width:100%;height:30px}:where(.gp-grid-filter-search){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-input-bg);color:var(--gp-grid-text);border-radius:4px;padding:0 10px;font-family:inherit;font-size:12px}:where(.gp-grid-filter-search:focus){border-color:var(--gp-grid-primary);outline:none}.gp-grid-filter-actions{display:flex}:where(.gp-grid-filter-actions){gap:8px}.gp-grid-filter-actions button{flex:1}:where(.gp-grid-filter-actions button){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-bg);color:var(--gp-grid-text);cursor:pointer;border-radius:3px;padding:4px 8px;font-family:inherit;font-size:11px}:where(.gp-grid-filter-actions button:hover:not(:disabled)){background-color:var(--gp-grid-hover)}:where(.gp-grid-filter-actions button:disabled){opacity:.5;cursor:not-allowed}.gp-grid-filter-list{flex-direction:column;max-height:200px;display:flex;overflow-y:auto}:where(.gp-grid-filter-list){border:1px solid var(--gp-grid-border-light);border-radius:4px;gap:4px;padding:6px}.gp-grid-filter-option{align-items:center;display:flex}:where(.gp-grid-filter-option){cursor:pointer;border-radius:3px;gap:8px;padding:4px 6px}:where(.gp-grid-filter-option:hover){background-color:var(--gp-grid-hover)}.gp-grid-filter-option input[type=checkbox]{margin:0}:where(.gp-grid-filter-option input[type=checkbox]){cursor:pointer}.gp-grid-filter-option span{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}:where(.gp-grid-filter-blank){color:var(--gp-grid-text-muted);font-style:italic}.gp-grid-filter-condition{flex-direction:column;display:flex}:where(.gp-grid-filter-condition){gap:6px}.gp-grid-filter-row{align-items:center;display:flex}:where(.gp-grid-filter-row){gap:6px}.gp-grid-filter-row select{height:30px}:where(.gp-grid-filter-row select){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-input-bg);color:var(--gp-grid-text);cursor:pointer;border-radius:4px;padding:0 6px;font-family:inherit;font-size:12px}.gp-grid-filter-row input[type=number],.gp-grid-filter-row input[type=date],.gp-grid-filter-row input[type=text],.gp-grid-filter-text-input{box-sizing:border-box;flex:1;min-width:0;height:30px}:where(.gp-grid-filter-row input[type=number]),:where(.gp-grid-filter-row input[type=date]),:where(.gp-grid-filter-row input[type=text]),:where(.gp-grid-filter-text-input){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-input-bg);color:var(--gp-grid-text);border-radius:4px;padding:0 8px;font-family:inherit;font-size:12px}:where(.gp-grid-filter-row input:focus){border-color:var(--gp-grid-primary);outline:none}:where(.gp-grid-filter-to){color:var(--gp-grid-text-secondary);font-size:11px}.gp-grid-filter-remove{width:24px;height:24px}:where(.gp-grid-filter-remove){color:var(--gp-grid-text-muted);cursor:pointer;background-color:#0000;border:none;border-radius:3px;padding:0;font-size:16px;line-height:1}:where(.gp-grid-filter-remove:hover){background-color:var(--gp-grid-error-bg);color:var(--gp-grid-error-text)}.gp-grid-filter-combination{display:flex}:where(.gp-grid-filter-combination){gap:4px;margin-bottom:4px}.gp-grid-filter-combination button{flex:1}:where(.gp-grid-filter-combination button){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-bg);color:var(--gp-grid-text-secondary);cursor:pointer;border-radius:3px;padding:4px 8px;font-family:inherit;font-size:10px}:where(.gp-grid-filter-combination button.active){background-color:var(--gp-grid-primary);border-color:var(--gp-grid-primary);color:#fff}.gp-grid-filter-add{width:100%}:where(.gp-grid-filter-add){border:1px dashed var(--gp-grid-border);color:var(--gp-grid-text-secondary);cursor:pointer;background-color:#0000;border-radius:4px;padding:6px;font-family:inherit;font-size:11px}:where(.gp-grid-filter-add:hover){background-color:var(--gp-grid-hover);border-color:var(--gp-grid-primary);color:var(--gp-grid-primary)}.gp-grid-filter-buttons{display:flex}:where(.gp-grid-filter-buttons){border-top:1px solid var(--gp-grid-border-light);gap:8px;padding-top:8px}.gp-grid-filter-btn-clear,.gp-grid-filter-btn-apply{flex:1}:where(.gp-grid-filter-btn-clear),:where(.gp-grid-filter-btn-apply){cursor:pointer;border-radius:4px;padding:8px 12px;font-family:inherit;font-size:12px}:where(.gp-grid-filter-btn-clear){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-bg);color:var(--gp-grid-text)}:where(.gp-grid-filter-btn-clear:hover){background-color:var(--gp-grid-hover)}:where(.gp-grid-filter-btn-apply){border:1px solid var(--gp-grid-primary);background-color:var(--gp-grid-primary);color:#fff}:where(.gp-grid-filter-btn-apply:hover){opacity:.9}.gp-grid-cell--row-drag-handle{touch-action:none}:where(.gp-grid-cell--row-drag-handle){cursor:grab}:where(.gp-grid-cell--row-drag-handle:active){cursor:grabbing}.gp-grid-row-drag-icon{justify-content:center;align-items:center;width:100%;height:100%;display:flex}:where(.gp-grid-row-drag-icon){color:var(--gp-grid-text-secondary)}.gp-grid-row-drag-icon svg{width:16px;height:16px}.gp-grid-row-drag-ghost{pointer-events:none;z-index:2000;box-sizing:border-box;position:fixed;overflow:hidden}:where(.gp-grid-row-drag-ghost){opacity:.8;background-color:var(--gp-grid-bg,#fff);border:2px solid var(--gp-grid-primary);border-radius:4px;box-shadow:0 4px 12px #00000026}.gp-grid-row-drop-indicator{z-index:1000;pointer-events:none;width:100%;height:3px;position:absolute;left:0}:where(.gp-grid-row-drop-indicator){background-color:var(--gp-grid-primary);border-radius:2px}
1
+ .gp-grid-container{--gp-grid-bg:#fff;--gp-grid-bg-alt:#f8f9fa;--gp-grid-text:#212529;--gp-grid-text-secondary:#6c757d;--gp-grid-text-muted:#adb5bd;--gp-grid-border:#dee2e6;--gp-grid-border-light:#e9ecef;--gp-grid-header-bg:#f1f3f5;--gp-grid-header-text:#212529;--gp-grid-header-z-index:10;--gp-grid-primary:#228be6;--gp-grid-primary-light:#e7f5ff;--gp-grid-primary-border:#74c0fc;--gp-grid-hover:#f1f3f5;--gp-grid-filter-bg:#f8f9fa;--gp-grid-input-bg:#fff;--gp-grid-input-border:#ced4da;--gp-grid-error-bg:#fff5f5;--gp-grid-error-text:#c92a2a;--gp-grid-loading-bg:#fffffff2;--gp-grid-loading-text:#495057;--gp-grid-scrollbar-track:#f1f3f5;--gp-grid-scrollbar-thumb:#ced4da;--gp-grid-scrollbar-thumb-hover:#adb5bd}.gp-grid-container--dark{--gp-grid-bg:#1a1b1e;--gp-grid-bg-alt:#25262b;--gp-grid-text:#c1c2c5;--gp-grid-text-secondary:#909296;--gp-grid-text-muted:#5c5f66;--gp-grid-border:#373a40;--gp-grid-border-light:#2c2e33;--gp-grid-header-bg:#25262b;--gp-grid-header-text:#c1c2c5;--gp-grid-header-z-index:10;--gp-grid-primary:#339af0;--gp-grid-primary-light:#1c3d5a;--gp-grid-primary-border:#1c7ed6;--gp-grid-hover:#2c2e33;--gp-grid-filter-bg:#25262b;--gp-grid-input-bg:#1a1b1e;--gp-grid-input-border:#373a40;--gp-grid-error-bg:#2c1a1a;--gp-grid-error-text:#ff6b6b;--gp-grid-loading-bg:#1a1b1ef2;--gp-grid-loading-text:#c1c2c5;--gp-grid-scrollbar-track:#25262b;--gp-grid-scrollbar-thumb:#373a40;--gp-grid-scrollbar-thumb-hover:#4a4d52}.gp-grid-container{-webkit-user-select:none;user-select:none;outline:none}:where(.gp-grid-container){color:var(--gp-grid-text);background-color:var(--gp-grid-bg);border:1px solid var(--gp-grid-border);border-radius:6px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:13px;line-height:1.5}.gp-grid-container:focus{outline:none}:where(.gp-grid-container:focus){border-color:var(--gp-grid-primary)}.gp-grid-header{z-index:11;flex-shrink:0;position:sticky;top:0;left:0;overflow:hidden}:where(.gp-grid-header){background-color:var(--gp-grid-header-bg);border-bottom:1px solid var(--gp-grid-border)}.gp-grid-container .gp-grid-header-cell{box-sizing:border-box;user-select:none;touch-action:none;display:flex;position:absolute;top:0}:where(.gp-grid-header-cell){border-right:1px solid var(--gp-grid-border);letter-spacing:.5px;color:var(--gp-grid-header-text);cursor:pointer;background-color:#0000;align-items:center;padding:0 12px;font-size:12px;font-weight:600;transition:background-color .1s}:where(.gp-grid-header-cell:hover){background-color:var(--gp-grid-hover)}:where(.gp-grid-header-cell:active){background-color:var(--gp-grid-border-light)}.gp-grid-container .gp-grid-header-text{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}:where(.gp-grid-header-text){color:var(--gp-grid-header-text)}.gp-grid-header-icons{align-items:center;gap:4px;margin-left:auto;display:flex}.gp-grid-sort-arrows{flex-direction:row;align-items:center;gap:2px;margin-left:6px;display:flex}.gp-grid-sort-arrows-stack{flex-direction:column;align-items:center;gap:2px;display:flex}:where(.gp-grid-sort-arrow-up),:where(.gp-grid-sort-arrow-down){opacity:.35;color:var(--gp-grid-text);transition:opacity .15s,color .15s}:where(.gp-grid-sort-arrow-up.active),:where(.gp-grid-sort-arrow-down.active){opacity:1;color:var(--gp-grid-primary)}:where(.gp-grid-sort-index){color:var(--gp-grid-primary);font-size:9px;font-weight:600}.gp-grid-filter-icon{justify-content:center;align-items:center;width:20px;height:20px;display:flex}:where(.gp-grid-filter-icon){cursor:pointer;color:var(--gp-grid-text-secondary);border-radius:4px;margin-left:2px;transition:background-color .15s,color .15s}:where(.gp-grid-filter-icon:hover){background-color:var(--gp-grid-hover);color:var(--gp-grid-primary)}:where(.gp-grid-filter-icon.active){color:var(--gp-grid-primary);background-color:var(--gp-grid-primary-light)}@media (pointer:coarse) and (hover:none) and (width<=768px){.gp-grid-filter-icon{width:44px;min-width:44px;height:44px;min-height:44px}:where(.gp-grid-filter-icon){margin-left:0}.gp-grid-filter-icon svg{width:20px;height:20px}}.gp-grid-header--loading .gp-grid-header-cell{pointer-events:none}:where(.gp-grid-header--loading .gp-grid-header-cell){opacity:.6;cursor:default}.gp-grid-header-resize-handle{cursor:col-resize;z-index:10;touch-action:none;width:6px;height:100%;position:absolute;top:0;right:-3px}:where(.gp-grid-header-resize-handle:hover),:where(.gp-grid-header-resize-handle--active){background-color:var(--gp-grid-primary);opacity:.4}@media (pointer:coarse) and (hover:none) and (width<=768px){.gp-grid-header-resize-handle{width:24px;right:-12px}}.gp-grid-column-resize-line{z-index:1000;pointer-events:none;width:2px;height:100%;position:absolute;top:0}:where(.gp-grid-column-resize-line){background-color:var(--gp-grid-primary)}.gp-grid-column-move-ghost{pointer-events:none;z-index:2000;box-sizing:border-box;white-space:nowrap;text-overflow:ellipsis;align-items:center;display:flex;position:fixed;overflow:hidden}:where(.gp-grid-column-move-ghost){opacity:.7;background-color:var(--gp-grid-header-bg);border:2px solid var(--gp-grid-primary);letter-spacing:.5px;color:var(--gp-grid-header-text);border-radius:4px;padding:0 12px;font-size:12px;font-weight:600;box-shadow:0 4px 12px #00000026}.gp-grid-column-drop-indicator{z-index:1000;pointer-events:none;width:3px;height:100%;position:absolute;top:0;left:0}:where(.gp-grid-column-drop-indicator){background-color:var(--gp-grid-primary);border-radius:2px}.gp-grid-rows-wrapper{z-index:1;will-change:transform;position:absolute;top:0;left:0}.gp-grid-row{position:absolute;top:0;left:0}:where(.gp-grid-row){background-color:var(--gp-grid-bg)}.gp-grid-cell{box-sizing:border-box;white-space:nowrap;-webkit-user-select:none;user-select:none;display:flex;position:absolute;top:0;overflow:hidden}.gp-grid-cell-content{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}.gp-grid-cell--wrap{align-items:flex-start}.gp-grid-cell--wrap .gp-grid-cell-content{white-space:normal;overflow-wrap:anywhere;text-overflow:clip}:where(.gp-grid-cell){cursor:cell;color:var(--gp-grid-text);border-right:1px solid var(--gp-grid-border-light);border-bottom:1px solid var(--gp-grid-border-light);background-color:#0000;align-items:center;padding:0 12px}.gp-grid-cell--active{z-index:5;outline:none}:where(.gp-grid-cell--active){background-color:var(--gp-grid-primary-light);border:2px solid var(--gp-grid-primary);padding:0 11px}:where(.gp-grid-cell--selected){background-color:var(--gp-grid-primary-light)}.gp-grid-cell--editing{z-index:10}:where(.gp-grid-cell--editing){background-color:var(--gp-grid-bg);border:2px solid var(--gp-grid-primary);padding:0}.gp-grid-fill-handle{z-index:11;pointer-events:auto;box-sizing:border-box;touch-action:none;width:8px;height:8px;position:absolute}:where(.gp-grid-fill-handle){background-color:var(--gp-grid-primary);border:2px solid var(--gp-grid-bg);cursor:crosshair;border-radius:1px}:where(.gp-grid-fill-handle:hover){transform:scale(1.2)}@media (pointer:coarse) and (hover:none) and (width<=768px){.gp-grid-fill-handle{width:24px;height:24px}:where(.gp-grid-fill-handle){border-width:3px;border-radius:3px}}:where(.gp-grid-cell.gp-grid-cell--fill-preview){background-color:var(--gp-grid-primary-light);border:1px dashed var(--gp-grid-primary)}.gp-grid-edit-input{width:100%;height:100%}:where(.gp-grid-edit-input){font-family:inherit;font-size:inherit;color:var(--gp-grid-text);background-color:#0000;border:none;padding:0 11px}.gp-grid-edit-input:focus{outline:none}.gp-grid-cell-peek{z-index:20;box-sizing:border-box;max-height:320px;position:absolute;overflow:auto}:where(.gp-grid-cell-peek){background-color:var(--gp-grid-bg);color:var(--gp-grid-text);border:2px solid var(--gp-grid-primary);font-family:inherit;font-size:inherit;white-space:pre-wrap;overflow-wrap:anywhere;cursor:text;-webkit-user-select:text;user-select:text;border-radius:3px;padding:8px 11px;line-height:1.4;box-shadow:0 4px 12px #00000026}.gp-grid-cell-peek .gp-grid-cell-content{white-space:inherit;text-overflow:clip;overflow:visible}.gp-grid-loading-anchor{z-index:900;pointer-events:none;height:0;position:sticky;top:0;left:0;overflow:visible}.gp-grid-loading-overlay{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0}:where(.gp-grid-loading-overlay){background-color:var(--gp-grid-loading-overlay-bg,#fff6)}:where(.gp-grid-container--dark .gp-grid-loading-overlay){background-color:var(--gp-grid-loading-overlay-bg,#0000004d)}.gp-grid-loading{z-index:1000;pointer-events:auto;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}:where(.gp-grid-loading){background-color:var(--gp-grid-loading-bg);color:var(--gp-grid-loading-text);border:1px solid var(--gp-grid-border);border-radius:6px;align-items:center;gap:10px;padding:12px 20px;font-size:13px;font-weight:500}.gp-grid-loading-spinner{width:16px;height:16px;animation:.7s linear infinite gp-grid-spin}:where(.gp-grid-loading-spinner){border:2px solid var(--gp-grid-border);border-top-color:var(--gp-grid-primary);border-radius:50%}@keyframes gp-grid-spin{to{transform:rotate(360deg)}}.gp-grid-error{z-index:1000;-webkit-user-select:text;user-select:text;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}:where(.gp-grid-error){background-color:var(--gp-grid-error-bg);color:var(--gp-grid-error-text);border:1px solid var(--gp-grid-error-text);text-align:center;cursor:text;border-radius:6px;max-width:80%;padding:12px 20px;font-size:13px;font-weight:500}.gp-grid-empty{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}:where(.gp-grid-empty){color:var(--gp-grid-text-muted);text-align:center;font-size:14px}.gp-grid-container::-webkit-scrollbar{width:8px;height:8px}:where(.gp-grid-container)::-webkit-scrollbar-track{background-color:var(--gp-grid-scrollbar-track)}:where(.gp-grid-container)::-webkit-scrollbar-thumb{background-color:var(--gp-grid-scrollbar-thumb);border-radius:4px}:where(.gp-grid-container)::-webkit-scrollbar-thumb:hover{background-color:var(--gp-grid-scrollbar-thumb-hover)}:where(.gp-grid-container)::-webkit-scrollbar-corner{background-color:var(--gp-grid-scrollbar-track)}.gp-grid-filter-popup{flex-direction:column;max-width:min(360px,100vw - 16px);max-height:400px;display:flex}:where(.gp-grid-filter-popup){background-color:var(--gp-grid-bg);border:1px solid var(--gp-grid-border);border-radius:6px;font-size:13px;box-shadow:0 4px 12px #00000026}:where(.gp-grid-filter-header){border-bottom:1px solid var(--gp-grid-border-light);color:var(--gp-grid-header-text);padding:10px 12px;font-weight:600}.gp-grid-filter-content{flex-direction:column;min-height:0;display:flex;overflow-y:auto}:where(.gp-grid-filter-content){gap:10px;padding:10px 12px}.gp-grid-filter-mode-toggle{display:flex}:where(.gp-grid-filter-mode-toggle){gap:4px}.gp-grid-filter-mode-toggle button{flex:1}:where(.gp-grid-filter-mode-toggle button){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-bg);color:var(--gp-grid-text-secondary);cursor:pointer;border-radius:3px;padding:6px 10px;font-family:inherit;font-size:11px;transition:background-color .15s,color .15s}:where(.gp-grid-filter-mode-toggle button:hover){color:var(--gp-grid-text)}.gp-grid-filter-mode-toggle button.active{background-color:var(--gp-grid-primary);border-color:var(--gp-grid-primary);color:#fff}:where(.gp-grid-filter-info){color:var(--gp-grid-text-secondary);background-color:var(--gp-grid-bg-alt);border-radius:4px;padding:6px 8px;font-size:11px}.gp-grid-filter-search{box-sizing:border-box;width:100%;height:30px}:where(.gp-grid-filter-search){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-input-bg);color:var(--gp-grid-text);border-radius:4px;padding:0 10px;font-family:inherit;font-size:12px}:where(.gp-grid-filter-search:focus){border-color:var(--gp-grid-primary);outline:none}.gp-grid-filter-actions{display:flex}:where(.gp-grid-filter-actions){gap:8px}.gp-grid-filter-actions button{flex:1}:where(.gp-grid-filter-actions button){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-bg);color:var(--gp-grid-text);cursor:pointer;border-radius:3px;padding:4px 8px;font-family:inherit;font-size:11px}:where(.gp-grid-filter-actions button:hover:not(:disabled)){background-color:var(--gp-grid-hover)}:where(.gp-grid-filter-actions button:disabled){opacity:.5;cursor:not-allowed}.gp-grid-filter-list{flex-direction:column;max-height:200px;display:flex;overflow-y:auto}:where(.gp-grid-filter-list){border:1px solid var(--gp-grid-border-light);border-radius:4px;gap:4px;padding:6px}.gp-grid-filter-option{align-items:center;display:flex}:where(.gp-grid-filter-option){cursor:pointer;border-radius:3px;gap:8px;padding:4px 6px}:where(.gp-grid-filter-option:hover){background-color:var(--gp-grid-hover)}.gp-grid-filter-option input[type=checkbox]{margin:0}:where(.gp-grid-filter-option input[type=checkbox]){cursor:pointer}.gp-grid-filter-option span{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}:where(.gp-grid-filter-blank){color:var(--gp-grid-text-muted);font-style:italic}.gp-grid-filter-groups,.gp-grid-filter-group{flex-direction:column;display:flex}:where(.gp-grid-filter-groups){gap:8px}:where(.gp-grid-filter-group){border:1px solid var(--gp-grid-border-light);background-color:var(--gp-grid-bg-alt);border-radius:5px;gap:6px;padding:8px}.gp-grid-filter-group-actions{justify-content:flex-end;align-items:center;display:flex}:where(.gp-grid-filter-group-actions){gap:6px}:where(.gp-grid-filter-group-actions .gp-grid-filter-combination){flex:1;margin-bottom:0}:where(.gp-grid-filter-group-remove){margin-bottom:-2px}:where(.gp-grid-filter-add-group){margin-top:2px}.gp-grid-filter-condition{flex-direction:column;display:flex}:where(.gp-grid-filter-condition){gap:6px}.gp-grid-filter-row{align-items:center;display:flex}:where(.gp-grid-filter-row){gap:6px}.gp-grid-filter-row select{height:30px}:where(.gp-grid-filter-row select){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-input-bg);color:var(--gp-grid-text);cursor:pointer;border-radius:4px;padding:0 6px;font-family:inherit;font-size:12px}.gp-grid-filter-row input[type=number],.gp-grid-filter-row input[type=date],.gp-grid-filter-row input[type=text],.gp-grid-filter-text-input{box-sizing:border-box;flex:1;min-width:0;height:30px}:where(.gp-grid-filter-row input[type=number]),:where(.gp-grid-filter-row input[type=date]),:where(.gp-grid-filter-row input[type=text]),:where(.gp-grid-filter-text-input){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-input-bg);color:var(--gp-grid-text);border-radius:4px;padding:0 8px;font-family:inherit;font-size:12px}:where(.gp-grid-filter-row input:focus){border-color:var(--gp-grid-primary);outline:none}:where(.gp-grid-filter-to){color:var(--gp-grid-text-secondary);font-size:11px}.gp-grid-filter-remove{flex:none;justify-content:center;align-items:center;width:24px;height:24px;display:inline-flex}:where(.gp-grid-filter-remove){color:var(--gp-grid-text-muted);cursor:pointer;background-color:#0000;border:none;border-radius:3px;padding:0;font-size:16px;line-height:1}:where(.gp-grid-filter-remove:hover){background-color:var(--gp-grid-error-bg);color:var(--gp-grid-error-text)}.gp-grid-filter-combination{display:flex}:where(.gp-grid-filter-combination){gap:4px;margin-bottom:4px}.gp-grid-filter-combination button{flex:1}:where(.gp-grid-filter-combination button){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-bg);color:var(--gp-grid-text-secondary);cursor:pointer;border-radius:3px;padding:4px 8px;font-family:inherit;font-size:10px}.gp-grid-filter-combination button.active{background-color:var(--gp-grid-primary);border-color:var(--gp-grid-primary);color:#fff}.gp-grid-filter-content button:focus-visible,.gp-grid-filter-content select:focus-visible,.gp-grid-filter-content input:focus-visible{outline:2px solid var(--gp-grid-primary);outline-offset:2px}.gp-grid-filter-mode-toggle button:focus-visible{outline-offset:-2px}.gp-grid-filter-add{width:100%}:where(.gp-grid-filter-add){border:1px dashed var(--gp-grid-border);color:var(--gp-grid-text-secondary);cursor:pointer;background-color:#0000;border-radius:4px;padding:6px;font-family:inherit;font-size:11px}:where(.gp-grid-filter-add:hover){background-color:var(--gp-grid-hover);border-color:var(--gp-grid-primary);color:var(--gp-grid-primary)}.gp-grid-filter-buttons{display:flex}:where(.gp-grid-filter-buttons){border-top:1px solid var(--gp-grid-border-light);gap:8px;padding-top:8px}.gp-grid-filter-btn-clear,.gp-grid-filter-btn-apply{flex:1}:where(.gp-grid-filter-btn-clear),:where(.gp-grid-filter-btn-apply){cursor:pointer;border-radius:4px;padding:8px 12px;font-family:inherit;font-size:12px}:where(.gp-grid-filter-btn-clear){border:1px solid var(--gp-grid-input-border);background-color:var(--gp-grid-bg);color:var(--gp-grid-text)}:where(.gp-grid-filter-btn-clear:hover){background-color:var(--gp-grid-hover)}:where(.gp-grid-filter-btn-apply){border:1px solid var(--gp-grid-primary);background-color:var(--gp-grid-primary);color:#fff}:where(.gp-grid-filter-btn-apply:hover){opacity:.9}.gp-grid-cell--row-drag-handle{touch-action:none}:where(.gp-grid-cell--row-drag-handle){cursor:grab}:where(.gp-grid-cell--row-drag-handle:active){cursor:grabbing}.gp-grid-row-drag-icon{justify-content:center;align-items:center;width:100%;height:100%;display:flex}:where(.gp-grid-row-drag-icon){color:var(--gp-grid-text-secondary)}.gp-grid-row-drag-icon svg{width:16px;height:16px}.gp-grid-row-drag-ghost{pointer-events:none;z-index:2000;box-sizing:border-box;position:fixed;overflow:hidden}:where(.gp-grid-row-drag-ghost){opacity:.8;background-color:var(--gp-grid-bg,#fff);border:2px solid var(--gp-grid-primary);border-radius:4px;box-shadow:0 4px 12px #00000026}.gp-grid-row-drop-indicator{z-index:1000;pointer-events:none;width:100%;height:3px;position:absolute;left:0}:where(.gp-grid-row-drop-indicator){background-color:var(--gp-grid-primary);border-radius:2px}
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { input, output, computed, TemplateRef, ChangeDetectionStrategy, Component, ViewChild, signal, effect, HostListener, inject, PLATFORM_ID, InjectionToken, Injectable } from '@angular/core';
3
3
  import { NgTemplateOutlet, isPlatformBrowser } from '@angular/common';
4
4
  import { defaultGridLabels, getFieldValue, isCellSelected, isCellActive, formatCellValue, isCellInFillPreview, isCellEditing, buildCellClasses, labelsForSelectedValues, rawValuesForLabels, getTextOperatorOptions, getNumberOperatorOptions, formatLabel, groupDistinctValues, isBlankCellValue, calculateFilterPopupPosition, bindPeekSelectAll, calculateScaledColumnPositions, getTotalWidth, calculateFillHandlePosition, DataSourceOwner, AutoScrollDriver, PendingRowDragController, PendingCellTapController, TouchScrollController, InputEventAdapter, applyBatchInstructions, scrollCellIntoView, GridCore, resolveGridLabels, createMutableClientDataSource } from '@gp-grid/core';
5
- export { GridCore, createClientDataSource, createDataSourceFromArray, createMutableClientDataSource, createServerDataSource } from '@gp-grid/core';
5
+ export { GridCore, createClientDataSource, createDataSourceFromArray, createMutableClientDataSource, createServerDataSource, isLegacyColumnFilterModel, normalizeColumnFilterModel } from '@gp-grid/core';
6
6
 
7
7
  const TEMPLATE$2 = `
8
8
  <div
@@ -463,158 +463,258 @@ const FILTER_POPUP_TEMPLATE = `
463
463
 
464
464
  <div [class]="'gp-grid-filter-content ' + (isNumberColumn() ? 'gp-grid-filter-number' : 'gp-grid-filter-text')">
465
465
  @if (isNumberColumn()) {
466
- @for (cond of numberConditions; track $index; let i = $index) {
467
- <div class="gp-grid-filter-condition">
468
- @if (i > 0) {
469
- <div class="gp-grid-filter-combination">
470
- <button
471
- type="button"
472
- [class.active]="numberConditions[i - 1]?.nextOperator === 'and'"
473
- (click)="setNumberNextOp(i - 1, 'and')">
474
- {{ labels().and }}
475
- </button>
476
- <button
477
- type="button"
478
- [class.active]="numberConditions[i - 1]?.nextOperator === 'or'"
479
- (click)="setNumberNextOp(i - 1, 'or')">
480
- {{ labels().or }}
481
- </button>
482
- </div>
483
- }
484
- <div class="gp-grid-filter-row">
485
- <select
486
- [value]="cond.operator"
487
- (change)="onNumberOperatorChange(i, $any($event.target).value)">
488
- @for (op of numberOperators(); track op.value) {
489
- <option [value]="op.value">{{ op.label }}</option>
490
- }
491
- </select>
492
- @if (!isValueLessNumberOp(cond.operator)) {
493
- <input
494
- type="number"
495
- [value]="cond.value"
496
- (input)="cond.value = $any($event.target).value"
497
- [placeholder]="labels().valuePlaceholder" />
498
- @if (cond.operator === 'between') {
499
- <span class="gp-grid-filter-to">{{ labels().betweenSeparator }}</span>
500
- <input
501
- type="number"
502
- [value]="cond.valueTo"
503
- (input)="cond.valueTo = $any($event.target).value"
504
- [placeholder]="labels().valuePlaceholder" />
505
- }
506
- }
507
- @if (numberConditions.length > 1) {
508
- <button
509
- type="button"
510
- class="gp-grid-filter-remove"
511
- (click)="removeNumberCondition(i)">{{ labels().removeCondition }}</button>
512
- }
513
- </div>
514
- </div>
515
- }
516
- <button type="button" class="gp-grid-filter-add" (click)="addNumberCondition()">
517
- {{ labels().addCondition }}
518
- </button>
519
- } @else {
520
- @if (showValuesMode()) {
521
- <div class="gp-grid-filter-mode-toggle">
466
+ <div class="gp-grid-filter-groups">
467
+ @if (numberGroups.length > 1) {
468
+ <div class="gp-grid-filter-combination">
522
469
  <button
523
470
  type="button"
524
- [class.active]="filterMode === 'values'"
525
- (click)="filterMode = 'values'">
526
- {{ labels().valuesMode }}
471
+ [class.active]="groupCombination === 'and'"
472
+ [attr.aria-pressed]="groupCombination === 'and'"
473
+ (click)="setGroupCombination('and')">
474
+ {{ labels().and }}
527
475
  </button>
528
476
  <button
529
477
  type="button"
530
- [class.active]="filterMode === 'condition'"
531
- (click)="filterMode = 'condition'">
532
- {{ labels().conditionMode }}
478
+ [class.active]="groupCombination === 'or'"
479
+ [attr.aria-pressed]="groupCombination === 'or'"
480
+ (click)="setGroupCombination('or')">
481
+ {{ labels().or }}
533
482
  </button>
534
483
  </div>
535
484
  }
536
-
537
- @if (filterMode === 'values' && showValuesMode()) {
538
- <input
539
- class="gp-grid-filter-search"
540
- type="text"
541
- [value]="searchText"
542
- (input)="searchText = $any($event.target).value"
543
- [placeholder]="labels().searchPlaceholder" />
544
- <div class="gp-grid-filter-actions">
545
- <button type="button" (click)="selectAll()">{{ labels().selectAll }}</button>
546
- <button type="button" (click)="deselectAll()">{{ labels().deselectAll }}</button>
547
- </div>
548
- <div class="gp-grid-filter-list">
549
- @if (hasBlanks()) {
550
- <label class="gp-grid-filter-option">
551
- <input
552
- type="checkbox"
553
- [checked]="includeBlanks"
554
- (change)="includeBlanks = $any($event.target).checked" />
555
- <span class="gp-grid-filter-blank">{{ labels().blanks }}</span>
556
- </label>
485
+ @for (group of numberGroups; track $index; let groupIndex = $index) {
486
+ <div class="gp-grid-filter-group">
487
+ @if (group.conditions.length > 1 || numberGroups.length > 1) {
488
+ <div class="gp-grid-filter-group-actions">
489
+ @if (group.conditions.length > 1) {
490
+ <div class="gp-grid-filter-combination">
491
+ <button
492
+ type="button"
493
+ [class.active]="group.combination === 'and'"
494
+ [attr.aria-pressed]="group.combination === 'and'"
495
+ (click)="setNumberGroupCombination(groupIndex, 'and')">
496
+ {{ labels().and }}
497
+ </button>
498
+ <button
499
+ type="button"
500
+ [class.active]="group.combination === 'or'"
501
+ [attr.aria-pressed]="group.combination === 'or'"
502
+ (click)="setNumberGroupCombination(groupIndex, 'or')">
503
+ {{ labels().or }}
504
+ </button>
505
+ </div>
506
+ }
507
+ @if (numberGroups.length > 1) {
508
+ <button
509
+ type="button"
510
+ class="gp-grid-filter-remove gp-grid-filter-group-remove"
511
+ (click)="removeNumberGroup(groupIndex)">
512
+ {{ labels().removeGroup }}
513
+ </button>
514
+ }
515
+ </div>
557
516
  }
558
- @for (entry of filteredUniqueEntries(); track entry.label) {
559
- <label class="gp-grid-filter-option">
560
- <input
561
- type="checkbox"
562
- [checked]="selectedLabels.has(entry.label)"
563
- (change)="toggleValue(entry.label, $any($event.target).checked)" />
564
- <span>{{ entry.label }}</span>
565
- </label>
517
+ @for (cond of group.conditions; track $index; let i = $index) {
518
+ <div class="gp-grid-filter-condition">
519
+ <div class="gp-grid-filter-row">
520
+ <select
521
+ [value]="cond.operator"
522
+ (change)="onNumberOperatorChange(groupIndex, i, $any($event.target).value)">
523
+ @for (op of numberOperators(); track op.value) {
524
+ <option [value]="op.value">{{ op.label }}</option>
525
+ }
526
+ </select>
527
+ @if (!isValueLessNumberOp(cond.operator)) {
528
+ <input
529
+ type="number"
530
+ [value]="cond.value"
531
+ (input)="cond.value = $any($event.target).value"
532
+ [placeholder]="labels().valuePlaceholder" />
533
+ @if (cond.operator === 'between') {
534
+ <span class="gp-grid-filter-to">{{ labels().betweenSeparator }}</span>
535
+ <input
536
+ type="number"
537
+ [value]="cond.valueTo"
538
+ (input)="cond.valueTo = $any($event.target).value"
539
+ [placeholder]="labels().valuePlaceholder" />
540
+ }
541
+ }
542
+ @if (group.conditions.length > 1) {
543
+ <button
544
+ type="button"
545
+ class="gp-grid-filter-remove"
546
+ (click)="removeNumberCondition(groupIndex, i)">
547
+ {{ labels().removeCondition }}
548
+ </button>
549
+ }
550
+ </div>
551
+ </div>
566
552
  }
553
+ <button
554
+ type="button"
555
+ class="gp-grid-filter-add"
556
+ (click)="addNumberCondition(groupIndex)">
557
+ {{ labels().addCondition }}
558
+ </button>
567
559
  </div>
568
560
  }
561
+ <button
562
+ type="button"
563
+ class="gp-grid-filter-add gp-grid-filter-add-group"
564
+ (click)="addNumberGroup()">
565
+ {{ labels().addGroup }}
566
+ </button>
567
+ </div>
568
+ } @else {
569
+ @if (showValuesMode()) {
570
+ <div class="gp-grid-filter-mode-toggle">
571
+ <button
572
+ type="button"
573
+ [class.active]="filterMode === 'values'"
574
+ [attr.aria-pressed]="filterMode === 'values'"
575
+ (click)="filterMode = 'values'">
576
+ {{ labels().valuesMode }}
577
+ </button>
578
+ <button
579
+ type="button"
580
+ [class.active]="filterMode === 'condition'"
581
+ [attr.aria-pressed]="filterMode === 'condition'"
582
+ (click)="filterMode = 'condition'">
583
+ {{ labels().conditionMode }}
584
+ </button>
585
+ </div>
586
+ }
569
587
 
570
- @if (filterMode === 'condition') {
571
- @for (cond of textConditions; track $index; let i = $index) {
572
- <div class="gp-grid-filter-condition">
573
- @if (i > 0) {
574
- <div class="gp-grid-filter-combination">
575
- <button
576
- type="button"
577
- [class.active]="textConditions[i - 1]?.nextOperator === 'and'"
578
- (click)="setTextNextOp(i - 1, 'and')">
579
- {{ labels().and }}
580
- </button>
581
- <button
582
- type="button"
583
- [class.active]="textConditions[i - 1]?.nextOperator === 'or'"
584
- (click)="setTextNextOp(i - 1, 'or')">
585
- {{ labels().or }}
586
- </button>
588
+ @if (filterMode === 'values' && showValuesMode()) {
589
+ <input
590
+ class="gp-grid-filter-search"
591
+ type="text"
592
+ [value]="searchText"
593
+ (input)="searchText = $any($event.target).value"
594
+ [placeholder]="labels().searchPlaceholder" />
595
+ <div class="gp-grid-filter-actions">
596
+ <button type="button" (click)="selectAll()">{{ labels().selectAll }}</button>
597
+ <button type="button" (click)="deselectAll()">{{ labels().deselectAll }}</button>
598
+ </div>
599
+ <div class="gp-grid-filter-list">
600
+ @if (hasBlanks()) {
601
+ <label class="gp-grid-filter-option">
602
+ <input
603
+ type="checkbox"
604
+ [checked]="includeBlanks"
605
+ (change)="includeBlanks = $any($event.target).checked" />
606
+ <span class="gp-grid-filter-blank">{{ labels().blanks }}</span>
607
+ </label>
608
+ }
609
+ @for (entry of filteredUniqueEntries(); track entry.label) {
610
+ <label class="gp-grid-filter-option">
611
+ <input
612
+ type="checkbox"
613
+ [checked]="selectedLabels.has(entry.label)"
614
+ (change)="toggleValue(entry.label, $any($event.target).checked)" />
615
+ <span>{{ entry.label }}</span>
616
+ </label>
617
+ }
618
+ </div>
619
+ }
620
+
621
+ @if (filterMode === 'condition') {
622
+ <div class="gp-grid-filter-groups">
623
+ @if (textGroups.length > 1) {
624
+ <div class="gp-grid-filter-combination">
625
+ <button
626
+ type="button"
627
+ [class.active]="groupCombination === 'and'"
628
+ [attr.aria-pressed]="groupCombination === 'and'"
629
+ (click)="setGroupCombination('and')">
630
+ {{ labels().and }}
631
+ </button>
632
+ <button
633
+ type="button"
634
+ [class.active]="groupCombination === 'or'"
635
+ [attr.aria-pressed]="groupCombination === 'or'"
636
+ (click)="setGroupCombination('or')">
637
+ {{ labels().or }}
638
+ </button>
639
+ </div>
640
+ }
641
+ @for (group of textGroups; track $index; let groupIndex = $index) {
642
+ <div class="gp-grid-filter-group">
643
+ @if (group.conditions.length > 1 || textGroups.length > 1) {
644
+ <div class="gp-grid-filter-group-actions">
645
+ @if (group.conditions.length > 1) {
646
+ <div class="gp-grid-filter-combination">
647
+ <button
648
+ type="button"
649
+ [class.active]="group.combination === 'and'"
650
+ [attr.aria-pressed]="group.combination === 'and'"
651
+ (click)="setTextGroupCombination(groupIndex, 'and')">
652
+ {{ labels().and }}
653
+ </button>
654
+ <button
655
+ type="button"
656
+ [class.active]="group.combination === 'or'"
657
+ [attr.aria-pressed]="group.combination === 'or'"
658
+ (click)="setTextGroupCombination(groupIndex, 'or')">
659
+ {{ labels().or }}
660
+ </button>
661
+ </div>
662
+ }
663
+ @if (textGroups.length > 1) {
664
+ <button
665
+ type="button"
666
+ class="gp-grid-filter-remove gp-grid-filter-group-remove"
667
+ (click)="removeTextGroup(groupIndex)">
668
+ {{ labels().removeGroup }}
669
+ </button>
670
+ }
587
671
  </div>
588
672
  }
589
- <div class="gp-grid-filter-row">
590
- <select
591
- [value]="cond.operator"
592
- (change)="onTextOperatorChange(i, $any($event.target).value)">
593
- @for (op of textOperators(); track op.value) {
594
- <option [value]="op.value">{{ op.label }}</option>
595
- }
596
- </select>
597
- @if (!isValueLessTextOp(cond.operator)) {
598
- <input
599
- class="gp-grid-filter-text-input"
600
- type="text"
601
- [value]="cond.value"
602
- (input)="cond.value = $any($event.target).value"
603
- [placeholder]="labels().valuePlaceholder" />
604
- }
605
- @if (textConditions.length > 1) {
606
- <button
607
- type="button"
608
- class="gp-grid-filter-remove"
609
- (click)="removeTextCondition(i)">{{ labels().removeCondition }}</button>
610
- }
611
- </div>
673
+ @for (cond of group.conditions; track $index; let i = $index) {
674
+ <div class="gp-grid-filter-condition">
675
+ <div class="gp-grid-filter-row">
676
+ <select
677
+ [value]="cond.operator"
678
+ (change)="onTextOperatorChange(groupIndex, i, $any($event.target).value)">
679
+ @for (op of textOperators(); track op.value) {
680
+ <option [value]="op.value">{{ op.label }}</option>
681
+ }
682
+ </select>
683
+ @if (!isValueLessTextOp(cond.operator)) {
684
+ <input
685
+ class="gp-grid-filter-text-input"
686
+ type="text"
687
+ [value]="cond.value"
688
+ (input)="cond.value = $any($event.target).value"
689
+ [placeholder]="labels().valuePlaceholder" />
690
+ }
691
+ @if (group.conditions.length > 1) {
692
+ <button
693
+ type="button"
694
+ class="gp-grid-filter-remove"
695
+ (click)="removeTextCondition(groupIndex, i)">
696
+ {{ labels().removeCondition }}
697
+ </button>
698
+ }
699
+ </div>
700
+ </div>
701
+ }
702
+ <button
703
+ type="button"
704
+ class="gp-grid-filter-add"
705
+ (click)="addTextCondition(groupIndex)">
706
+ {{ labels().addCondition }}
707
+ </button>
612
708
  </div>
613
709
  }
614
- <button type="button" class="gp-grid-filter-add" (click)="addTextCondition()">
615
- {{ labels().addCondition }}
710
+ <button
711
+ type="button"
712
+ class="gp-grid-filter-add gp-grid-filter-add-group"
713
+ (click)="addTextGroup()">
714
+ {{ labels().addGroup }}
616
715
  </button>
617
- }
716
+ </div>
717
+ }
618
718
  }
619
719
 
620
720
  <div class="gp-grid-filter-buttons">
@@ -637,131 +737,151 @@ const isValueLessNumberOp = (operator) => VALUE_LESS_NUMBER_OPERATORS.includes(o
637
737
  const defaultTextCondition = () => ({
638
738
  operator: 'contains',
639
739
  value: '',
640
- nextOperator: 'and',
641
740
  });
642
741
  const defaultNumberCondition = () => ({
643
742
  operator: '=',
644
743
  value: '',
645
744
  valueTo: '',
646
- nextOperator: 'and',
745
+ });
746
+ const defaultTextGroup = () => ({
747
+ conditions: [defaultTextCondition()],
748
+ combination: 'and',
749
+ });
750
+ const defaultNumberGroup = () => ({
751
+ conditions: [defaultNumberCondition()],
752
+ combination: 'and',
753
+ });
754
+ const defaultTextState = (entries) => ({
755
+ filterMode: 'values',
756
+ selectedLabels: new Set(entries.map(entry => entry.label)),
757
+ includeBlanks: true,
758
+ textGroups: [defaultTextGroup()],
759
+ combination: 'and',
647
760
  });
648
761
  const initTextState = (filter, entries) => {
649
762
  if (!filter)
650
763
  return defaultTextState(entries);
651
- const textConds = filter.conditions.filter((c) => c.type === 'text');
652
- if (textConds.length === 0)
653
- return defaultTextState(entries);
654
- const firstCond = textConds[0];
655
- if (firstCond?.selectedValues !== undefined) {
764
+ const firstCondition = filter.groups[0]?.conditions[0];
765
+ if (firstCondition?.type === 'text' && firstCondition.selectedValues !== undefined) {
656
766
  return {
657
767
  filterMode: 'values',
658
- selectedLabels: labelsForSelectedValues(entries, firstCond.selectedValues),
659
- includeBlanks: firstCond.includeBlank ?? true,
660
- textConditions: [defaultTextCondition()],
768
+ selectedLabels: labelsForSelectedValues(entries, firstCondition.selectedValues),
769
+ includeBlanks: firstCondition.includeBlank ?? true,
770
+ textGroups: [defaultTextGroup()],
771
+ combination: 'and',
661
772
  };
662
773
  }
774
+ const textGroups = filter.groups.flatMap((group) => {
775
+ const conditions = group.conditions.flatMap((condition) => {
776
+ if (condition.type !== 'text')
777
+ return [];
778
+ return [{ operator: condition.operator, value: condition.value ?? '' }];
779
+ });
780
+ if (conditions.length === 0)
781
+ return [];
782
+ return [{ conditions, combination: group.combination }];
783
+ });
784
+ if (textGroups.length === 0)
785
+ return defaultTextState(entries);
663
786
  return {
664
787
  filterMode: 'condition',
665
- selectedLabels: new Set(entries.map(e => e.label)),
788
+ selectedLabels: new Set(entries.map(entry => entry.label)),
666
789
  includeBlanks: true,
667
- textConditions: textConds.map((c, i) => ({
668
- operator: c.operator,
669
- value: c.value ?? '',
670
- nextOperator: textConds[i]?.nextOperator ?? 'and',
671
- })),
790
+ textGroups,
791
+ combination: filter.combination,
672
792
  };
673
793
  };
674
- const defaultTextState = (entries) => ({
675
- filterMode: 'values',
676
- selectedLabels: new Set(entries.map(e => e.label)),
677
- includeBlanks: true,
678
- textConditions: [defaultTextCondition()],
679
- });
680
- const initNumberConditions = (filter) => {
681
- if (!filter)
682
- return [defaultNumberCondition()];
683
- const numConds = filter.conditions.filter((c) => c.type === 'number');
684
- if (numConds.length === 0)
685
- return [defaultNumberCondition()];
686
- return numConds.map((c, i) => ({
687
- operator: c.operator,
688
- value: c.value !== undefined ? String(c.value) : '',
689
- valueTo: c.valueTo !== undefined ? String(c.valueTo) : '',
690
- nextOperator: numConds[i]?.nextOperator ?? 'and',
691
- }));
794
+ const initNumberState = (filter) => {
795
+ if (!filter) {
796
+ return { numberGroups: [defaultNumberGroup()], combination: 'and' };
797
+ }
798
+ const numberGroups = filter.groups.flatMap((group) => {
799
+ const conditions = group.conditions.flatMap((condition) => {
800
+ if (condition.type !== 'number')
801
+ return [];
802
+ return [{
803
+ operator: condition.operator,
804
+ value: condition.value !== undefined ? String(condition.value) : '',
805
+ valueTo: condition.valueTo !== undefined ? String(condition.valueTo) : '',
806
+ }];
807
+ });
808
+ if (conditions.length === 0)
809
+ return [];
810
+ return [{ conditions, combination: group.combination }];
811
+ });
812
+ if (numberGroups.length === 0) {
813
+ return { numberGroups: [defaultNumberGroup()], combination: 'and' };
814
+ }
815
+ return { numberGroups, combination: filter.combination };
692
816
  };
693
817
  const buildTextFilter = (input) => {
694
- if (input.filterMode === 'values') {
818
+ if (input.filterMode === 'values')
695
819
  return buildValuesFilter(input);
696
- }
697
- return buildConditionTextFilter(input.textConditions);
820
+ const groups = buildTextGroups(input.textGroups);
821
+ if (groups.length === 0)
822
+ return null;
823
+ return { groups, combination: input.combination };
698
824
  };
699
825
  const buildValuesFilter = (input) => {
700
- const allSelected = input.entries.every(e => input.selectedLabels.has(e.label));
826
+ const allSelected = input.entries.every(entry => input.selectedLabels.has(entry.label));
701
827
  if (allSelected && input.includeBlanks)
702
828
  return null;
703
829
  return {
704
- conditions: [{
705
- type: 'text',
706
- operator: 'equals',
707
- selectedValues: rawValuesForLabels(input.entries, input.selectedLabels),
708
- includeBlank: input.includeBlanks,
830
+ groups: [{
831
+ conditions: [{
832
+ type: 'text',
833
+ operator: 'equals',
834
+ selectedValues: rawValuesForLabels(input.entries, input.selectedLabels),
835
+ includeBlank: input.includeBlanks,
836
+ }],
837
+ combination: 'and',
709
838
  }],
710
- combination: 'or',
839
+ combination: 'and',
711
840
  };
712
841
  };
713
- const buildConditionTextFilter = (textConditions) => {
842
+ const buildTextGroups = (sourceGroups) => sourceGroups.flatMap((group) => {
714
843
  const conditions = [];
715
- for (let i = 0; i < textConditions.length; i++) {
716
- const cond = textConditions[i];
717
- if (!cond)
718
- continue;
719
- if (!isValueLessTextOp(cond.operator) && !cond.value)
844
+ for (const condition of group.conditions) {
845
+ if (!isValueLessTextOp(condition.operator) && !condition.value)
720
846
  continue;
721
- const out = {
847
+ const builtCondition = {
722
848
  type: 'text',
723
- operator: cond.operator,
849
+ operator: condition.operator,
724
850
  };
725
- if (!isValueLessTextOp(cond.operator))
726
- out.value = cond.value;
727
- linkNextOperator(conditions, i, textConditions);
728
- conditions.push(out);
851
+ if (!isValueLessTextOp(condition.operator)) {
852
+ builtCondition.value = condition.value;
853
+ }
854
+ conditions.push(builtCondition);
729
855
  }
730
856
  if (conditions.length === 0)
731
- return null;
732
- return { conditions, combination: textConditions[0]?.nextOperator ?? 'and' };
733
- };
734
- const buildNumberFilter = (numberConditions) => {
735
- const conditions = [];
736
- for (let i = 0; i < numberConditions.length; i++) {
737
- const cond = numberConditions[i];
738
- if (!cond)
739
- continue;
740
- if (!isValueLessNumberOp(cond.operator) && !cond.value)
741
- continue;
742
- const out = {
743
- type: 'number',
744
- operator: cond.operator,
745
- };
746
- if (!isValueLessNumberOp(cond.operator)) {
747
- out.value = parseFloat(cond.value);
748
- if (cond.operator === 'between' && cond.valueTo) {
749
- out.valueTo = parseFloat(cond.valueTo);
857
+ return [];
858
+ return [{ conditions, combination: group.combination }];
859
+ });
860
+ const buildNumberFilter = (numberGroups, combination) => {
861
+ const groups = numberGroups.flatMap((group) => {
862
+ const conditions = [];
863
+ for (const condition of group.conditions) {
864
+ if (!isValueLessNumberOp(condition.operator) && !condition.value)
865
+ continue;
866
+ const builtCondition = {
867
+ type: 'number',
868
+ operator: condition.operator,
869
+ };
870
+ if (!isValueLessNumberOp(condition.operator)) {
871
+ builtCondition.value = Number.parseFloat(condition.value);
872
+ if (condition.operator === 'between' && condition.valueTo) {
873
+ builtCondition.valueTo = Number.parseFloat(condition.valueTo);
874
+ }
750
875
  }
876
+ conditions.push(builtCondition);
751
877
  }
752
- linkNextOperator(conditions, i, numberConditions);
753
- conditions.push(out);
754
- }
755
- if (conditions.length === 0)
878
+ if (conditions.length === 0)
879
+ return [];
880
+ return [{ conditions, combination: group.combination }];
881
+ });
882
+ if (groups.length === 0)
756
883
  return null;
757
- return { conditions, combination: numberConditions[0]?.nextOperator ?? 'and' };
758
- };
759
- const linkNextOperator = (built, currentIndex, source) => {
760
- const prev = built[built.length - 1];
761
- if (prev === undefined || currentIndex === 0)
762
- return;
763
- const prevSource = source[currentIndex - 1];
764
- prev.nextOperator = prevSource?.nextOperator ?? 'and';
884
+ return { groups, combination };
765
885
  };
766
886
  const resolveColId = (column) => column.colId ?? column.field;
767
887
  const isNumberColumn = (column) => column.cellDataType === 'number';
@@ -785,8 +905,9 @@ class FilterPopupComponent {
785
905
  // Ticked display labels; raw values are resolved on apply (buildTextFilter).
786
906
  selectedLabels = new Set();
787
907
  includeBlanks = true;
788
- textConditions = [defaultTextCondition()];
789
- numberConditions = [defaultNumberCondition()];
908
+ textGroups = [defaultTextGroup()];
909
+ numberGroups = [defaultNumberGroup()];
910
+ groupCombination = 'and';
790
911
  textOperators() {
791
912
  return getTextOperatorOptions(this.labels());
792
913
  }
@@ -872,29 +993,52 @@ class FilterPopupComponent {
872
993
  this.includeBlanks = false;
873
994
  this.selectedLabels.clear();
874
995
  }
875
- onTextOperatorChange(index, value) {
876
- setField(this.textConditions, index, 'operator', value);
996
+ onTextOperatorChange(groupIndex, index, value) {
997
+ const group = this.textGroups[groupIndex];
998
+ if (group)
999
+ setField(group.conditions, index, 'operator', value);
1000
+ }
1001
+ onNumberOperatorChange(groupIndex, index, value) {
1002
+ const group = this.numberGroups[groupIndex];
1003
+ if (group)
1004
+ setField(group.conditions, index, 'operator', value);
877
1005
  }
878
- onNumberOperatorChange(index, value) {
879
- setField(this.numberConditions, index, 'operator', value);
1006
+ addTextCondition(groupIndex) {
1007
+ this.textGroups[groupIndex]?.conditions.push(defaultTextCondition());
880
1008
  }
881
- addTextCondition() {
882
- this.textConditions.push(defaultTextCondition());
1009
+ addNumberCondition(groupIndex) {
1010
+ this.numberGroups[groupIndex]?.conditions.push(defaultNumberCondition());
883
1011
  }
884
- addNumberCondition() {
885
- this.numberConditions.push(defaultNumberCondition());
1012
+ removeTextCondition(groupIndex, index) {
1013
+ this.textGroups[groupIndex]?.conditions.splice(index, 1);
886
1014
  }
887
- removeTextCondition(index) {
888
- this.textConditions.splice(index, 1);
1015
+ removeNumberCondition(groupIndex, index) {
1016
+ this.numberGroups[groupIndex]?.conditions.splice(index, 1);
889
1017
  }
890
- removeNumberCondition(index) {
891
- this.numberConditions.splice(index, 1);
1018
+ addTextGroup() {
1019
+ this.textGroups.push(defaultTextGroup());
892
1020
  }
893
- setTextNextOp(index, value) {
894
- setField(this.textConditions, index, 'nextOperator', value);
1021
+ addNumberGroup() {
1022
+ this.numberGroups.push(defaultNumberGroup());
895
1023
  }
896
- setNumberNextOp(index, value) {
897
- setField(this.numberConditions, index, 'nextOperator', value);
1024
+ removeTextGroup(groupIndex) {
1025
+ this.textGroups.splice(groupIndex, 1);
1026
+ }
1027
+ removeNumberGroup(groupIndex) {
1028
+ this.numberGroups.splice(groupIndex, 1);
1029
+ }
1030
+ setTextGroupCombination(groupIndex, value) {
1031
+ const group = this.textGroups[groupIndex];
1032
+ if (group)
1033
+ group.combination = value;
1034
+ }
1035
+ setNumberGroupCombination(groupIndex, value) {
1036
+ const group = this.numberGroups[groupIndex];
1037
+ if (group)
1038
+ group.combination = value;
1039
+ }
1040
+ setGroupCombination(value) {
1041
+ this.groupCombination = value;
898
1042
  }
899
1043
  handleApply() {
900
1044
  this.apply.emit({
@@ -906,27 +1050,32 @@ class FilterPopupComponent {
906
1050
  this.apply.emit({ colId: resolveColId(this.column()), filter: null });
907
1051
  }
908
1052
  buildFilter() {
909
- if (this.isNumberColumn())
910
- return buildNumberFilter(this.numberConditions);
1053
+ if (this.isNumberColumn()) {
1054
+ return buildNumberFilter(this.numberGroups, this.groupCombination);
1055
+ }
911
1056
  return buildTextFilter({
912
1057
  filterMode: this.filterMode,
913
1058
  entries: this.uniqueEntries(),
914
1059
  selectedLabels: this.selectedLabels,
915
1060
  includeBlanks: this.includeBlanks,
916
- textConditions: this.textConditions,
1061
+ textGroups: this.textGroups,
1062
+ combination: this.groupCombination,
917
1063
  });
918
1064
  }
919
1065
  initFromCurrentFilter() {
920
1066
  const filter = this.currentFilter();
921
1067
  if (this.isNumberColumn()) {
922
- this.numberConditions = initNumberConditions(filter);
1068
+ const state = initNumberState(filter);
1069
+ this.numberGroups = state.numberGroups;
1070
+ this.groupCombination = state.combination;
923
1071
  return;
924
1072
  }
925
1073
  const state = initTextState(filter, this.uniqueEntries());
926
1074
  this.filterMode = state.filterMode;
927
1075
  this.selectedLabels = state.selectedLabels;
928
1076
  this.includeBlanks = state.includeBlanks;
929
- this.textConditions = state.textConditions;
1077
+ this.textGroups = state.textGroups;
1078
+ this.groupCombination = state.combination;
930
1079
  }
931
1080
  updatePosition() {
932
1081
  if (!this.popupEl?.nativeElement)
@@ -955,7 +1104,7 @@ class FilterPopupComponent {
955
1104
  });
956
1105
  };
957
1106
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: FilterPopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
958
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: FilterPopupComponent, isStandalone: true, selector: "gp-grid-filter-popup", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, colIndex: { classPropertyName: "colIndex", publicName: "colIndex", isSignal: true, isRequired: true, transformFunction: null }, anchorEl: { classPropertyName: "anchorEl", publicName: "anchorEl", isSignal: true, isRequired: true, transformFunction: null }, distinctValues: { classPropertyName: "distinctValues", publicName: "distinctValues", isSignal: true, isRequired: true, transformFunction: null }, currentFilter: { classPropertyName: "currentFilter", publicName: "currentFilter", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { apply: "apply", close: "close" }, host: { listeners: { "keydown.escape": "onEscape()" } }, viewQueries: [{ propertyName: "popupEl", first: true, predicate: ["popupEl"], descendants: true }], ngImport: i0, template: "\n<div\n #popupEl\n class=\"gp-grid-filter-popup\"\n [style.position]=\"'fixed'\"\n [style.zIndex]=\"10000\"\n [style.top.px]=\"popupTop()\"\n [style.left.px]=\"popupLeft()\"\n [style.minWidth.px]=\"popupMinWidth()\"\n [style.visibility]=\"positioned() ? 'visible' : 'hidden'\"\n (keydown.escape)=\"close.emit()\"\n (click)=\"$event.stopPropagation()\">\n\n <div class=\"gp-grid-filter-header\">\n {{ filterTitle() }}\n </div>\n\n <div [class]=\"'gp-grid-filter-content ' + (isNumberColumn() ? 'gp-grid-filter-number' : 'gp-grid-filter-text')\">\n @if (isNumberColumn()) {\n @for (cond of numberConditions; track $index; let i = $index) {\n <div class=\"gp-grid-filter-condition\">\n @if (i > 0) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"numberConditions[i - 1]?.nextOperator === 'and'\"\n (click)=\"setNumberNextOp(i - 1, 'and')\">\n {{ labels().and }}\n </button>\n <button\n type=\"button\"\n [class.active]=\"numberConditions[i - 1]?.nextOperator === 'or'\"\n (click)=\"setNumberNextOp(i - 1, 'or')\">\n {{ labels().or }}\n </button>\n </div>\n }\n <div class=\"gp-grid-filter-row\">\n <select\n [value]=\"cond.operator\"\n (change)=\"onNumberOperatorChange(i, $any($event.target).value)\">\n @for (op of numberOperators(); track op.value) {\n <option [value]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n @if (!isValueLessNumberOp(cond.operator)) {\n <input\n type=\"number\"\n [value]=\"cond.value\"\n (input)=\"cond.value = $any($event.target).value\"\n [placeholder]=\"labels().valuePlaceholder\" />\n @if (cond.operator === 'between') {\n <span class=\"gp-grid-filter-to\">{{ labels().betweenSeparator }}</span>\n <input\n type=\"number\"\n [value]=\"cond.valueTo\"\n (input)=\"cond.valueTo = $any($event.target).value\"\n [placeholder]=\"labels().valuePlaceholder\" />\n }\n }\n @if (numberConditions.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove\"\n (click)=\"removeNumberCondition(i)\">{{ labels().removeCondition }}</button>\n }\n </div>\n </div>\n }\n <button type=\"button\" class=\"gp-grid-filter-add\" (click)=\"addNumberCondition()\">\n {{ labels().addCondition }}\n </button>\n } @else {\n @if (showValuesMode()) {\n <div class=\"gp-grid-filter-mode-toggle\">\n <button\n type=\"button\"\n [class.active]=\"filterMode === 'values'\"\n (click)=\"filterMode = 'values'\">\n {{ labels().valuesMode }}\n </button>\n <button\n type=\"button\"\n [class.active]=\"filterMode === 'condition'\"\n (click)=\"filterMode = 'condition'\">\n {{ labels().conditionMode }}\n </button>\n </div>\n }\n\n @if (filterMode === 'values' && showValuesMode()) {\n <input\n class=\"gp-grid-filter-search\"\n type=\"text\"\n [value]=\"searchText\"\n (input)=\"searchText = $any($event.target).value\"\n [placeholder]=\"labels().searchPlaceholder\" />\n <div class=\"gp-grid-filter-actions\">\n <button type=\"button\" (click)=\"selectAll()\">{{ labels().selectAll }}</button>\n <button type=\"button\" (click)=\"deselectAll()\">{{ labels().deselectAll }}</button>\n </div>\n <div class=\"gp-grid-filter-list\">\n @if (hasBlanks()) {\n <label class=\"gp-grid-filter-option\">\n <input\n type=\"checkbox\"\n [checked]=\"includeBlanks\"\n (change)=\"includeBlanks = $any($event.target).checked\" />\n <span class=\"gp-grid-filter-blank\">{{ labels().blanks }}</span>\n </label>\n }\n @for (entry of filteredUniqueEntries(); track entry.label) {\n <label class=\"gp-grid-filter-option\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedLabels.has(entry.label)\"\n (change)=\"toggleValue(entry.label, $any($event.target).checked)\" />\n <span>{{ entry.label }}</span>\n </label>\n }\n </div>\n }\n\n @if (filterMode === 'condition') {\n @for (cond of textConditions; track $index; let i = $index) {\n <div class=\"gp-grid-filter-condition\">\n @if (i > 0) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"textConditions[i - 1]?.nextOperator === 'and'\"\n (click)=\"setTextNextOp(i - 1, 'and')\">\n {{ labels().and }}\n </button>\n <button\n type=\"button\"\n [class.active]=\"textConditions[i - 1]?.nextOperator === 'or'\"\n (click)=\"setTextNextOp(i - 1, 'or')\">\n {{ labels().or }}\n </button>\n </div>\n }\n <div class=\"gp-grid-filter-row\">\n <select\n [value]=\"cond.operator\"\n (change)=\"onTextOperatorChange(i, $any($event.target).value)\">\n @for (op of textOperators(); track op.value) {\n <option [value]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n @if (!isValueLessTextOp(cond.operator)) {\n <input\n class=\"gp-grid-filter-text-input\"\n type=\"text\"\n [value]=\"cond.value\"\n (input)=\"cond.value = $any($event.target).value\"\n [placeholder]=\"labels().valuePlaceholder\" />\n }\n @if (textConditions.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove\"\n (click)=\"removeTextCondition(i)\">{{ labels().removeCondition }}</button>\n }\n </div>\n </div>\n }\n <button type=\"button\" class=\"gp-grid-filter-add\" (click)=\"addTextCondition()\">\n {{ labels().addCondition }}\n </button>\n }\n }\n\n <div class=\"gp-grid-filter-buttons\">\n <button type=\"button\" class=\"gp-grid-filter-btn-clear\" (click)=\"handleClear()\">\n {{ labels().clear }}\n </button>\n <button type=\"button\" class=\"gp-grid-filter-btn-apply\" (click)=\"handleApply()\">\n {{ labels().apply }}\n </button>\n </div>\n </div>\n</div>\n", isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1107
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: FilterPopupComponent, isStandalone: true, selector: "gp-grid-filter-popup", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, colIndex: { classPropertyName: "colIndex", publicName: "colIndex", isSignal: true, isRequired: true, transformFunction: null }, anchorEl: { classPropertyName: "anchorEl", publicName: "anchorEl", isSignal: true, isRequired: true, transformFunction: null }, distinctValues: { classPropertyName: "distinctValues", publicName: "distinctValues", isSignal: true, isRequired: true, transformFunction: null }, currentFilter: { classPropertyName: "currentFilter", publicName: "currentFilter", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { apply: "apply", close: "close" }, host: { listeners: { "keydown.escape": "onEscape()" } }, viewQueries: [{ propertyName: "popupEl", first: true, predicate: ["popupEl"], descendants: true }], ngImport: i0, template: "\n<div\n #popupEl\n class=\"gp-grid-filter-popup\"\n [style.position]=\"'fixed'\"\n [style.zIndex]=\"10000\"\n [style.top.px]=\"popupTop()\"\n [style.left.px]=\"popupLeft()\"\n [style.minWidth.px]=\"popupMinWidth()\"\n [style.visibility]=\"positioned() ? 'visible' : 'hidden'\"\n (keydown.escape)=\"close.emit()\"\n (click)=\"$event.stopPropagation()\">\n\n <div class=\"gp-grid-filter-header\">\n {{ filterTitle() }}\n </div>\n\n <div [class]=\"'gp-grid-filter-content ' + (isNumberColumn() ? 'gp-grid-filter-number' : 'gp-grid-filter-text')\">\n @if (isNumberColumn()) {\n <div class=\"gp-grid-filter-groups\">\n @if (numberGroups.length > 1) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"groupCombination === 'and'\"\n [attr.aria-pressed]=\"groupCombination === 'and'\"\n (click)=\"setGroupCombination('and')\">\n {{ labels().and }}\n </button>\n <button\n type=\"button\"\n [class.active]=\"groupCombination === 'or'\"\n [attr.aria-pressed]=\"groupCombination === 'or'\"\n (click)=\"setGroupCombination('or')\">\n {{ labels().or }}\n </button>\n </div>\n }\n @for (group of numberGroups; track $index; let groupIndex = $index) {\n <div class=\"gp-grid-filter-group\">\n @if (group.conditions.length > 1 || numberGroups.length > 1) {\n <div class=\"gp-grid-filter-group-actions\">\n @if (group.conditions.length > 1) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"group.combination === 'and'\"\n [attr.aria-pressed]=\"group.combination === 'and'\"\n (click)=\"setNumberGroupCombination(groupIndex, 'and')\">\n {{ labels().and }}\n </button>\n <button\n type=\"button\"\n [class.active]=\"group.combination === 'or'\"\n [attr.aria-pressed]=\"group.combination === 'or'\"\n (click)=\"setNumberGroupCombination(groupIndex, 'or')\">\n {{ labels().or }}\n </button>\n </div>\n }\n @if (numberGroups.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove gp-grid-filter-group-remove\"\n (click)=\"removeNumberGroup(groupIndex)\">\n {{ labels().removeGroup }}\n </button>\n }\n </div>\n }\n @for (cond of group.conditions; track $index; let i = $index) {\n <div class=\"gp-grid-filter-condition\">\n <div class=\"gp-grid-filter-row\">\n <select\n [value]=\"cond.operator\"\n (change)=\"onNumberOperatorChange(groupIndex, i, $any($event.target).value)\">\n @for (op of numberOperators(); track op.value) {\n <option [value]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n @if (!isValueLessNumberOp(cond.operator)) {\n <input\n type=\"number\"\n [value]=\"cond.value\"\n (input)=\"cond.value = $any($event.target).value\"\n [placeholder]=\"labels().valuePlaceholder\" />\n @if (cond.operator === 'between') {\n <span class=\"gp-grid-filter-to\">{{ labels().betweenSeparator }}</span>\n <input\n type=\"number\"\n [value]=\"cond.valueTo\"\n (input)=\"cond.valueTo = $any($event.target).value\"\n [placeholder]=\"labels().valuePlaceholder\" />\n }\n }\n @if (group.conditions.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove\"\n (click)=\"removeNumberCondition(groupIndex, i)\">\n {{ labels().removeCondition }}\n </button>\n }\n </div>\n </div>\n }\n <button\n type=\"button\"\n class=\"gp-grid-filter-add\"\n (click)=\"addNumberCondition(groupIndex)\">\n {{ labels().addCondition }}\n </button>\n </div>\n }\n <button\n type=\"button\"\n class=\"gp-grid-filter-add gp-grid-filter-add-group\"\n (click)=\"addNumberGroup()\">\n {{ labels().addGroup }}\n </button>\n </div>\n } @else {\n @if (showValuesMode()) {\n <div class=\"gp-grid-filter-mode-toggle\">\n <button\n type=\"button\"\n [class.active]=\"filterMode === 'values'\"\n [attr.aria-pressed]=\"filterMode === 'values'\"\n (click)=\"filterMode = 'values'\">\n {{ labels().valuesMode }}\n </button>\n <button\n type=\"button\"\n [class.active]=\"filterMode === 'condition'\"\n [attr.aria-pressed]=\"filterMode === 'condition'\"\n (click)=\"filterMode = 'condition'\">\n {{ labels().conditionMode }}\n </button>\n </div>\n }\n\n @if (filterMode === 'values' && showValuesMode()) {\n <input\n class=\"gp-grid-filter-search\"\n type=\"text\"\n [value]=\"searchText\"\n (input)=\"searchText = $any($event.target).value\"\n [placeholder]=\"labels().searchPlaceholder\" />\n <div class=\"gp-grid-filter-actions\">\n <button type=\"button\" (click)=\"selectAll()\">{{ labels().selectAll }}</button>\n <button type=\"button\" (click)=\"deselectAll()\">{{ labels().deselectAll }}</button>\n </div>\n <div class=\"gp-grid-filter-list\">\n @if (hasBlanks()) {\n <label class=\"gp-grid-filter-option\">\n <input\n type=\"checkbox\"\n [checked]=\"includeBlanks\"\n (change)=\"includeBlanks = $any($event.target).checked\" />\n <span class=\"gp-grid-filter-blank\">{{ labels().blanks }}</span>\n </label>\n }\n @for (entry of filteredUniqueEntries(); track entry.label) {\n <label class=\"gp-grid-filter-option\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedLabels.has(entry.label)\"\n (change)=\"toggleValue(entry.label, $any($event.target).checked)\" />\n <span>{{ entry.label }}</span>\n </label>\n }\n </div>\n }\n\n @if (filterMode === 'condition') {\n <div class=\"gp-grid-filter-groups\">\n @if (textGroups.length > 1) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"groupCombination === 'and'\"\n [attr.aria-pressed]=\"groupCombination === 'and'\"\n (click)=\"setGroupCombination('and')\">\n {{ labels().and }}\n </button>\n <button\n type=\"button\"\n [class.active]=\"groupCombination === 'or'\"\n [attr.aria-pressed]=\"groupCombination === 'or'\"\n (click)=\"setGroupCombination('or')\">\n {{ labels().or }}\n </button>\n </div>\n }\n @for (group of textGroups; track $index; let groupIndex = $index) {\n <div class=\"gp-grid-filter-group\">\n @if (group.conditions.length > 1 || textGroups.length > 1) {\n <div class=\"gp-grid-filter-group-actions\">\n @if (group.conditions.length > 1) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"group.combination === 'and'\"\n [attr.aria-pressed]=\"group.combination === 'and'\"\n (click)=\"setTextGroupCombination(groupIndex, 'and')\">\n {{ labels().and }}\n </button>\n <button\n type=\"button\"\n [class.active]=\"group.combination === 'or'\"\n [attr.aria-pressed]=\"group.combination === 'or'\"\n (click)=\"setTextGroupCombination(groupIndex, 'or')\">\n {{ labels().or }}\n </button>\n </div>\n }\n @if (textGroups.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove gp-grid-filter-group-remove\"\n (click)=\"removeTextGroup(groupIndex)\">\n {{ labels().removeGroup }}\n </button>\n }\n </div>\n }\n @for (cond of group.conditions; track $index; let i = $index) {\n <div class=\"gp-grid-filter-condition\">\n <div class=\"gp-grid-filter-row\">\n <select\n [value]=\"cond.operator\"\n (change)=\"onTextOperatorChange(groupIndex, i, $any($event.target).value)\">\n @for (op of textOperators(); track op.value) {\n <option [value]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n @if (!isValueLessTextOp(cond.operator)) {\n <input\n class=\"gp-grid-filter-text-input\"\n type=\"text\"\n [value]=\"cond.value\"\n (input)=\"cond.value = $any($event.target).value\"\n [placeholder]=\"labels().valuePlaceholder\" />\n }\n @if (group.conditions.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove\"\n (click)=\"removeTextCondition(groupIndex, i)\">\n {{ labels().removeCondition }}\n </button>\n }\n </div>\n </div>\n }\n <button\n type=\"button\"\n class=\"gp-grid-filter-add\"\n (click)=\"addTextCondition(groupIndex)\">\n {{ labels().addCondition }}\n </button>\n </div>\n }\n <button\n type=\"button\"\n class=\"gp-grid-filter-add gp-grid-filter-add-group\"\n (click)=\"addTextGroup()\">\n {{ labels().addGroup }}\n </button>\n </div>\n }\n }\n\n <div class=\"gp-grid-filter-buttons\">\n <button type=\"button\" class=\"gp-grid-filter-btn-clear\" (click)=\"handleClear()\">\n {{ labels().clear }}\n </button>\n <button type=\"button\" class=\"gp-grid-filter-btn-apply\" (click)=\"handleApply()\">\n {{ labels().apply }}\n </button>\n </div>\n </div>\n</div>\n", isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
959
1108
  }
960
1109
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: FilterPopupComponent, decorators: [{
961
1110
  type: Component,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gp-grid/angular",
3
3
  "description": "A high-performance Angular data grid component with virtual scrolling, cell selection, sorting, filtering, and Excel-like editing",
4
- "version": "0.22.0",
4
+ "version": "0.23.1",
5
5
  "license": "Apache-2.0",
6
6
  "module": "fesm2022/gp-grid-angular.mjs",
7
7
  "typings": "types/gp-grid-angular.d.ts",
@@ -51,7 +51,7 @@
51
51
  "@angular/core": ">=18.0.0"
52
52
  },
53
53
  "dependencies": {
54
- "@gp-grid/core": "0.22.0",
54
+ "@gp-grid/core": "0.23.1",
55
55
  "tslib": "^2.8.1"
56
56
  },
57
57
  "sideEffects": false,
@@ -1,6 +1,6 @@
1
1
  import * as _gp_grid_core from '@gp-grid/core';
2
- import { HeaderRendererParams, SortDirection, VisibleColumnInfo, HeaderData, ColumnDefinition, SlotData, CellPosition, CellRange, CellValue, CellRendererParams, EditRendererParams, FillHandlePosition, DragState, GridLabels, ColumnFilterModel, getTextOperatorOptions, getNumberOperatorOptions, DistinctValueEntry, BatchChangeSetters, DataSource, RowId, HighlightingOptions, RowLoadingOptions, CellValueChangedEvent, ParallelSortOptions, MutableDataSource } from '@gp-grid/core';
3
- export { CellDataType, CellPosition, CellRange, CellRendererParams, CellValue, CellValueChangedEvent, ColumnDefinition, ColumnFilterModel, DataSource, DataSourceLoadMode, DataSourceRange, DataSourceRequest, DataSourceResponse, EditRendererParams, FilterCondition, FilterModel, GridCore, GridFilterOperatorLabels, GridInstruction, GridLabels, HeaderRendererParams, HighlightContext, HighlightingOptions, MutableDataSource, RowCacheEviction, RowCacheOptions, RowId, RowLoadingMode, RowLoadingOptions, SortDirection, SortModel, createClientDataSource, createDataSourceFromArray, createMutableClientDataSource, createServerDataSource } from '@gp-grid/core';
2
+ import { HeaderRendererParams, SortDirection, VisibleColumnInfo, HeaderData, ColumnDefinition, SlotData, CellPosition, CellRange, CellValue, CellRendererParams, EditRendererParams, FillHandlePosition, DragState, GridLabels, FilterCombination, ColumnFilterModel, getTextOperatorOptions, getNumberOperatorOptions, DistinctValueEntry, BatchChangeSetters, DataSource, RowId, HighlightingOptions, RowLoadingOptions, CellValueChangedEvent, GridLabelOverrides, ParallelSortOptions, MutableDataSource } from '@gp-grid/core';
3
+ export { CellDataType, CellPosition, CellRange, CellRendererParams, CellValue, CellValueChangedEvent, ColumnDefinition, ColumnFilterInput, ColumnFilterModel, DataSource, DataSourceLoadMode, DataSourceRange, DataSourceRequest, DataSourceResponse, EditRendererParams, FilterCombination, FilterCondition, FilterConditionGroup, FilterModel, GridCore, GridFilterOperatorLabels, GridInstruction, GridLabelOverrides, GridLabels, HeaderRendererParams, HighlightContext, HighlightingOptions, LegacyColumnFilterModel, LegacyFilterCondition, MutableDataSource, RowCacheEviction, RowCacheOptions, RowId, RowLoadingMode, RowLoadingOptions, SortDirection, SortModel, createClientDataSource, createDataSourceFromArray, createMutableClientDataSource, createServerDataSource, isLegacyColumnFilterModel, normalizeColumnFilterModel } from '@gp-grid/core';
4
4
  import * as _angular_core from '@angular/core';
5
5
  import { TemplateRef, ElementRef, AfterViewInit, OnDestroy, Signal, OnInit, InjectionToken, Provider } from '@angular/core';
6
6
 
@@ -148,14 +148,18 @@ declare class GridBodyComponent {
148
148
  interface TextConditionState {
149
149
  operator: string;
150
150
  value: string;
151
- nextOperator: 'and' | 'or';
152
151
  }
153
152
  interface NumberConditionState {
154
153
  operator: string;
155
154
  value: string;
156
155
  valueTo: string;
157
- nextOperator: 'and' | 'or';
158
156
  }
157
+ interface ConditionGroupState<TCondition> {
158
+ conditions: TCondition[];
159
+ combination: FilterCombination;
160
+ }
161
+ type TextConditionGroupState = ConditionGroupState<TextConditionState>;
162
+ type NumberConditionGroupState = ConditionGroupState<NumberConditionState>;
159
163
  type FilterMode = 'values' | 'condition';
160
164
 
161
165
  declare class FilterPopupComponent implements AfterViewInit, OnDestroy {
@@ -179,8 +183,9 @@ declare class FilterPopupComponent implements AfterViewInit, OnDestroy {
179
183
  searchText: string;
180
184
  selectedLabels: Set<string>;
181
185
  includeBlanks: boolean;
182
- textConditions: TextConditionState[];
183
- numberConditions: NumberConditionState[];
186
+ textGroups: TextConditionGroupState[];
187
+ numberGroups: NumberConditionGroupState[];
188
+ groupCombination: FilterCombination;
184
189
  protected textOperators(): ReturnType<typeof getTextOperatorOptions>;
185
190
  protected numberOperators(): ReturnType<typeof getNumberOperatorOptions>;
186
191
  protected filterTitle(): string;
@@ -198,14 +203,19 @@ declare class FilterPopupComponent implements AfterViewInit, OnDestroy {
198
203
  toggleValue(label: string, checked: boolean): void;
199
204
  selectAll(): void;
200
205
  deselectAll(): void;
201
- onTextOperatorChange(index: number, value: string): void;
202
- onNumberOperatorChange(index: number, value: string): void;
203
- addTextCondition(): void;
204
- addNumberCondition(): void;
205
- removeTextCondition(index: number): void;
206
- removeNumberCondition(index: number): void;
207
- setTextNextOp(index: number, value: 'and' | 'or'): void;
208
- setNumberNextOp(index: number, value: 'and' | 'or'): void;
206
+ onTextOperatorChange(groupIndex: number, index: number, value: string): void;
207
+ onNumberOperatorChange(groupIndex: number, index: number, value: string): void;
208
+ addTextCondition(groupIndex: number): void;
209
+ addNumberCondition(groupIndex: number): void;
210
+ removeTextCondition(groupIndex: number, index: number): void;
211
+ removeNumberCondition(groupIndex: number, index: number): void;
212
+ addTextGroup(): void;
213
+ addNumberGroup(): void;
214
+ removeTextGroup(groupIndex: number): void;
215
+ removeNumberGroup(groupIndex: number): void;
216
+ setTextGroupCombination(groupIndex: number, value: FilterCombination): void;
217
+ setNumberGroupCombination(groupIndex: number, value: FilterCombination): void;
218
+ setGroupCombination(value: FilterCombination): void;
209
219
  handleApply(): void;
210
220
  handleClear(): void;
211
221
  private buildFilter;
@@ -390,8 +400,8 @@ declare class GpGridComponent implements OnInit, AfterViewInit, OnDestroy {
390
400
  fromIndex: number;
391
401
  toIndex: number;
392
402
  }>;
393
- labels: _angular_core.InputSignal<Partial<GridLabels>>;
394
- protected readonly resolvedLabels: _angular_core.Signal<GridLabels>;
403
+ labels: _angular_core.InputSignal<GridLabelOverrides>;
404
+ protected readonly resolvedLabels: _angular_core.Signal<_gp_grid_core.GridLabels>;
395
405
  protected readonly vm: GpGridViewModel;
396
406
  private readonly bindings;
397
407
  constructor();