add set filter
This commit is contained in:
parent
b622444b3c
commit
2d31e1d086
8 changed files with 298 additions and 57 deletions
|
|
@ -0,0 +1,11 @@
|
|||
package ru.micord.ervu.property.grid;
|
||||
|
||||
/**
|
||||
* @author gulnaz
|
||||
*/
|
||||
public enum FilterType {
|
||||
TEXT,
|
||||
DATE,
|
||||
NUMBER,
|
||||
SET
|
||||
}
|
||||
|
|
@ -7,4 +7,5 @@ public class StaticColumn {
|
|||
|
||||
public String column;
|
||||
public String type;
|
||||
public FilterType filterType;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@
|
|||
.webbpm.ervu_lkrp_ul .warning-group + field-set {
|
||||
margin-top: var(--indent-medium);
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .warning-group + .warning-group,
|
||||
.webbpm.ervu_lkrp_ul .warning-group + .warning-group,
|
||||
.webbpm.ervu_lkrp_ul .warning-group + .data-group {
|
||||
margin-top: var(--indent-small);
|
||||
}
|
||||
|
|
@ -300,20 +300,20 @@
|
|||
margin-bottom: 16px;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .paragraph-group > .vertical-container > *:last-child {
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .paragraph-group + .paragraph-group {
|
||||
padding-top: 24px;
|
||||
margin-top: 24px;
|
||||
border-top: 1px solid var(--border-light);
|
||||
}
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul .fieldset {
|
||||
padding: 24px;
|
||||
margin-bottom: 0;
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: 4px;
|
||||
background-color: var(--bg-light);
|
||||
background-color: var(--bg-light);
|
||||
box-shadow: none;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .fieldset legend + div {
|
||||
|
|
@ -392,7 +392,7 @@
|
|||
margin-top: var(--indent-medium);
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul input,
|
||||
.webbpm.ervu_lkrp_ul input,
|
||||
.webbpm.ervu_lkrp_ul button {
|
||||
border-radius: 4px;
|
||||
box-shadow: none !important;
|
||||
|
|
@ -426,7 +426,7 @@
|
|||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular {
|
||||
font-family: 'Inter';
|
||||
}
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-popup .ag-select-list-item {
|
||||
font-size: var(--size-text-secondary);
|
||||
|
|
@ -440,31 +440,39 @@
|
|||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-icon:is(.ag-icon-small-down, .ag-icon-filter)::before {
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-icon:is(.ag-icon-small-down)::before {
|
||||
content: "";
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-icon.ag-icon-small-down::before {
|
||||
background-image: url(../img/svg/arrow-left.svg);
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-icon.ag-icon-filter::before {
|
||||
background-image: url(../img/svg/filter.svg);
|
||||
top: -4px;
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-icon-menu {
|
||||
background: transparent url(../img/svg/filter.svg) center no-repeat;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-header-cell-menu-button:not(.ag-header-menu-always-show) {
|
||||
opacity: unset;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-filter-select {
|
||||
font-size: var(--size-text-secondary);
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-filter-select .ag-picker-field-wrapper {
|
||||
font-family: 'InterSB';
|
||||
border: 0;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-filter-select .ag-picker-field-wrapper .ag-picker-field-display {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
@ -472,6 +480,7 @@
|
|||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-filter-body {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-filter-body input {
|
||||
color: var(--color-light);
|
||||
font-size: var(--size-text-secondary);
|
||||
|
|
@ -479,6 +488,43 @@
|
|||
padding: 6px 12px !important;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-set-filter {
|
||||
min-width: 100px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-set-filter-item + .ag-set-filter-item {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-filter .ag-filter-checkbox {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 2px solid var(--color-link);
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
margin-right: 12px;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-filter .ag-filter-checkbox:before {
|
||||
content: '';
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-filter .ag-filter-checkbox:checked:before {
|
||||
border-radius: 2px;
|
||||
background: var(--color-link) url(../img/svg/input-checked.svg);
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul ag-grid-angular .ag-header-row {
|
||||
font-family: 'InterSB';
|
||||
}
|
||||
|
|
@ -528,7 +574,7 @@
|
|||
flex-direction: column-reverse;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .main-block .left-block {
|
||||
padding-right: 0;
|
||||
padding-right: 0;
|
||||
margin-top: var(--indent-medium);
|
||||
}
|
||||
|
||||
|
|
@ -542,8 +588,8 @@
|
|||
top: 20px;
|
||||
left: auto;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul .left-block {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -702,7 +748,7 @@
|
|||
background: none;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .modal.show .modal-content .warning-group > div > * + *:not([hidden]) {
|
||||
margin-top: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .modal.show .modal-content hyper-link.btn {
|
||||
padding: 0;
|
||||
|
|
@ -711,7 +757,7 @@
|
|||
}
|
||||
.webbpm.ervu_lkrp_ul .modal.show .modal-content hyper-link.btn .hyper-link {
|
||||
padding: 13px 38px;
|
||||
border: 1px solid var(--color-link);
|
||||
border: 1px solid var(--color-link);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .modal.show .modal-content hyper-link.btn .hyper-link:hover {
|
||||
|
|
@ -814,7 +860,7 @@
|
|||
.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-size::before {
|
||||
position: relative;
|
||||
content: "|";
|
||||
margin-right: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-delete-btn {
|
||||
color: var(--color-link);
|
||||
|
|
@ -871,7 +917,7 @@
|
|||
|
||||
/* temp fix + add flex-wrap*/
|
||||
.webbpm.ervu_lkrp_ul :is(.fieldset, .warning-group) .horizontal-container text + button-component:not(.info) {
|
||||
margin-top: -2px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul .dialog-link {
|
||||
|
|
|
|||
3
frontend/src/resources/img/svg/input-checked.svg
Normal file
3
frontend/src/resources/img/svg/input-checked.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.56192 6.65976L5.83968 9.93751L11.4379 4.33928" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 179 B |
|
|
@ -1,12 +1,15 @@
|
|||
import {ColDef, ICellRendererFunc, SuppressKeyboardEventParams} from "ag-grid-community";
|
||||
import {
|
||||
ColDef,
|
||||
DateFilter,
|
||||
ICellRendererFunc,
|
||||
SuppressKeyboardEventParams
|
||||
} from "ag-grid-community";
|
||||
import {
|
||||
DateTimeUtil,
|
||||
DefaultTooltip,
|
||||
GridCellTooltipUtils,
|
||||
GridColumnComparatorUtils,
|
||||
GridColumnFilterUtils,
|
||||
GridColumnKeyboardUtils,
|
||||
GridSettingHeader,
|
||||
GridValueFormatterUtils,
|
||||
GridValueRendererUtils,
|
||||
PinnedType
|
||||
|
|
@ -14,6 +17,7 @@ import {
|
|||
import {Moment} from "moment";
|
||||
import * as moment from "moment-timezone";
|
||||
import {StaticGridColumn} from "../../../generated/ru/micord/ervu/property/grid/StaticGridColumn";
|
||||
import {CustomGridColumnFilterUtils} from "./filter/CustomGridColumnFilterUtils";
|
||||
|
||||
export class StaticColumnInitializer {
|
||||
|
||||
|
|
@ -50,40 +54,6 @@ export class StaticColumnInitializer {
|
|||
let type = column.field.type;
|
||||
|
||||
if (type != null) {
|
||||
|
||||
if (column.filter !== false) {
|
||||
columnDef.floatingFilter = gridRef.floatingFilter;
|
||||
columnDef.filter = GridColumnFilterUtils.columnFilter(type);
|
||||
|
||||
if (columnDef.filter === 'agDateColumnFilter') {
|
||||
columnDef.filterParams = {
|
||||
comparator: function (filterLocalDateAtMidnight, cellValue) {
|
||||
|
||||
if (!cellValue) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let filterMoment: Moment = moment.utc(filterLocalDateAtMidnight)
|
||||
.add(-filterLocalDateAtMidnight.getTimezoneOffset(), 'm');
|
||||
let cellMoment: Moment = DateTimeUtil.parseToMidnightUTC(cellValue);
|
||||
|
||||
if (filterMoment.isSame(cellMoment)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cellMoment.isBefore(filterMoment)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cellMoment.isAfter(filterMoment)) {
|
||||
return 1;
|
||||
}
|
||||
},
|
||||
browserDatePicker: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (gridRef.getRowModelType() == "clientSide") {
|
||||
columnDef.comparator = GridColumnComparatorUtils.columnComparator(type);
|
||||
}
|
||||
|
|
@ -91,6 +61,39 @@ export class StaticColumnInitializer {
|
|||
columnDef.cellRenderer = gridRef.createRenderer(column);
|
||||
}
|
||||
|
||||
if (column.filter !== false) {
|
||||
columnDef.floatingFilter = gridRef.floatingFilter;
|
||||
columnDef.filter = CustomGridColumnFilterUtils.columnFilter(column.field.filterType);
|
||||
|
||||
if (columnDef.filter === DateFilter) {
|
||||
columnDef.filterParams = {
|
||||
comparator: function (filterLocalDateAtMidnight, cellValue) {
|
||||
|
||||
if (!cellValue) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let filterMoment: Moment = moment.utc(filterLocalDateAtMidnight)
|
||||
.add(-filterLocalDateAtMidnight.getTimezoneOffset(), 'm');
|
||||
let cellMoment: Moment = DateTimeUtil.parseToMidnightUTC(cellValue);
|
||||
|
||||
if (filterMoment.isSame(cellMoment)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cellMoment.isBefore(filterMoment)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cellMoment.isAfter(filterMoment)) {
|
||||
return 1;
|
||||
}
|
||||
},
|
||||
browserDatePicker: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
columnDef.suppressKeyboardEvent = (params: SuppressKeyboardEventParams) => {
|
||||
return GridColumnKeyboardUtils.suppressHomeAndEndKeyboardEvent(params);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
import {FilterType} from "../../../../generated/ru/micord/ervu/property/grid/FilterType";
|
||||
import {DateFilter, NumberFilter, TextFilter} from "ag-grid-community";
|
||||
import {SetFilter} from "./SetFilter";
|
||||
|
||||
export class CustomGridColumnFilterUtils {
|
||||
|
||||
public static columnFilter(type: FilterType) {
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case FilterType.NUMBER:
|
||||
return NumberFilter;
|
||||
case FilterType.DATE:
|
||||
return DateFilter;
|
||||
case FilterType.SET:
|
||||
return SetFilter;
|
||||
case FilterType.TEXT:
|
||||
default:
|
||||
return TextFilter;
|
||||
}
|
||||
}
|
||||
}
|
||||
111
frontend/src/ts/ervu/component/grid/filter/SetFilter.ts
Normal file
111
frontend/src/ts/ervu/component/grid/filter/SetFilter.ts
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import {AgPromise, IDoesFilterPassParams, IFilterComp, IFilterParams} from "ag-grid-community";
|
||||
|
||||
export class SetFilter implements IFilterComp {
|
||||
|
||||
private OPTION_TEMPLATE = `<label class="ag-set-filter-item">
|
||||
<input type="checkbox" class="ag-filter-checkbox" checked/>
|
||||
<span class="ag-filter-value"></span>
|
||||
</label>`;
|
||||
|
||||
private eGui!: HTMLDivElement;
|
||||
private selectAll: HTMLInputElement;
|
||||
private checkboxes: HTMLInputElement[] = [];
|
||||
private values: any[] = [];
|
||||
private initialValues: any[];
|
||||
private filterActive: boolean;
|
||||
private filterChangedCallback!: (additionalEventAttributes?: any) => void;
|
||||
private filterParams!: IFilterParams;
|
||||
private valueType: string;
|
||||
|
||||
init(params: IFilterParams): void {
|
||||
this.eGui = document.createElement('div');
|
||||
this.eGui.className = 'ag-set-filter';
|
||||
let index = 0;
|
||||
this.selectAll = this.initCheckBox('selectAll', 'Все', index);
|
||||
this.checkboxes.push(this.selectAll);
|
||||
|
||||
params.api.getRenderedNodes()
|
||||
.map(node => node.data[params.colDef.field])
|
||||
.sort((n1, n2) => n1 > n2 ? 1 : n1 < n2 ? -1 : 0)
|
||||
.forEach(value => {
|
||||
if (this.values.includes(value)) {
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
let id = `option-${index}`;
|
||||
let checkbox = this.initCheckBox(id, value, index);
|
||||
this.checkboxes.push(checkbox);
|
||||
this.values.push(value);
|
||||
});
|
||||
this.initialValues = this.values.slice();
|
||||
this.filterParams = params;
|
||||
this.filterActive = false;
|
||||
this.filterChangedCallback = params.filterChangedCallback;
|
||||
|
||||
if (this.values.length > 0) {
|
||||
this.valueType = typeof this.values[0];
|
||||
}
|
||||
};
|
||||
|
||||
private initCheckBox(id: string, value: string, index: number): HTMLInputElement {
|
||||
this.eGui.insertAdjacentHTML('beforeend', this.OPTION_TEMPLATE);
|
||||
this.eGui.querySelectorAll('.ag-filter-value')[index].innerHTML = value;
|
||||
let checkbox = this.eGui.querySelectorAll('.ag-filter-checkbox')[index] as HTMLInputElement;
|
||||
checkbox.setAttribute('id', id);
|
||||
checkbox.addEventListener('change', this.onCheckBoxChanged.bind(this));
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
getGui(): HTMLDivElement {
|
||||
return this.eGui;
|
||||
};
|
||||
|
||||
onCheckBoxChanged(event: any) {
|
||||
let checked = event.target.checked;
|
||||
|
||||
if (event.target === this.selectAll) {
|
||||
this.checkboxes.forEach(checkbox => checkbox.checked = checked);
|
||||
this.values = checked ? this.initialValues.slice() : [];
|
||||
}
|
||||
else {
|
||||
let value = event.target.nextElementSibling.textContent;
|
||||
value = this.valueType === 'number' ? +value : value;
|
||||
|
||||
if (checked) {
|
||||
this.values.push(value);
|
||||
|
||||
if (this.values.length == this.initialValues.length) {
|
||||
this.selectAll.checked = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
let index = this.values.indexOf(value);
|
||||
this.values.splice(index, 1);
|
||||
this.selectAll.checked = false;
|
||||
}
|
||||
}
|
||||
this.filterActive = !this.selectAll.checked;
|
||||
this.filterChangedCallback();
|
||||
}
|
||||
|
||||
doesFilterPass(params: IDoesFilterPassParams): boolean {
|
||||
let { field } = this.filterParams.colDef;
|
||||
return this.values.includes(params.data[field]);
|
||||
}
|
||||
|
||||
getModel(): any {
|
||||
return this.isFilterActive() ? { value: this.values } : null;
|
||||
}
|
||||
|
||||
isFilterActive(): boolean {
|
||||
return this.filterActive;
|
||||
}
|
||||
|
||||
setModel(model: any): void | AgPromise<void> {
|
||||
this.values = model == null ? [] : model.value;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.checkboxes.forEach(checkBox => checkBox.removeEventListener('change', this.onCheckBoxChanged.bind(this)));
|
||||
}
|
||||
}
|
||||
|
|
@ -734,6 +734,12 @@
|
|||
<simple>"departureDateTime"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>filterType</key>
|
||||
<value>
|
||||
<simple>"DATE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>type</key>
|
||||
<value>
|
||||
|
|
@ -822,6 +828,12 @@
|
|||
<simple>"fileName"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>filterType</key>
|
||||
<value>
|
||||
<simple>"TEXT"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>type</key>
|
||||
<value>
|
||||
|
|
@ -904,6 +916,12 @@
|
|||
<simple>"filePatternCode"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>filterType</key>
|
||||
<value>
|
||||
<simple>"SET"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>type</key>
|
||||
<value>
|
||||
|
|
@ -986,6 +1004,12 @@
|
|||
<simple>"senderFio"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>filterType</key>
|
||||
<value>
|
||||
<simple>"SET"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>type</key>
|
||||
<value>
|
||||
|
|
@ -1068,6 +1092,12 @@
|
|||
<simple>"status"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>filterType</key>
|
||||
<value>
|
||||
<simple>"TEXT"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>type</key>
|
||||
<value>
|
||||
|
|
@ -1150,6 +1180,12 @@
|
|||
<simple>"filesSentCount"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>filterType</key>
|
||||
<value>
|
||||
<simple>"NUMBER"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>type</key>
|
||||
<value>
|
||||
|
|
@ -1232,6 +1268,12 @@
|
|||
<simple>"acceptedFilesCount"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>filterType</key>
|
||||
<value>
|
||||
<simple>"NUMBER"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>type</key>
|
||||
<value>
|
||||
|
|
@ -1347,8 +1389,8 @@
|
|||
<container>false</container>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
|
||||
<removed>true</removed>
|
||||
<expanded>false</expanded>
|
||||
<removed>true</removed>
|
||||
</scripts>
|
||||
<scripts id="38036714-7fff-4404-98d3-b0f5cc846368">
|
||||
<classRef type="TS">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue