Skip to content

Commit cc9936d

Browse files
authored
feat(filter-bar): tree-select pipe (#DS-3529) (#794)
1 parent dce78cc commit cc9936d

23 files changed

Lines changed: 1179 additions & 53 deletions

packages/components-dev/filter-bar/module.ts

Lines changed: 193 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { KbqButtonModule } from '@koobiq/components/button';
1313
import { DateAdapter } from '@koobiq/components/core';
1414
import { KbqDividerModule } from '@koobiq/components/divider';
1515
import {
16+
kbqBuildTree,
1617
KbqFilter,
1718
KbqFilterBar,
1819
KbqFilterBarModule,
@@ -25,6 +26,25 @@ import { DateTime } from 'luxon';
2526
import { FilterBarExamplesModule } from '../../docs-examples/components/filter-bar';
2627
import { DevLocaleSelector } from '../locale-selector';
2728

29+
const DEV_DATA_OBJECT = {
30+
'No roles': 'value 0',
31+
'Management and Configuration': {
32+
Administrator: { value: 'value 1' },
33+
Operator: 'value 2',
34+
User: 'value 3'
35+
},
36+
'MP 10': {
37+
Administrator: 'value 4',
38+
Operator: 'value 5',
39+
User: 'value 6'
40+
},
41+
'Knowledge Base': {
42+
Administrator: 'value 7',
43+
Operator: 'value 8',
44+
User: 'value 9'
45+
}
46+
};
47+
2848
@Component({
2949
standalone: true,
3050
imports: [FilterBarExamplesModule],
@@ -104,7 +124,7 @@ export class DevApp implements AfterViewInit {
104124

105125
filters: KbqFilter[] = [
106126
{
107-
name: 'Select',
127+
name: 'select',
108128
readonly: false,
109129
disabled: false,
110130
changed: false,
@@ -158,6 +178,141 @@ export class DevApp implements AfterViewInit {
158178
}
159179
]
160180
},
181+
{
182+
name: 'tree-select',
183+
readonly: false,
184+
disabled: false,
185+
changed: false,
186+
saved: false,
187+
pipes: [
188+
{
189+
name: 'required',
190+
id: 'TreeSelect',
191+
// required - не может быть пустым, всегда есть дефолтное значение
192+
value: 'value 0',
193+
type: KbqPipeTypes.TreeSelect,
194+
195+
search: true,
196+
197+
cleanable: false,
198+
removable: false,
199+
disabled: false
200+
},
201+
{
202+
name: 'empty',
203+
id: 'TreeSelect',
204+
type: KbqPipeTypes.TreeSelect,
205+
value: null,
206+
207+
cleanable: true,
208+
removable: false,
209+
disabled: false
210+
},
211+
{
212+
name: 'cleanable',
213+
id: 'TreeSelect',
214+
value: { name: 'Не определен', id: '1' },
215+
type: KbqPipeTypes.TreeSelect,
216+
217+
search: true,
218+
219+
cleanable: true,
220+
removable: false,
221+
disabled: false
222+
},
223+
{
224+
name: 'removable',
225+
id: 'TreeSelect',
226+
value: { name: 'Не определен', id: '1' },
227+
type: KbqPipeTypes.TreeSelect,
228+
229+
search: true,
230+
231+
cleanable: false,
232+
removable: true,
233+
disabled: false
234+
},
235+
{
236+
name: 'disabled',
237+
id: 'TreeSelect',
238+
value: { name: 'Не определен', id: '1' },
239+
type: KbqPipeTypes.TreeSelect,
240+
241+
cleanable: false,
242+
removable: false,
243+
disabled: true
244+
}
245+
]
246+
},
247+
{
248+
name: 'multi-tree-select',
249+
readonly: false,
250+
disabled: false,
251+
changed: false,
252+
saved: false,
253+
pipes: [
254+
{
255+
name: 'required',
256+
id: 'MultiTreeSelect',
257+
// required - не может быть пустым, всегда есть дефолтное значение
258+
value: ['value 0'],
259+
type: KbqPipeTypes.MultiTreeSelect,
260+
261+
search: true,
262+
selectAll: true,
263+
264+
cleanable: false,
265+
removable: false,
266+
disabled: false
267+
},
268+
{
269+
name: 'empty',
270+
id: 'MultiTreeSelect',
271+
type: KbqPipeTypes.MultiTreeSelect,
272+
value: null,
273+
274+
cleanable: true,
275+
removable: false,
276+
disabled: false
277+
},
278+
{
279+
name: 'cleanable',
280+
id: 'MultiTreeSelect',
281+
value: ['value 2', 'value 3'],
282+
type: KbqPipeTypes.MultiTreeSelect,
283+
284+
search: true,
285+
selectAll: true,
286+
287+
cleanable: true,
288+
removable: false,
289+
disabled: false
290+
},
291+
{
292+
name: 'removable',
293+
id: 'MultiTreeSelect',
294+
value: [],
295+
type: KbqPipeTypes.MultiTreeSelect,
296+
297+
search: true,
298+
selectAll: true,
299+
300+
cleanable: false,
301+
removable: true,
302+
disabled: false
303+
},
304+
{
305+
name: 'disabled',
306+
id: 'MultiTreeSelect',
307+
value: [],
308+
type: KbqPipeTypes.MultiTreeSelect,
309+
310+
cleanable: false,
311+
removable: false,
312+
disabled: true
313+
}
314+
]
315+
},
161316
{
162317
name: 'MultiSelect',
163318
readonly: false,
@@ -586,7 +741,7 @@ export class DevApp implements AfterViewInit {
586741
}
587742
];
588743
pipeTemplates: KbqPipeTemplate[];
589-
defaultFilter: KbqFilter | null = this.filters[1];
744+
defaultFilter: KbqFilter | null = this.filters[2];
590745
activeFilter: KbqFilter | null = this.defaultFilter;
591746

592747
ngAfterViewInit(): void {
@@ -607,51 +762,66 @@ export class DevApp implements AfterViewInit {
607762
{ name: 'Option 10', id: '10', type: 'error' }
608763
],
609764
valueTemplate: this.optionTemplate,
610-
search: true,
611765

612766
cleanable: false,
613767
removable: false,
614768
disabled: false
615769
},
616770
{
617-
name: 'mySelect',
618-
type: KbqPipeTypes.Select,
619-
id: 'mySelect',
771+
name: 'MultiSelect',
772+
id: 'MultiSelect',
773+
type: KbqPipeTypes.MultiSelect,
620774
values: [
621775
{ name: 'Option 1', id: '1', type: 'error' },
622776
{ name: 'Option 2', id: '2', type: 'warning' },
623777
{ name: 'Option 3', id: '3', type: 'success' },
624778
{ name: 'Option 4', id: '4', type: 'error' },
625-
{ name: 'Option 5', id: '5', type: 'warning' }
779+
{ name: 'Option 5', id: '5', type: 'warning' },
780+
{ name: 'Option 6', id: '6', type: 'success' },
781+
{ name: 'Option 7', id: '7', type: 'error' },
782+
{ name: 'Option 8', id: '8', type: 'warning' },
783+
{ name: 'Option 9', id: '9', type: 'success' },
784+
{ name: 'Option 10', id: '10', type: 'error' }
626785
],
627786
valueTemplate: this.optionTemplate,
628-
search: true,
787+
788+
cleanable: false,
789+
removable: true,
790+
disabled: false
791+
},
792+
{
793+
name: 'TreeSelect',
794+
type: KbqPipeTypes.TreeSelect,
795+
values: kbqBuildTree(DEV_DATA_OBJECT, 0),
629796

630797
cleanable: false,
631798
removable: false,
632799
disabled: false
633800
},
634801
{
635-
name: 'MultiSelect',
636-
id: 'MultiSelect',
637-
type: KbqPipeTypes.MultiSelect,
802+
name: 'MultiTreeSelect',
803+
type: KbqPipeTypes.MultiTreeSelect,
804+
values: kbqBuildTree(DEV_DATA_OBJECT, 0),
805+
806+
cleanable: false,
807+
removable: false,
808+
disabled: false
809+
},
810+
{
811+
name: 'mySelect',
812+
type: KbqPipeTypes.Select,
813+
id: 'mySelect',
638814
values: [
639815
{ name: 'Option 1', id: '1', type: 'error' },
640816
{ name: 'Option 2', id: '2', type: 'warning' },
641817
{ name: 'Option 3', id: '3', type: 'success' },
642818
{ name: 'Option 4', id: '4', type: 'error' },
643-
{ name: 'Option 5', id: '5', type: 'warning' },
644-
{ name: 'Option 6', id: '6', type: 'success' },
645-
{ name: 'Option 7', id: '7', type: 'error' },
646-
{ name: 'Option 8', id: '8', type: 'warning' },
647-
{ name: 'Option 9', id: '9', type: 'success' },
648-
{ name: 'Option 10', id: '10', type: 'error' }
819+
{ name: 'Option 5', id: '5', type: 'warning' }
649820
],
650821
valueTemplate: this.optionTemplate,
651-
search: true,
652822

653823
cleanable: false,
654-
removable: true,
824+
removable: false,
655825
disabled: false
656826
},
657827
{
@@ -666,7 +836,6 @@ export class DevApp implements AfterViewInit {
666836
{ name: 'Option 5', id: '5', type: 'warning' }
667837
],
668838
valueTemplate: this.optionTemplate,
669-
search: true,
670839

671840
cleanable: false,
672841
removable: true,
@@ -762,6 +931,10 @@ export class DevApp implements AfterViewInit {
762931
console.log('onFilterChange: ', filter);
763932
}
764933

934+
onPipeChange(pipe: KbqPipe | null) {
935+
console.log('onPipeChange: ', pipe);
936+
}
937+
765938
onSelectFilter(filter: KbqFilter) {
766939
console.log('onSelectFilter: ', filter);
767940

packages/components-dev/filter-bar/template.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
[pipeTemplates]="pipeTemplates"
1111
[(filter)]="activeFilter"
1212
(filterChange)="onFilterChange($event)"
13+
(onChangePipe)="onPipeChange($event)"
1314
(onClosePipe)="onClosePipe($event)"
1415
>
1516
<kbq-filters

packages/components/filter-bar/filter-bar.karma-spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,6 @@ describe('KbqFilterBar', () => {
305305
expect(required.nativeElement.classList).not.toContain('kbq-pipe_cleanable');
306306
expect(required.nativeElement.classList).not.toContain('kbq-pipe_removable');
307307
expect(required.nativeElement.classList).not.toContain('kbq-pipe_disabled');
308-
expect(required.nativeElement.classList).not.toContain('kbq-pipe_empty');
309308
});
310309

311310
it('MultiSelect: state empty', () => {

packages/components/filter-bar/filter-bar.types.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { KbqFilterBar } from './filter-bar';
44
import { KbqPipeDateComponent } from './pipes/pipe-date';
55
import { KbqPipeDatetimeComponent } from './pipes/pipe-datetime';
66
import { KbqPipeMultiSelectComponent } from './pipes/pipe-multi-select';
7+
import { KbqPipeMultiTreeSelectComponent } from './pipes/pipe-multi-tree-select';
78
import { KbqPipeReadonlyComponent } from './pipes/pipe-readonly';
89
import { KbqPipeSelectComponent } from './pipes/pipe-select';
910
import { KbqPipeTextComponent } from './pipes/pipe-text';
11+
import { KbqPipeTreeSelectComponent } from './pipes/pipe-tree-select';
1012

1113
/**default configuration of filter-bar */
1214
export const KBQ_FILTER_BAR_DEFAULT_CONFIGURATION = ruRULocaleData.filterBar;
@@ -30,7 +32,9 @@ export enum KbqPipeTypes {
3032
ReadOnly = 'readonly',
3133
Text = 'text',
3234
Select = 'select',
35+
TreeSelect = 'tree-select',
3336
MultiSelect = 'multiselect',
37+
MultiTreeSelect = 'multi-tree-select',
3438
Date = 'date',
3539
Datetime = 'datetime'
3640
}
@@ -42,7 +46,9 @@ export const defaultFilterBarPipes: [string, unknown][] = [
4246
[KbqPipeTypes.ReadOnly, KbqPipeReadonlyComponent],
4347
[KbqPipeTypes.Text, KbqPipeTextComponent],
4448
[KbqPipeTypes.Select, KbqPipeSelectComponent],
49+
[KbqPipeTypes.TreeSelect, KbqPipeTreeSelectComponent],
4550
[KbqPipeTypes.MultiSelect, KbqPipeMultiSelectComponent],
51+
[KbqPipeTypes.MultiTreeSelect, KbqPipeMultiTreeSelectComponent],
4652
[KbqPipeTypes.Date, KbqPipeDateComponent],
4753
[KbqPipeTypes.Datetime, KbqPipeDatetimeComponent]
4854

@@ -116,3 +122,36 @@ export interface KbqSaveFilterEvent {
116122
filterBar: KbqFilterBar;
117123
status?: KbqSaveFilterStatuses;
118124
}
125+
126+
/** Flattened node */
127+
export class KbqTreeSelectFlatNode {
128+
name: string;
129+
value: unknown;
130+
level: number;
131+
expandable: boolean;
132+
parent?: KbqTreeSelectFlatNode;
133+
}
134+
135+
export interface KbqTreeSelectNode {
136+
children: KbqTreeSelectNode[] | null;
137+
name: string;
138+
value: unknown;
139+
}
140+
141+
/**
142+
* Build the file structure tree. The `value` is the Json object, or a sub-tree of a Json object.
143+
* The return value is the list of extends `KbqTreeSelectNode`.
144+
*/
145+
export function kbqBuildTree<T extends KbqTreeSelectNode>(value: any, level: number): T[] {
146+
const data: T[] = [];
147+
148+
for (const [k, v] of Object.entries(value)) {
149+
data.push({
150+
name: k.toString(),
151+
value: v,
152+
children: typeof v === 'object' && v && !v['value'] ? kbqBuildTree(v, level + 1) : null
153+
} as T);
154+
}
155+
156+
return data;
157+
}

packages/components/filter-bar/pipes/base-pipe.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
}
4848

4949
.kbq-select:not(:hover, .kbq-active),
50+
.kbq-tree-select:not(:hover, .kbq-active),
5051
.kbq-button:not(:hover, .kbq-active, [disabled]) {
5152
& + .kbq-pipe-button:not(:hover, .kbq-active)::after {
5253
content: '';

0 commit comments

Comments
 (0)