Merge branch 'test/SUPPORT-8728_dropdowntreeview_component' into ervu/tmp_makets

* test/SUPPORT-8728_dropdowntreeview_component:
  SUPPORT-8728: fix case parent id = self id; + add sort order
  правки, ошибка
  SUPPORT-8728: fix накидывание скрипта фильтра
  SUPPORT-8728: перенос и удаление неиспользуемых методов
This commit is contained in:
Фоат Саляхов 2024-12-09 14:45:08 +03:00
commit f2765d6b7d
28 changed files with 322 additions and 1208 deletions

View file

@ -12,7 +12,6 @@ public class TreeItemDto {
public String label;
public TreeItemDto[] children;
public Object businessId;
public String domainId;
public TreeItemDto(Object id, Object parentId, String label) {
this.id = id;

View file

@ -20,9 +20,4 @@ public class TreeItemRpcService extends Behavior {
public List<TreeItemDto> loadTreeData() {
return treeItemService.loadTreeData();
}
@RpcCall
public List<TreeItemDto> loadTreeDataByDomainId(String domainId) {
return treeItemService.loadTreeDataByDomainId(domainId);
}
}

View file

@ -1,6 +1,7 @@
package component.service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -14,10 +15,12 @@ import org.springframework.stereotype.Service;
import ru.cg.webbpm.modules.database.api.bean.TableRow;
import ru.cg.webbpm.modules.database.api.dao.option.LoadOptions;
import ru.cg.webbpm.modules.database.api.dao.option.SortOrder;
import ru.cg.webbpm.modules.database.bean.annotation.GraphSource;
import ru.cg.webbpm.modules.database.bean.annotation.TypedColumn;
import ru.cg.webbpm.modules.database.bean.entity_graph.EntityColumn;
import ru.cg.webbpm.modules.database.bean.entity_graph.EntityColumnType;
import ru.cg.webbpm.modules.standard_annotations.editor.AdvancedProperty;
import ru.cg.webbpm.modules.standard_annotations.validation.NotNull;
/**
@ -39,41 +42,27 @@ public class TreeItemService {
public EntityColumn labelColumn;
@GraphSource(value = TreeItemRpcService.class, scanMode = GraphSource.ScanMode.SELF)
public EntityColumn businessIdColumn;
@AdvancedProperty
@GraphSource(value = TreeItemRpcService.class, scanMode = GraphSource.ScanMode.SELF)
public EntityColumn domainIdColumn;
public EntityColumn sortColumn;
@AdvancedProperty
@NotNull(predicate = "sortColumn != null")
public SortOrder sortOrder;
public List<TreeItemDto> loadTreeData() {
List<TreeItemDto> loadedTreeItems = loadTreeItems();
loadedTreeItems.forEach(item -> item.domainId = null);
return loadedTreeItems.stream()
.filter(item -> item.parentId == null)
.toList();
}
public List<TreeItemDto> loadTreeDataByDomainId(String domainId) {
if (domainId == null || domainIdColumn == null) {
return loadTreeData();
}
List<TreeItemDto> filteredTreeItems = loadTreeItems().stream()
.filter(item -> item.domainId.equalsIgnoreCase(domainId))
.toList();
filteredTreeItems.forEach(this::setDomainIdToNull);
return filteredTreeItems;
}
private void setDomainIdToNull(TreeItemDto treeItem) {
treeItem.domainId = null;
TreeItemDto[] treeItemChildren = treeItem.children;
if (treeItemChildren != null && treeItemChildren.length > 0) {
for (TreeItemDto child : treeItemChildren) {
setDomainIdToNull(child);
}
}
}
private List<TreeItemDto> loadTreeItems() {
List<TreeItemDto> loadedList = this.loadDao.load(getColumns(), new LoadOptions()).stream()
LoadOptions loadOptions = new LoadOptions();
if (sortColumn != null && sortOrder != null) {
loadOptions.setSortFields(Collections.singletonMap(sortColumn, sortOrder));
}
List<TreeItemDto> loadedList = this.loadDao.load(getColumns(), loadOptions).stream()
.map(this::toTreeItemDto)
.toList();
@ -105,9 +94,6 @@ public class TreeItemService {
if (businessIdColumn != null) {
columnSet.add(businessIdColumn);
}
if (domainIdColumn != null) {
columnSet.add(domainIdColumn);
}
return columnSet;
}
@ -115,12 +101,12 @@ public class TreeItemService {
TreeItemDto treeItemDto = new TreeItemDto(
tableRow.get(idColumn), tableRow.get(parentIdColumn), (String) tableRow.get(labelColumn)
);
if (treeItemDto.parentId == treeItemDto.id) {
treeItemDto.parentId = null;
}
if (businessIdColumn != null) {
treeItemDto.businessId = tableRow.get(businessIdColumn);
}
if (domainIdColumn != null) {
treeItemDto.domainId =(String) tableRow.get(domainIdColumn);
}
return treeItemDto;
}
}

View file

@ -1,6 +1,6 @@
import {Injectable} from "@angular/core";
import {DefaultTreeviewI18n} from "../lib/treeview-i18n";
import {TreeviewItem, TreeviewSelection} from "../lib/treeview-item";
import {DefaultTreeviewI18n, TreeviewItem, TreeviewSelection} from "ngx-treeview";
@Injectable()
export class DropdownTreeviewSelectI18n extends DefaultTreeviewI18n {

View file

@ -1,78 +1,79 @@
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Input,
OnChanges,
Output,
ViewChild
ChangeDetectionStrategy,
Component,
EventEmitter,
Input,
OnChanges,
Output,
ViewChild
} from '@angular/core';
import { DropdownTreeviewSelectI18n } from './dropdown-treeview-select-i18n';
import { DropdownTreeviewComponent } from "../lib/dropdown-treeview.component";
import { TreeviewConfig } from "../lib/treeview-config";
import { TreeviewHelper } from "../lib/treeview-helper";
import { TreeviewI18n } from "../lib/treeview-i18n";
import { TreeviewItem } from "../lib/treeview-item";
import { isNil } from '../lib/utils';
import {DropdownTreeviewSelectI18n} from './dropdown-treeview-select-i18n';
import {
DropdownTreeviewComponent,
TreeviewConfig,
TreeviewHelper,
TreeviewI18n,
TreeviewItem
} from "ngx-treeview";
@Component({
moduleId: module.id,
selector: 'ngx-dropdown-treeview-select',
templateUrl: './../../../../../../src/resources/template/component/external/ngx-treeview/dropdown-treeview-select.component.html',
providers: [{ provide: TreeviewI18n, useClass: DropdownTreeviewSelectI18n }],
changeDetection: ChangeDetectionStrategy.OnPush
})
moduleId: module.id,
selector: 'ngx-dropdown-treeview-select',
templateUrl: './../../../../../../src/resources/template/component/external/ngx-treeview/dropdown-treeview-select.component.html',
providers: [{provide: TreeviewI18n, useClass: DropdownTreeviewSelectI18n}],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DropdownTreeviewSelectComponent implements OnChanges {
@Input() config: TreeviewConfig;
@Input() items: TreeviewItem[];
@Input() maxHeight: number;
@Input() value: any;
@Output() valueChange = new EventEmitter<any>();
@ViewChild(DropdownTreeviewComponent) dropdownTreeviewComponent: DropdownTreeviewComponent;
filterText: string;
private dropdownTreeviewSelectI18n: DropdownTreeviewSelectI18n;
@Input() config: TreeviewConfig;
@Input() items: TreeviewItem[];
@Input() maxHeight: number;
@Input() value: any;
@Output() valueChange = new EventEmitter<any>();
@ViewChild(DropdownTreeviewComponent) dropdownTreeviewComponent: DropdownTreeviewComponent;
filterText: string;
private dropdownTreeviewSelectI18n: DropdownTreeviewSelectI18n;
constructor(public i18n: TreeviewI18n) {
this.config = TreeviewConfig.create({
hasAllCheckBox: false,
hasCollapseExpand: false,
hasFilter: true,
maxHeight: 500
});
this.dropdownTreeviewSelectI18n = i18n as DropdownTreeviewSelectI18n;
constructor(public i18n: TreeviewI18n) {
this.config = TreeviewConfig.create({
hasAllCheckBox: false,
hasCollapseExpand: false,
hasFilter: true,
maxHeight: 500
});
this.dropdownTreeviewSelectI18n = i18n as DropdownTreeviewSelectI18n;
}
ngOnChanges(): void {
if (this.maxHeight) {
this.config.maxHeight = this.maxHeight;
}
this.updateSelectedItem();
}
ngOnChanges(): void {
if (this.maxHeight) {
this.config.maxHeight = this.maxHeight;
}
this.updateSelectedItem();
}
select(item: TreeviewItem): void {
this.selectItem(item);
}
private updateSelectedItem(): void {
if (!isNil(this.items)) {
const selectedItem = TreeviewHelper.findItemInList(this.items, this.value);
this.selectItem(selectedItem);
}
}
private selectItem(item: TreeviewItem): void {
if (this.dropdownTreeviewSelectI18n.selectedItem !== item) {
this.dropdownTreeviewSelectI18n.selectedItem = item;
if (this.dropdownTreeviewComponent) {
this.dropdownTreeviewComponent.onSelectedChange([item]);
}
if (item) {
if (this.value !== item.value) {
this.value = item.value;
this.valueChange.emit(item.value);
}
}
select(item: TreeviewItem): void {
this.selectItem(item);
}
private updateSelectedItem(): void {
if (this.items !== null) {
const selectedItem = TreeviewHelper.findItemInList(this.items, this.value);
this.selectItem(selectedItem);
}
}
private selectItem(item: TreeviewItem): void {
if (this.dropdownTreeviewSelectI18n.selectedItem !== item) {
this.dropdownTreeviewSelectI18n.selectedItem = item;
if (this.dropdownTreeviewComponent) {
this.dropdownTreeviewComponent.onSelectedChange([item]);
}
if (item) {
if (this.value !== item.value) {
this.value = item.value;
this.valueChange.emit(item.value);
}
}
}
}
}

View file

@ -1,22 +0,0 @@
import { Directive, HostListener } from '@angular/core';
import { DropdownDirective } from './dropdown.directive';
@Directive({
selector: '[ngxDropdownMenu]',
host: {
'[class.dropdown-menu]': 'true',
'[class.show]': 'dropdown.isOpen'
}
})
export class DropdownMenuDirective {
constructor(public dropdown: DropdownDirective) {
}
@HostListener('click', ['$event'])
onClick(event: MouseEvent): void {
if (event.button !== 2 && event.srcElement.attributes[0]
&& event.srcElement.attributes[0].nodeValue === 'form-check-label') {
this.dropdown.close();
}
}
}

View file

@ -1,17 +0,0 @@
import { Directive, ElementRef } from '@angular/core';
import { DropdownDirective } from './dropdown.directive';
@Directive({
selector: '[ngxDropdownToggle]',
host: {
class: 'dropdown-toggle',
'aria-haspopup': 'true',
'[attr.aria-expanded]': 'dropdown.isOpen',
'(click)': 'dropdown.toggle()'
}
})
export class DropdownToggleDirective {
constructor(public dropdown: DropdownDirective, elementRef: ElementRef) {
dropdown.toggleElement = elementRef.nativeElement;
}
}

View file

@ -1,49 +0,0 @@
import {
Component,
EventEmitter,
Input,
Output,
ViewChild,
TemplateRef,
ChangeDetectionStrategy
} from '@angular/core';
import { TreeviewI18n } from './treeview-i18n';
import { TreeviewItem } from './treeview-item';
import { TreeviewConfig } from './treeview-config';
import { TreeviewComponent } from './treeview.component';
import { TreeviewHeaderTemplateContext } from './treeview-header-template-context';
import { TreeviewItemTemplateContext } from './treeview-item-template-context';
@Component({
moduleId: module.id,
selector: 'ngx-dropdown-treeview',
templateUrl: './../../../../../../src/resources/template/component/external/ngx-treeview/dropdown-treeview.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DropdownTreeviewComponent {
@Input() buttonClass = 'btn-outline-secondary';
@Input() headerTemplate: TemplateRef<TreeviewHeaderTemplateContext>;
@Input() itemTemplate: TemplateRef<TreeviewItemTemplateContext>;
@Input() items: TreeviewItem[];
@Input() config: TreeviewConfig;
@Output() selectedChange = new EventEmitter<any[]>(true);
@Output() filterChange = new EventEmitter<string>();
@ViewChild(TreeviewComponent) treeviewComponent: TreeviewComponent;
buttonLabel: string;
constructor(
public i18n: TreeviewI18n,
private defaultConfig: TreeviewConfig
) {
this.config = this.defaultConfig;
}
onSelectedChange(values: any[]): void {
this.buttonLabel = this.i18n.getText(this.treeviewComponent.selection);
this.selectedChange.emit(values);
}
onFilterChange(text: string): void {
this.filterChange.emit(text);
}
}

View file

@ -1,54 +0,0 @@
import { Directive, Input, Output, HostBinding, HostListener, EventEmitter } from '@angular/core';
import { isNil } from './utils';
@Directive({
selector: '[ngxDropdown]',
exportAs: 'ngxDropdown'
})
export class DropdownDirective {
toggleElement: any;
@Input('open') internalOpen = false;
@Output() openChange = new EventEmitter<boolean>();
@HostBinding('class.show') get isOpen(): boolean {
return this.internalOpen;
}
@HostListener('keyup.esc')
onKeyupEsc(): void {
this.close();
}
@HostListener('document:click', ['$event'])
onDocumentClick(event: MouseEvent): void {
if (event.button !== 2 && !this.isEventFromToggle(event)) {
this.close();
}
}
open(): void {
if (!this.internalOpen) {
this.internalOpen = true;
this.openChange.emit(true);
}
}
close(): void {
if (this.internalOpen) {
this.internalOpen = false;
this.openChange.emit(false);
}
}
toggle(): void {
if (this.isOpen) {
this.close();
} else {
this.open();
}
}
private isEventFromToggle(event: MouseEvent): boolean {
return !isNil(this.toggleElement) && this.toggleElement.contains(event.target);
}
}

View file

@ -1,26 +0,0 @@
import { Injectable } from '@angular/core';
@Injectable()
export class TreeviewConfig {
hasAllCheckBox = true;
hasFilter = false;
hasCollapseExpand = false;
decoupleChildFromParent = false;
maxHeight = 500;
get hasDivider(): boolean {
return this.hasFilter || this.hasAllCheckBox || this.hasCollapseExpand;
}
public static create(fields?: {
hasAllCheckBox?: boolean,
hasFilter?: boolean,
hasCollapseExpand?: boolean,
decoupleChildFromParent?: boolean
maxHeight?: number,
}): TreeviewConfig {
const config = new TreeviewConfig();
Object.assign(config, fields);
return config;
}
}

View file

@ -1,107 +0,0 @@
import { Injectable } from '@angular/core';
import { TreeviewItem } from './treeview-item';
import {TreeviewComponent} from './treeview.component';
import { isNil } from './utils';
@Injectable()
export abstract class TreeviewEventParser {
abstract getSelectedChange(component: TreeviewComponent): any[];
}
@Injectable()
export class DefaultTreeviewEventParser extends TreeviewEventParser {
getSelectedChange(component: TreeviewComponent): any[] {
const checkedItems = component.selection.checkedItems;
if (!isNil(checkedItems)) {
return checkedItems.map(item => item.value);
}
return [];
}
}
export interface DownlineTreeviewItem {
item: TreeviewItem;
parent: DownlineTreeviewItem;
}
@Injectable()
export class DownlineTreeviewEventParser extends TreeviewEventParser {
getSelectedChange(component: TreeviewComponent): any[] {
const items = component.items;
if (!isNil(items)) {
let result: DownlineTreeviewItem[] = [];
items.forEach(item => {
const links = this.getLinks(item, null);
if (!isNil(links)) {
result = result.concat(links);
}
});
return result;
}
return [];
}
private getLinks(item: TreeviewItem, parent: DownlineTreeviewItem): DownlineTreeviewItem[] {
if (!isNil(item.children)) {
const link = {
item,
parent
};
let result: DownlineTreeviewItem[] = [];
item.children.forEach(child => {
const links = this.getLinks(child, link);
if (!isNil(links)) {
result = result.concat(links);
}
});
return result;
}
if (item.checked) {
return [{
item,
parent
}];
}
return null;
}
}
@Injectable()
export class OrderDownlineTreeviewEventParser extends TreeviewEventParser {
private currentDownlines: DownlineTreeviewItem[] = [];
private parser = new DownlineTreeviewEventParser();
getSelectedChange(component: TreeviewComponent): any[] {
const newDownlines: DownlineTreeviewItem[] = this.parser.getSelectedChange(component);
if (this.currentDownlines.length === 0) {
this.currentDownlines = newDownlines;
} else {
const intersectDownlines: DownlineTreeviewItem[] = [];
this.currentDownlines.forEach(downline => {
let foundIndex = -1;
const length = newDownlines.length;
for (let i = 0; i < length; i++) {
if (downline.item.value === newDownlines[i].item.value) {
foundIndex = i;
break;
}
}
if (foundIndex !== -1) {
intersectDownlines.push(newDownlines[foundIndex]);
newDownlines.splice(foundIndex, 1);
}
});
this.currentDownlines = intersectDownlines.concat(newDownlines);
}
return this.currentDownlines;
}
}

View file

@ -1,10 +0,0 @@
import { TreeviewItem } from './treeview-item';
import { TreeviewConfig } from './treeview-config';
export interface TreeviewHeaderTemplateContext {
config: TreeviewConfig;
item: TreeviewItem;
onCollapseExpand: () => void;
onCheckedChange: (checked: boolean) => void;
onFilterTextChange: (text: string) => void;
}

View file

@ -1,94 +0,0 @@
import { TreeviewItem } from './treeview-item';
import { isNil, pull } from './utils';
export const TreeviewHelper = {
findItem,
findItemInList,
findParent,
removeItem,
concatSelection
};
function findItem(root: TreeviewItem, value: any): TreeviewItem {
if (isNil(root)) {
return undefined;
}
if (root.value === value) {
return root;
}
if (root.children) {
for (const child of root.children) {
const foundItem = findItem(child, value);
if (foundItem) {
return foundItem;
}
}
}
return undefined;
}
function findItemInList(list: TreeviewItem[], value: any): TreeviewItem {
if (isNil(list)) {
return undefined;
}
for (const item of list) {
const foundItem = findItem(item, value);
if (foundItem) {
return foundItem;
}
}
return undefined;
}
function findParent(root: TreeviewItem, item: TreeviewItem): TreeviewItem {
if (isNil(root) || isNil(root.children)) {
return undefined;
}
for (const child of root.children) {
if (child === item) {
return root;
} else {
const parent = findParent(child, item);
if (parent) {
return parent;
}
}
}
return undefined;
}
function removeItem(root: TreeviewItem, item: TreeviewItem): boolean {
const parent = findParent(root, item);
if (parent) {
pull(parent.children, item);
if (parent.children.length === 0) {
parent.children = undefined;
} else {
parent.correctChecked();
}
return true;
}
return false;
}
function concatSelection(items: TreeviewItem[], checked: TreeviewItem[], unchecked: TreeviewItem[]): { [k: string]: TreeviewItem[] } {
let checkedItems = [...checked];
let uncheckedItems = [...unchecked];
for (const item of items) {
const selection = item.getSelection();
checkedItems = checkedItems.concat(selection.checkedItems);
uncheckedItems = uncheckedItems.concat(selection.uncheckedItems);
}
return {
checked: checkedItems,
unchecked: uncheckedItems
};
}

View file

@ -1,49 +0,0 @@
import { Injectable } from '@angular/core';
import { TreeviewSelection } from './treeview-item';
@Injectable()
export abstract class TreeviewI18n {
abstract getText(selection: TreeviewSelection): string;
abstract getAllCheckboxText(): string;
abstract getFilterPlaceholder(): string;
abstract getFilterNoItemsFoundText(): string;
abstract getTooltipCollapseExpandText(isCollapse: boolean): string;
}
@Injectable()
export class DefaultTreeviewI18n extends TreeviewI18n {
getText(selection: TreeviewSelection): string {
if (selection.uncheckedItems.length === 0) {
if (selection.checkedItems.length > 0) {
return this.getAllCheckboxText();
} else {
return '';
}
}
switch (selection.checkedItems.length) {
case 0:
return 'Select options';
case 1:
return selection.checkedItems[0].text;
default:
return `${selection.checkedItems.length} options selected`;
}
}
getAllCheckboxText(): string {
return 'Все';
}
getFilterPlaceholder(): string {
return 'Поиск';
}
getFilterNoItemsFoundText(): string {
return 'Элементы не найдены';
}
getTooltipCollapseExpandText(isCollapse: boolean): string {
return isCollapse ? 'Развернуть' : 'Свернуть';
}
}

View file

@ -1,7 +0,0 @@
import { TreeviewItem } from './treeview-item';
export interface TreeviewItemTemplateContext {
item: TreeviewItem;
onCollapseExpand: () => void;
onCheckedChange: () => void;
}

View file

@ -1,68 +0,0 @@
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Input,
Output,
TemplateRef
} from '@angular/core';
import { TreeviewConfig } from './treeview-config';
import { TreeviewItem } from './treeview-item';
import { TreeviewItemTemplateContext } from './treeview-item-template-context';
import { isNil } from './utils';
@Component({
moduleId: module.id,
selector: 'ngx-treeview-item',
templateUrl: './../../../../../../src/resources/template/component/external/ngx-treeview/treeview-item.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TreeviewItemComponent {
@Input() config: TreeviewConfig;
@Input() template: TemplateRef<TreeviewItemTemplateContext>;
@Input() item: TreeviewItem;
@Output() checkedChange = new EventEmitter<boolean>();
constructor(
private defaultConfig: TreeviewConfig
) {
this.config = this.defaultConfig;
}
onCollapseExpand = () => {
this.item.collapsed = !this.item.collapsed;
}
onCheckedChange = () => {
const checked = this.item.checked;
if (!isNil(this.item.children) && !this.config.decoupleChildFromParent) {
this.item.children.forEach(child => child.setCheckedRecursive(checked));
}
this.checkedChange.emit(checked);
}
onChildCheckedChange(child: TreeviewItem, checked: boolean): void {
if (!this.config.decoupleChildFromParent) {
let itemChecked: boolean = null;
for (const childItem of this.item.children) {
if (itemChecked === null) {
itemChecked = childItem.checked;
} else if (itemChecked !== childItem.checked) {
itemChecked = undefined;
break;
}
}
if (itemChecked === null) {
itemChecked = false;
}
if (this.item.checked !== itemChecked) {
this.item.checked = itemChecked;
}
}
this.checkedChange.emit(checked);
}
}

View file

@ -1,185 +0,0 @@
import { TreeviewHelper } from './treeview-helper';
import { isBoolean, isNil, isString } from './utils';
export interface TreeviewSelection {
checkedItems: TreeviewItem[];
uncheckedItems: TreeviewItem[];
}
export interface TreeItem {
text: string;
value: any;
disabled?: boolean;
checked?: boolean;
collapsed?: boolean;
children?: TreeItem[];
}
export class TreeviewItem {
private internalDisabled = false;
private internalChecked = true;
private internalCollapsed = false;
private internalChildren: TreeviewItem[];
text: string;
value: any;
constructor(item: TreeItem, autoCorrectChecked = false) {
if (isNil(item)) {
throw new Error('Item must be defined');
}
if (isString(item.text)) {
this.text = item.text;
} else {
throw new Error('A text of item must be string object');
}
this.value = item.value;
if (isBoolean(item.checked)) {
this.checked = item.checked;
}
if (isBoolean(item.collapsed)) {
this.collapsed = item.collapsed;
}
if (isBoolean(item.disabled)) {
this.disabled = item.disabled;
}
if (!isNil(item.children) && item.children.length > 0) {
this.children = item.children.map(child => {
if (this.disabled === true) {
child.disabled = true;
}
return new TreeviewItem(child);
});
}
if (autoCorrectChecked) {
this.correctChecked();
}
}
get checked(): boolean {
return this.internalChecked;
}
set checked(value: boolean) {
if (!this.internalDisabled) {
if (this.internalChecked !== value) {
this.internalChecked = value;
}
}
}
get indeterminate(): boolean {
return this.checked === undefined;
}
setCheckedRecursive(value: boolean): void {
if (!this.internalDisabled) {
this.internalChecked = value;
if (!isNil(this.internalChildren)) {
this.internalChildren.forEach(child => child.setCheckedRecursive(value));
}
}
}
get disabled(): boolean {
return this.internalDisabled;
}
set disabled(value: boolean) {
if (this.internalDisabled !== value) {
this.internalDisabled = value;
if (!isNil(this.internalChildren)) {
this.internalChildren.forEach(child => child.disabled = value);
}
}
}
get collapsed(): boolean {
return this.internalCollapsed;
}
set collapsed(value: boolean) {
if (this.internalCollapsed !== value) {
this.internalCollapsed = value;
}
}
setCollapsedRecursive(value: boolean): void {
this.internalCollapsed = value;
if (!isNil(this.internalChildren)) {
this.internalChildren.forEach(child => child.setCollapsedRecursive(value));
}
}
get children(): TreeviewItem[] {
return this.internalChildren;
}
set children(value: TreeviewItem[]) {
if (this.internalChildren !== value) {
if (!isNil(value) && value.length === 0) {
throw new Error('Children must be not an empty array');
}
this.internalChildren = value;
if (!isNil(this.internalChildren)) {
let checked = null;
this.internalChildren.forEach(child => {
if (checked === null) {
checked = child.checked;
} else {
if (child.checked !== checked) {
checked = undefined;
return;
}
}
});
this.internalChecked = checked;
}
}
}
getSelection(): TreeviewSelection {
let checkedItems: TreeviewItem[] = [];
let uncheckedItems: TreeviewItem[] = [];
if (isNil(this.internalChildren)) {
if (this.internalChecked) {
checkedItems.push(this);
} else {
uncheckedItems.push(this);
}
} else {
const selection = TreeviewHelper.concatSelection(this.internalChildren, checkedItems, uncheckedItems);
checkedItems = selection.checked;
uncheckedItems = selection.unchecked;
}
return {
checkedItems,
uncheckedItems
};
}
correctChecked(): void {
this.internalChecked = this.getCorrectChecked();
}
private getCorrectChecked(): boolean {
let checked: boolean = null;
if (!isNil(this.internalChildren)) {
for (const child of this.internalChildren) {
child.internalChecked = child.getCorrectChecked();
if (checked === null) {
checked = child.internalChecked;
} else if (checked !== child.internalChecked) {
checked = undefined;
break;
}
}
} else {
checked = this.checked;
}
return checked;
}
}

View file

@ -1,242 +0,0 @@
import {
Component,
Input,
Output,
EventEmitter,
OnChanges,
OnInit,
SimpleChanges,
TemplateRef,
ChangeDetectionStrategy
} from '@angular/core';
import { TreeviewI18n } from './treeview-i18n';
import { TreeviewItem, TreeviewSelection } from './treeview-item';
import { TreeviewConfig } from './treeview-config';
import { TreeviewEventParser } from './treeview-event-parser';
import { TreeviewHeaderTemplateContext } from './treeview-header-template-context';
import { TreeviewItemTemplateContext } from './treeview-item-template-context';
import { TreeviewHelper } from './treeview-helper';
import { isNil } from './utils';
class FilterTreeviewItem extends TreeviewItem {
private readonly refItem: TreeviewItem;
constructor(item: TreeviewItem) {
super({
text: item.text,
value: item.value,
disabled: item.disabled,
checked: item.checked,
collapsed: item.collapsed,
children: item.children
});
this.refItem = item;
}
updateRefChecked(): void {
this.children.forEach(child => {
if (child instanceof FilterTreeviewItem) {
child.updateRefChecked();
}
});
let refChecked = this.checked;
if (refChecked) {
for (const refChild of this.refItem.children) {
if (!refChild.checked) {
refChecked = false;
break;
}
}
}
this.refItem.checked = refChecked;
}
}
@Component({
moduleId: module.id,
selector: 'ngx-treeview',
templateUrl: './../../../../../../src/resources/template/component/external/ngx-treeview/treeview.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TreeviewComponent implements OnChanges, OnInit {
@Input() headerTemplate: TemplateRef<TreeviewHeaderTemplateContext>;
@Input() itemTemplate: TemplateRef<TreeviewItemTemplateContext>;
@Input() items: TreeviewItem[];
@Input() config: TreeviewConfig;
@Output() selectedChange = new EventEmitter<any[]>();
@Output() filterChange = new EventEmitter<string>();
headerTemplateContext: TreeviewHeaderTemplateContext;
allItem: TreeviewItem;
filterText = '';
filterItems: TreeviewItem[];
selection: TreeviewSelection;
constructor(
public i18n: TreeviewI18n,
private defaultConfig: TreeviewConfig,
private eventParser: TreeviewEventParser
) {
this.config = this.defaultConfig;
this.allItem = new TreeviewItem({ text: 'All', value: undefined });
}
get hasFilterItems(): boolean {
return !isNil(this.filterItems) && this.filterItems.length > 0;
}
get maxHeight(): string {
return `${this.config.maxHeight}`;
}
ngOnInit(): void {
this.createHeaderTemplateContext();
this.generateSelection();
}
ngOnChanges(changes: SimpleChanges): void {
const itemsSimpleChange = changes.items;
if (!isNil(itemsSimpleChange) && !isNil(this.items)) {
this.updateFilterItems();
this.updateCollapsedOfAll();
this.raiseSelectedChange();
}
}
onAllCollapseExpand(): void {
this.allItem.collapsed = !this.allItem.collapsed;
this.filterItems.forEach(item => item.setCollapsedRecursive(this.allItem.collapsed));
}
onFilterTextChange(text: string): void {
this.filterText = text;
this.filterChange.emit(text);
this.updateFilterItems();
}
onAllCheckedChange(): void {
const checked = this.allItem.checked;
this.filterItems.forEach(item => {
item.setCheckedRecursive(checked);
if (item instanceof FilterTreeviewItem) {
item.updateRefChecked();
}
});
this.raiseSelectedChange();
}
onItemCheckedChange(item: TreeviewItem, checked: boolean): void {
if (item instanceof FilterTreeviewItem) {
item.updateRefChecked();
}
this.updateCheckedOfAll();
this.raiseSelectedChange();
}
raiseSelectedChange(): void {
this.generateSelection();
const values = this.eventParser.getSelectedChange(this);
setTimeout(() => {
this.selectedChange.emit(values);
});
}
private createHeaderTemplateContext(): void {
this.headerTemplateContext = {
config: this.config,
item: this.allItem,
onCheckedChange: () => this.onAllCheckedChange(),
onCollapseExpand: () => this.onAllCollapseExpand(),
onFilterTextChange: (text) => this.onFilterTextChange(text)
};
}
private generateSelection(): void {
let checkedItems: TreeviewItem[] = [];
let uncheckedItems: TreeviewItem[] = [];
if (!isNil(this.items)) {
const selection = TreeviewHelper.concatSelection(this.items, checkedItems, uncheckedItems);
checkedItems = selection.checked;
uncheckedItems = selection.unchecked;
}
this.selection = {
checkedItems,
uncheckedItems
};
}
private updateFilterItems(): void {
if (this.filterText !== '') {
const filterItems: TreeviewItem[] = [];
const filterText = this.filterText.toLowerCase();
this.items.forEach(item => {
const newItem = this.filterItem(item, filterText);
if (!isNil(newItem)) {
filterItems.push(newItem);
}
});
this.filterItems = filterItems;
} else {
this.filterItems = this.items;
}
this.updateCheckedOfAll();
}
private filterItem(item: TreeviewItem, filterText: string): TreeviewItem {
const isMatch = item.text.toLowerCase().includes(filterText);
if (isMatch) {
return item;
} else {
if (!isNil(item.children)) {
const children: TreeviewItem[] = [];
item.children.forEach(child => {
const newChild = this.filterItem(child, filterText);
if (!isNil(newChild)) {
children.push(newChild);
}
});
if (children.length > 0) {
const newItem = new FilterTreeviewItem(item);
newItem.collapsed = false;
newItem.children = children;
return newItem;
}
}
}
return undefined;
}
private updateCheckedOfAll(): void {
let itemChecked: boolean = null;
for (const filterItem of this.filterItems) {
if (itemChecked === null) {
itemChecked = filterItem.checked;
} else if (itemChecked !== filterItem.checked) {
itemChecked = undefined;
break;
}
}
if (itemChecked === null) {
itemChecked = false;
}
this.allItem.checked = itemChecked;
}
private updateCollapsedOfAll(): void {
let hasItemExpanded = false;
for (const filterItem of this.filterItems) {
if (!filterItem.collapsed) {
hasItemExpanded = true;
break;
}
}
this.allItem.collapsed = !hasItemExpanded;
}
}

View file

@ -1,48 +0,0 @@
import { NgModule, ModuleWithProviders } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { DropdownDirective } from './dropdown.directive';
import { DropdownMenuDirective } from './dropdown-menu.directive';
import { DropdownToggleDirective } from './dropdown-toggle.directive';
import { DropdownTreeviewComponent } from './dropdown-treeview.component';
import { TreeviewComponent } from './treeview.component';
import { TreeviewItemComponent } from './treeview-item.component';
import { TreeviewPipe } from './treeview.pipe';
import { TreeviewI18n, DefaultTreeviewI18n } from './treeview-i18n';
import { TreeviewConfig } from './treeview-config';
import { TreeviewEventParser, DefaultTreeviewEventParser } from './treeview-event-parser';
@NgModule({
imports: [
FormsModule,
CommonModule
],
declarations: [
// TreeviewComponent,
// TreeviewItemComponent,
TreeviewPipe,
DropdownDirective,
DropdownMenuDirective,
DropdownToggleDirective
// DropdownTreeviewComponent
], exports: [
// TreeviewComponent,
TreeviewPipe,
DropdownDirective,
DropdownMenuDirective,
DropdownToggleDirective
// DropdownTreeviewComponent
]
})
export class TreeviewModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: TreeviewModule,
providers: [
TreeviewConfig,
{ provide: TreeviewI18n, useClass: DefaultTreeviewI18n },
{ provide: TreeviewEventParser, useClass: DefaultTreeviewEventParser }
]
};
}
}

View file

@ -1,16 +0,0 @@
import { Pipe, PipeTransform } from '@angular/core';
import { TreeviewItem } from './treeview-item';
import { isNil } from './utils';
@Pipe({
name: 'ngxTreeview'
})
export class TreeviewPipe implements PipeTransform {
transform(objects: any[], textField: string): TreeviewItem[] {
if (isNil(objects)) {
return undefined;
}
return objects.map(object => new TreeviewItem({ text: object[textField], value: object }));
}
}

View file

@ -1,45 +0,0 @@
export default class Utils {
static isBoolean(value): boolean { return isBoolean(value); }
static isNil(value): boolean { return isNil(value); }
static isString(value): boolean { return isString(value); }
};
export const isBoolean = (value:any): boolean => {
return value === true || value === false; // todo not completed; to complete from 'lodash'
};
export const isNil = (value:any): boolean => {
// from 'lodash'
return value == null;
};
export const isString = (value:any): boolean => {
return typeof value == 'string'; // todo not completed; to complete from 'lodash'
};
export const pull = (array:any[], item:any): any[] => {
// modified 'remove' from 'lodash'
// mutate array
const result = [];
if (!(array && array.length)) {
return result;
}
const indexes = [];
array.forEach((value, index) => {
if (value === item) {
result.push(value);
indexes.push(index);
}
});
for (const index of indexes.reverse()) {
array.splice(index, 1);
}
return result;
};
export const reverse = (array:any[]): any[] => {
// from 'lodash'
return array == null ? array : array.reverse();
};

View file

@ -1,11 +1,20 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef} from "@angular/core";
import {Control, Event, UserService, Visible} from "@webbpm/base-package";
import {TreeItem, TreeviewItem} from "../external/ngx-treeview/lib/treeview-item";
import {
DropdownTreeviewSelectI18n
} from "../external/ngx-treeview/dropdown-treeview-select/dropdown-treeview-select-i18n";
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef
} from "@angular/core";
import {
Event,
InputControl,
UserService,
Visible
} from "@webbpm/base-package";
import {TreeItemDto} from "../../generated/component/model/TreeItemDto";
import {TreeItemRpcService} from "../../generated/component/rpc/TreeItemRpcService";
import {DropdownTreeviewSelectI18n} from "../external/ngx-treeview/dropdown-treeview-select/dropdown-treeview-select-i18n";
import {TreeItem, TreeviewItem} from "ngx-treeview";
@Component({
moduleId: module.id,
@ -16,7 +25,7 @@ import {TreeItemRpcService} from "../../generated/component/rpc/TreeItemRpcServi
}],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DropdownTreeViewComponent extends Control {
export class DropdownTreeViewComponent extends InputControl {
public collapseLevel: number;
public maxHeight: number;
@ -31,8 +40,7 @@ export class DropdownTreeViewComponent extends Control {
private rpcService: TreeItemRpcService;
constructor(el: ElementRef, cd: ChangeDetectorRef,
// todo replace UserService by another one from SUPPORT-8421 providing accountId or domainId
private i18n: DropdownTreeviewSelectI18n, private userService: UserService) {
private i18n: DropdownTreeviewSelectI18n) {
super(el, cd);
}
@ -44,14 +52,12 @@ export class DropdownTreeViewComponent extends Control {
@Visible()
public loadTreeItems(): void {
// todo replace the called method by
// this.rpcService.loadTreeDataByDomainId(domainId)
this.rpcService.loadTreeData()
.then((res: TreeItemDto[]) => {
this.items = res.map(value => new TreeviewItem(this.createTreeItem(value)));
const rootItem = this.items[0];
this.i18n.selectedItem = rootItem;
this.value = rootItem.value;
this.value = rootItem ? rootItem.value : rootItem;
this.doCollapseLevel();
this.cd.markForCheck();
});
@ -78,6 +84,7 @@ export class DropdownTreeViewComponent extends Control {
public onValueChange($event: any) {
this.valueChangeEvent.trigger($event);
this.applyListener(this.changeListeners);
}
@Visible()
@ -104,4 +111,34 @@ export class DropdownTreeViewComponent extends Control {
viewItem.children.forEach((value) => this.checkCollapseLevelRecursive(value, level + 1))
}
}
getPresentationValue(): string | number | boolean {
return this.value;
}
getValue(): any {
return this.value;
}
getValueAsModel(): any {
return this.value ? this.value : null;
}
setValue(value: any): any {
this.value = value;
}
onChange() {
super.onChange();
this.valueChangeEvent.trigger(this.value);
}
subscribeToModelChange() {
//empty because there is no ngModel here
}
unsubscribeToModelChange() {
//empty because there is no ngModel here
}
}

View file

@ -0,0 +1,21 @@
import {AnalyticalScope, Filter, FilterComponent, FilterUtil} from "@webbpm/base-package";
import {DropdownTreeViewComponent} from "../../../component/field/DropdownTreeViewComponent";
@AnalyticalScope(DropdownTreeViewComponent)
export class DropdownTreeViewFilterComponent extends FilterComponent {
public isBusinessId: boolean;
public getFilter(): Filter {
let treeViewComponent = this.getScript(DropdownTreeViewComponent) as DropdownTreeViewComponent;
let model = treeViewComponent.value;
if (model) {
let value = this.isBusinessId ? model.businessId : model.id;
return FilterUtil.singleValueFilter(this.getObjectId(), value, this.operation);
}
else {
return null;
}
}
}

View file

@ -23,10 +23,7 @@ import {FilterContainer} from "../../ervu_business_metrics/component/filter/Filt
import {TokenInterceptor} from "./interceptor/token-interceptor.service";
import {DropdownTreeViewComponent} from "../../component/field/DropdownTreeViewComponent";
import {DropdownTreeviewSelectComponent} from "../../component/external/ngx-treeview/dropdown-treeview-select/dropdown-treeview-select.component";
import {DropdownTreeviewComponent} from "../../component/external/ngx-treeview/lib/dropdown-treeview.component";
import {TreeviewModule} from '../../component/external/ngx-treeview/lib/treeview.module';
import {TreeviewComponent} from "../../component/external/ngx-treeview/lib/treeview.component";
import {TreeviewItemComponent} from "../../component/external/ngx-treeview/lib/treeview-item.component";
import {TreeviewModule} from "ngx-treeview";
registerLocaleData(localeRu);
export const DIRECTIVES = [
@ -37,10 +34,7 @@ export const DIRECTIVES = [
forwardRef(() => ErvuChartV2),
forwardRef(() => FilterContainer),
forwardRef(() => DropdownTreeViewComponent),
forwardRef(() => DropdownTreeviewComponent),
forwardRef(() => DropdownTreeviewSelectComponent),
forwardRef(() => TreeviewComponent),
forwardRef(() => TreeviewItemComponent)
forwardRef(() => DropdownTreeviewSelectComponent)
];
@NgModule({

View file

@ -62,7 +62,8 @@
'chartjs-adapter-moment': 'npm:chartjs-adapter-moment/dist/chartjs-adapter-moment.js',
'tslib': 'npm:tslib/tslib.js',
'ngx-international-phone-number': 'npm:ngx-international-phone-number/ngx-international-phone-number.umd.js',
'google-libphonenumber': 'npm:google-libphonenumber/dist/libphonenumber.js'
'google-libphonenumber': 'npm:google-libphonenumber/dist/libphonenumber.js',
'ngx-treeview': 'npm:ngx-treeview'
},
packages: {
'webbpm': { main: 'main', defaultExtension: 'js'},
@ -87,7 +88,8 @@
'inputmask': {
main: 'dist/inputmask.js',
defaultExtension: 'js'
}
},
'ngx-treeview': { main: "bundles/ngx-treeview.umd.js", defaultExtension: 'js'}
}
});
})(this);

View file

@ -62,7 +62,8 @@
'chartjs-adapter-moment': 'npm:chartjs-adapter-moment/dist/chartjs-adapter-moment.js',
'tslib': 'npm:tslib/tslib.js',
'ngx-international-phone-number': 'npm:ngx-international-phone-number/ngx-international-phone-number.umd.js',
'google-libphonenumber': 'npm:google-libphonenumber/dist/libphonenumber.js'
'google-libphonenumber': 'npm:google-libphonenumber/dist/libphonenumber.js',
'ng2-dropdown-treeview': 'npm:ng2-dropdown-treeview'
},
packages: {
'preview': { main: './modules/preview/preview.main', defaultExtension: 'js'},
@ -86,7 +87,8 @@
'inputmask': {
main: 'dist/inputmask.js',
defaultExtension: 'js'
}
},
'ng2-dropdown-treeview':{ main: "bundles/ng2-dropdown-treeview.umd.js", defaultExtension: 'js'}
}
});
})(this);

View file

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xmlComponent>
<id>236ea5f5-8f3b-4266-9395-e7062180423d</id>
<name>FilterDropdownTreeView</name>
<category>filters</category>
<internal>false</internal>
<versions>
<studioVersion>3.177.3</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.177.3</value>
</entry>
</packageVersions>
</versions>
<rootObject id="97aef2b8-52a1-47f4-a930-9b0bce6994f9">
<name>FilterDropdownTreeView</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="977e4959-7aa0-4530-b424-74bb5d24c6f7">
<classRef type="TS">
<className>DropdownTreeViewComponent</className>
<packageName>component.field</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
</scripts>
<scripts id="7f107b73-51a9-4dd0-a277-9315aa09cea9">
<classRef type="TS">
<className>FilterComponent</className>
<packageName>component.filter</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
</scripts>
</rootObject>
</xmlComponent>

View file

@ -3002,12 +3002,165 @@
</children>
</children>
</children>
<children id="9f9c5c62-0ba7-447e-8d37-3eafef87fca1">
<prototypeId>d7d54cfb-26b5-4dba-b56f-b6247183c24d</prototypeId>
<componentRootId>9f9c5c62-0ba7-447e-8d37-3eafef87fca1</componentRootId>
<name>Горизонтальный контейнер новый</name>
<container>true</container>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
<scripts id="b6068710-0f31-48ec-8e03-c0c1480a40c0">
<properties>
<entry>
<key>service</key>
<value>
<implRef/>
</value>
</entry>
</properties>
</scripts>
<scripts id="fe04d7fb-6c5b-46c4-b723-667732d81f4f"/>
<scripts id="5c566210-2a60-4048-a2d1-84c7dd023248"/>
<scripts id="3171b2e1-b4af-4335-95fa-1b2592604b84"/>
<children id="4865ca95-3ec1-4b4a-8179-b70261e79e69">
<prototypeId>b310f98a-69c6-4e7b-8cdb-f1ab9f9c0d94</prototypeId>
<componentRootId>4865ca95-3ec1-4b4a-8179-b70261e79e69</componentRootId>
<name>Выпадающий список Причины</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="682038cd-2f2f-447c-b219-faec471b2077">
<prototypeId>97aef2b8-52a1-47f4-a930-9b0bce6994f9</prototypeId>
<componentRootId>682038cd-2f2f-447c-b219-faec471b2077</componentRootId>
<name>FilterDropdownTreeView</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="f4c97cf8-64a1-497e-9ffb-75a39c6f9b0e">
<prototypeId>630accd8-3776-4fc1-a87c-3a0f05274678</prototypeId>
<componentRootId>f4c97cf8-64a1-497e-9ffb-75a39c6f9b0e</componentRootId>
<name>DropdownTreeView</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="e1d0d895-79ee-4230-a47c-e4086b8dbfdb">
<properties>
<entry>
<key>collapseLevel</key>
<value>
<simple>2.0</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="bba43bd6-32d0-4039-bd0c-2d70871a2649">
<properties>
<entry>
<key>treeItemService</key>
<value>
<complex>
<entry>
<key>businessIdColumn</key>
<value>
<simple>{"schema":"summonses_list","table":"summonses_reason","entity":"summonses_reason","name":"code"}</simple>
</value>
</entry>
<entry>
<key>idColumn</key>
<value>
<simple>{"schema":"summonses_list","table":"summonses_reason","entity":"summonses_reason","name":"summonses_reason_id"}</simple>
</value>
</entry>
<entry>
<key>labelColumn</key>
<value>
<simple>{"schema":"summonses_list","table":"summonses_reason","entity":"summonses_reason","name":"name"}</simple>
</value>
</entry>
<entry>
<key>loadDao</key>
<value>
<complex>
<entry>
<key>graph</key>
<value>
<simple>{"conditionGroup":{"operator":"AND","conditions":[],"groups":[]},"nodeByIndex":{"0":{"tableName":"summonses_reason","schemaName":"summonses_list","x":390.0,"y":241.0,"alias":"summonses_reason","conditionGroup":{"operator":"AND","conditions":[],"groups":[]},"emptyEntityAction":"IGNORE_OR_DELETE"}},"nodes":[{"tableName":"summonses_reason","schemaName":"summonses_list","x":390.0,"y":241.0,"alias":"summonses_reason","conditionGroup":{"operator":"AND","conditions":[],"groups":[]},"emptyEntityAction":"IGNORE_OR_DELETE"}],"nodeByEntityName":{"summonses_reason":{"tableName":"summonses_reason","schemaName":"summonses_list","x":390.0,"y":241.0,"alias":"summonses_reason","conditionGroup":{"operator":"AND","conditions":[],"groups":[]},"emptyEntityAction":"IGNORE_OR_DELETE"}},"matrix":[[null]],"mainNodeIndex":0}</simple>
</value>
</entry>
</complex>
</value>
</entry>
<entry>
<key>parentIdColumn</key>
<value>
<simple>{"schema":"summonses_list","table":"summonses_reason","entity":"summonses_reason","name":"parent_summonses_reason_id"}</simple>
</value>
</entry>
<entry>
<key>sortColumn</key>
<value>
<simple>{"schema":"summonses_list","table":"summonses_reason","entity":"summonses_reason","name":"summonses_reason_id"}</simple>
</value>
</entry>
<entry>
<key>sortOrder</key>
<value>
<simple>"ASC"</simple>
</value>
</entry>
</complex>
</value>
</entry>
</properties>
</scripts>
<scripts id="3c7faf24-c429-4b32-941a-8fcb931b36ac">
<classRef type="TS">
<className>FilterComponent</className>
<packageName>component.filter</packageName>
</classRef>
</scripts>
<scripts id="8e3e86de-54c2-48c5-b105-28ecb59d845e">
<classRef type="TS">
<className>DropdownTreeViewFilterComponent</className>
<packageName>ervu_business_metrics.component.filter</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>isBusinessId</key>
<value>
<simple>true</simple>
</value>
</entry>
<entry>
<key>operation</key>
<value>
<simple>"EQUAL"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="ad7fdcc3-0e9e-47d1-bbed-536c1db97b4a">
<prototypeId>97aef2b8-52a1-47f4-a930-9b0bce6994f9</prototypeId>
<componentRootId>ad7fdcc3-0e9e-47d1-bbed-536c1db97b4a</componentRootId>
<name>FilterDropdownTreeView</name>
<container>false</container>
<removed>true</removed>
</children>
</children>
<children id="4865ca95-3ec1-4b4a-8179-b70261e79e69">
<prototypeId>b310f98a-69c6-4e7b-8cdb-f1ab9f9c0d94</prototypeId>
<componentRootId>4865ca95-3ec1-4b4a-8179-b70261e79e69</componentRootId>
<name>Выпадающий список РФ</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="18ecb7c6-2148-4eb0-a18c-97af5e73cac7">
<prototypeId>d7d54cfb-26b5-4dba-b56f-b6247183c24d</prototypeId>
<componentRootId>18ecb7c6-2148-4eb0-a18c-97af5e73cac7</componentRootId>
<name>ГК Второй ряд</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -3032,7 +3185,6 @@
<componentRootId>e1256afb-de60-497e-9f3f-2e242baffa07</componentRootId>
<name>Vbox_50%</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -3121,6 +3273,7 @@
</complex>
</value>
</item>
<item id="e7c93b8d-4615-42f0-9ea3-d6bfe5821855" removed="true"/>
</value>
</entry>
<entry>
@ -3157,6 +3310,7 @@
</complex>
</value>
</item>
<item id="44abcd97-d239-4ea7-b4f4-2e68d45f1b1f" removed="true"/>
</value>
</entry>
</properties>
@ -9073,7 +9227,6 @@
<componentRootId>8ccf3815-9f26-4892-9c46-f78f72f8b019</componentRootId>
<name>ГК Четвертый ряд</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>