adding data browser
This commit is contained in:
@@ -3,5 +3,6 @@ import { ChartAxis } from './chart-type';
|
||||
|
||||
export interface ChartConfig {
|
||||
type: ChartType;
|
||||
title?: string;
|
||||
axis: ChartAxis[];
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ export interface IDataset {
|
||||
xAxisKey?: string;
|
||||
yAxisKey?: string;
|
||||
};
|
||||
spanGaps?: boolean;
|
||||
stack?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
<canvas #chart></canvas>
|
||||
<canvas #chart class="fit"></canvas>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.fit {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ export class DatasetService {
|
||||
if (axis.fill) {
|
||||
entry.fill = axis.fill;
|
||||
}
|
||||
entry.spanGaps = true;
|
||||
result.push(entry);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,11 +15,18 @@ export class OptionsService {
|
||||
result.maintainAspectRatio = false;
|
||||
result.scales = this.scaleService.scales(chart, data);
|
||||
|
||||
if (chart.title) {
|
||||
result.plugins = result.plugins ?? {};
|
||||
result.plugins.title = result.plugins.title ?? {};
|
||||
result.plugins.title.text = chart.title;
|
||||
}
|
||||
|
||||
const fill =
|
||||
(chart?.axis ?? []).filter((axis: ChartAxis) => axis.fill).length > 0;
|
||||
if (fill) {
|
||||
result.plugins = result.plugins ?? {};
|
||||
result.plugins.filler = { propagate: true };
|
||||
result.plugins.filler = result.plugins.filler ?? {};
|
||||
result.plugins.filler.propagate = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
1
src/components/context-menu/context-menu.component.html
Normal file
1
src/components/context-menu/context-menu.component.html
Normal file
@@ -0,0 +1 @@
|
||||
<p>context-menu works!</p>
|
||||
23
src/components/context-menu/context-menu.component.spec.ts
Normal file
23
src/components/context-menu/context-menu.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ContextMenuComponent } from './context-menu.component';
|
||||
|
||||
describe('ContextMenuComponent', () => {
|
||||
let component: ContextMenuComponent;
|
||||
let fixture: ComponentFixture<ContextMenuComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ContextMenuComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ContextMenuComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
12
src/components/context-menu/context-menu.component.ts
Normal file
12
src/components/context-menu/context-menu.component.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'fbi-context-menu',
|
||||
standalone: true,
|
||||
imports: [],
|
||||
templateUrl: './context-menu.component.html',
|
||||
styleUrl: './context-menu.component.scss'
|
||||
})
|
||||
export class ContextMenuComponent {
|
||||
|
||||
}
|
||||
6
src/components/metadata/metadata.component.html
Normal file
6
src/components/metadata/metadata.component.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<div class="header">Fields!</div>
|
||||
<fbi-tree
|
||||
class="tree"
|
||||
[node]="node"
|
||||
(actionClick)="onActionClick($event)"
|
||||
></fbi-tree>
|
||||
17
src/components/metadata/metadata.component.scss
Normal file
17
src/components/metadata/metadata.component.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
border: 1px solid black;
|
||||
padding-left: 4px;
|
||||
background-color: lightgray;
|
||||
}
|
||||
|
||||
.tree {
|
||||
flex: 1;
|
||||
overflow: scroll;
|
||||
}
|
||||
23
src/components/metadata/metadata.component.spec.ts
Normal file
23
src/components/metadata/metadata.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MetadataComponent } from './metadata.component';
|
||||
|
||||
describe('MetadataComponent', () => {
|
||||
let component: MetadataComponent;
|
||||
let fixture: ComponentFixture<MetadataComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MetadataComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(MetadataComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
75
src/components/metadata/metadata.component.ts
Normal file
75
src/components/metadata/metadata.component.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { TreeComponent } from '../tree/tree.component';
|
||||
import { TreeNode } from '../../models/tree-node';
|
||||
import { MetaService } from '../../services/meta.service';
|
||||
import { Action } from '../../models/action';
|
||||
import { faAdd } from '@fortawesome/free-solid-svg-icons';
|
||||
import { QueryService } from '../../services/query.service';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { Query } from '../../models/query';
|
||||
|
||||
@Component({
|
||||
selector: 'fbi-metadata',
|
||||
standalone: true,
|
||||
imports: [TreeComponent],
|
||||
templateUrl: './metadata.component.html',
|
||||
styleUrl: './metadata.component.scss',
|
||||
})
|
||||
export class MetadataComponent {
|
||||
node: TreeNode = new TreeNode({});
|
||||
|
||||
constructor(metaService: MetaService, private queryService: QueryService) {
|
||||
combineLatest({
|
||||
meta: metaService.Data,
|
||||
query: queryService.Query,
|
||||
}).subscribe((d: { meta: Partial<TreeNode>[]; query: Query }) => {
|
||||
const inuse = d.query.fields;
|
||||
const expanded = this.getExpanded(this.node);
|
||||
|
||||
const recurse = (node: Partial<TreeNode>) => {
|
||||
node.hidden = inuse.includes(node.data);
|
||||
node.expanded = expanded.includes(node.data);
|
||||
|
||||
const children = node.children ?? [];
|
||||
children.forEach((child: Partial<TreeNode>) => recurse(child));
|
||||
if (children.length === 0) {
|
||||
const actions = [{ label: 'Add', icon: faAdd, data: ACTIONS.ADD }];
|
||||
node.actions = actions as Action[];
|
||||
}
|
||||
};
|
||||
|
||||
(d.meta ?? []).forEach((node: Partial<TreeNode>) => recurse(node));
|
||||
|
||||
this.node = new TreeNode({
|
||||
hidden: true,
|
||||
expanded: true,
|
||||
children: d.meta as TreeNode[],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onActionClick(event: { action: Action; node: TreeNode }): void {
|
||||
switch (event.action.data) {
|
||||
case ACTIONS.ADD:
|
||||
this.queryService.add(event.node.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private getExpanded(node: TreeNode): string[] {
|
||||
const result: string[] = [];
|
||||
|
||||
if (node.expanded) {
|
||||
result.push(node.data);
|
||||
}
|
||||
(node.children ?? []).forEach((child: TreeNode) =>
|
||||
result.push(...this.getExpanded(child))
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
enum ACTIONS {
|
||||
ADD = 'add',
|
||||
}
|
||||
6
src/components/query/query.component.html
Normal file
6
src/components/query/query.component.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<div class="header">Query!</div>
|
||||
<fbi-tree
|
||||
class="tree"
|
||||
[node]="node"
|
||||
(actionClick)="onActionClick($event)"
|
||||
></fbi-tree>
|
||||
17
src/components/query/query.component.scss
Normal file
17
src/components/query/query.component.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
border: 1px solid black;
|
||||
padding-left: 4px;
|
||||
background-color: lightgray;
|
||||
}
|
||||
|
||||
.tree {
|
||||
flex: 1;
|
||||
overflow: scroll;
|
||||
}
|
||||
23
src/components/query/query.component.spec.ts
Normal file
23
src/components/query/query.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { QueryComponent } from './query.component';
|
||||
|
||||
describe('QueryComponent', () => {
|
||||
let component: QueryComponent;
|
||||
let fixture: ComponentFixture<QueryComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [QueryComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(QueryComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
106
src/components/query/query.component.ts
Normal file
106
src/components/query/query.component.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { TreeComponent } from '../tree/tree.component';
|
||||
import { TreeNode } from '../../models/tree-node';
|
||||
import { QueryService } from '../../services/query.service';
|
||||
import { Query } from '../../models/query';
|
||||
import { Action } from '../../models/action';
|
||||
import {
|
||||
faArrowDown,
|
||||
faArrowUp,
|
||||
faRemove,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { ExecuteService } from '../../services/execute.service';
|
||||
import { Header } from '../../models/header';
|
||||
|
||||
@Component({
|
||||
selector: 'fbi-query',
|
||||
standalone: true,
|
||||
imports: [TreeComponent],
|
||||
templateUrl: './query.component.html',
|
||||
styleUrl: './query.component.scss',
|
||||
})
|
||||
export class QueryComponent {
|
||||
node: TreeNode = new TreeNode({});
|
||||
|
||||
constructor(
|
||||
private queryService: QueryService,
|
||||
executeService: ExecuteService
|
||||
) {
|
||||
queryService.Query.subscribe((query: Query) => {
|
||||
executeService.headers(query).subscribe((headers: Header[]) => {
|
||||
this.node = new TreeNode({
|
||||
hidden: true,
|
||||
expanded: true,
|
||||
children: [
|
||||
{
|
||||
label: 'Fields',
|
||||
leaf: false,
|
||||
expanded: true,
|
||||
children: (query?.fields ?? []).map(
|
||||
(field: string, index: number, array: string[]) => {
|
||||
const actions = [];
|
||||
if (index !== 0) {
|
||||
actions.push({
|
||||
label: 'Move Up',
|
||||
icon: faArrowUp,
|
||||
data: { cmd: ACTIONS.UP, data: index - 1 },
|
||||
});
|
||||
}
|
||||
if (index !== array.length - 1) {
|
||||
actions.push({
|
||||
label: 'Move Down',
|
||||
icon: faArrowDown,
|
||||
data: { cmd: ACTIONS.DOWN, data: index + 1 },
|
||||
});
|
||||
}
|
||||
actions.push({
|
||||
label: 'Remove',
|
||||
icon: faRemove,
|
||||
data: { cmd: ACTIONS.REMOVE },
|
||||
});
|
||||
|
||||
const label =
|
||||
headers.find((header: Header) => header.source === field)
|
||||
?.label ?? 'unknown';
|
||||
|
||||
return {
|
||||
label: label,
|
||||
data: field,
|
||||
actions: actions,
|
||||
};
|
||||
}
|
||||
),
|
||||
},
|
||||
{ label: 'Filters', expanded: true, leaf: false },
|
||||
] as TreeNode[],
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onActionClick(event: { action: Action; node: TreeNode }): void {
|
||||
const data = event.action.data as ActionData;
|
||||
switch (data.cmd) {
|
||||
case ACTIONS.REMOVE:
|
||||
this.queryService.remove(event.node.data);
|
||||
break;
|
||||
case ACTIONS.UP:
|
||||
this.queryService.add(event.node.data, data.data);
|
||||
break;
|
||||
case ACTIONS.DOWN:
|
||||
this.queryService.add(event.node.data, data.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ACTIONS {
|
||||
REMOVE = 'del',
|
||||
UP = 'up',
|
||||
DOWN = 'down',
|
||||
}
|
||||
|
||||
type ActionData = {
|
||||
cmd: ACTIONS;
|
||||
data: any;
|
||||
};
|
||||
1
src/components/result/result.component.html
Normal file
1
src/components/result/result.component.html
Normal file
@@ -0,0 +1 @@
|
||||
<fbi-table [headers]="headers" [rows]="rows"></fbi-table>
|
||||
3
src/components/result/result.component.scss
Normal file
3
src/components/result/result.component.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
padding: 8px;
|
||||
}
|
||||
23
src/components/result/result.component.spec.ts
Normal file
23
src/components/result/result.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ResultComponent } from './result.component';
|
||||
|
||||
describe('ResultComponent', () => {
|
||||
let component: ResultComponent;
|
||||
let fixture: ComponentFixture<ResultComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ResultComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ResultComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
49
src/components/result/result.component.ts
Normal file
49
src/components/result/result.component.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { TreeComponent } from '../tree/tree.component';
|
||||
import { QueryService } from '../../services/query.service';
|
||||
import { Query } from '../../models/query';
|
||||
import { ExecuteService } from '../../services/execute.service';
|
||||
import { forkJoin } from 'rxjs';
|
||||
import { Header } from '../../models/header';
|
||||
import { TableComponent } from '../table/table.component';
|
||||
|
||||
@Component({
|
||||
selector: 'fbi-result',
|
||||
standalone: true,
|
||||
imports: [CommonModule, TreeComponent, TableComponent],
|
||||
templateUrl: './result.component.html',
|
||||
styleUrl: './result.component.scss',
|
||||
})
|
||||
export class ResultComponent {
|
||||
headers: Header[] = [];
|
||||
rows: Record<string, any>[] = [];
|
||||
|
||||
constructor(
|
||||
queryService: QueryService,
|
||||
private executeService: ExecuteService
|
||||
) {
|
||||
let last: string = '';
|
||||
queryService.Query.subscribe((query: Query) => {
|
||||
if (query.isValid()) {
|
||||
const current = query.toString();
|
||||
if (last !== current) {
|
||||
this.load(query);
|
||||
last = current;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private load(query: Query): void {
|
||||
forkJoin({
|
||||
headers: this.executeService.headers(query),
|
||||
data: this.executeService.data(query),
|
||||
}).subscribe(
|
||||
(result: { headers: Header[]; data: Record<string, any>[] }) => {
|
||||
this.headers = result.headers;
|
||||
this.rows = result.data;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
export class Header {
|
||||
label: string;
|
||||
source: string;
|
||||
|
||||
constructor(data: Partial<Header>) {
|
||||
this.label = data?.label ?? '';
|
||||
this.source = data?.source ?? '';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
<table>
|
||||
<tr>
|
||||
<th *ngFor="let h of header">
|
||||
<th></th>
|
||||
<th *ngFor="let h of headers">
|
||||
{{ h.label }}
|
||||
</th>
|
||||
</tr>
|
||||
<tr *ngFor="let row of rows | async; let index">
|
||||
<td>{{ index }}</td>
|
||||
<td *ngFor="let h of header">
|
||||
<tr *ngFor="let row of rows; index as i">
|
||||
<td>{{ i }}</td>
|
||||
<td *ngFor="let h of headers">
|
||||
{{ row[h.source] }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Header } from './header';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Header } from '../../models/header';
|
||||
|
||||
@Component({
|
||||
selector: 'fbi-table',
|
||||
@@ -11,6 +10,6 @@ import { Observable } from 'rxjs';
|
||||
styleUrl: './table.component.scss',
|
||||
})
|
||||
export class TableComponent {
|
||||
@Input() header!: Header[];
|
||||
@Input() rows!: Observable<Record<string, any>[]>;
|
||||
@Input() headers!: Header[];
|
||||
@Input() rows!: Record<string, any>[];
|
||||
}
|
||||
|
||||
32
src/components/tree/tree.component.html
Normal file
32
src/components/tree/tree.component.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<div
|
||||
*ngIf="!node?.hidden"
|
||||
class="treenode"
|
||||
[class.disabled]="node.disabled"
|
||||
[class.clickable]="!node.leaf"
|
||||
(click)="toggle($event)"
|
||||
>
|
||||
<span *ngIf="!node.leaf" class="icon">
|
||||
<fa-icon *ngIf="node.expanded" [icon]="faCaretDown"></fa-icon>
|
||||
<fa-icon *ngIf="!node.expanded" [icon]="faCaretRight"></fa-icon>
|
||||
</span>
|
||||
<span [title]="node.label" class="fill ellipsis">{{ node.label }}</span>
|
||||
<ng-container *ngIf="node.leaf && !node.disabled" class="actions">
|
||||
<fa-icon
|
||||
*ngFor="let action of node.actions"
|
||||
class="action clickable"
|
||||
[icon]="action.icon"
|
||||
(click)="onActionClick($event, action)"
|
||||
></fa-icon>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div
|
||||
*ngIf="node && node.children && node.expanded"
|
||||
[class.branch]="node.label !== ''"
|
||||
>
|
||||
<fbi-tree
|
||||
*ngFor="let child of node.children"
|
||||
[node]="child"
|
||||
(actionClick)="passActionClick($event)"
|
||||
></fbi-tree>
|
||||
</div>
|
||||
56
src/components/tree/tree.component.scss
Normal file
56
src/components/tree/tree.component.scss
Normal file
@@ -0,0 +1,56 @@
|
||||
.branch {
|
||||
margin-left: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.treenode {
|
||||
color: #333;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.treenode:hover {
|
||||
background-color: #edf8fa;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fill {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: dimgray;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.treenode:hover > .action {
|
||||
visibility: visible;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.action {
|
||||
visibility: hidden;
|
||||
width: 0px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
22
src/components/tree/tree.component.spec.ts
Normal file
22
src/components/tree/tree.component.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TreeComponent } from './tree.component';
|
||||
|
||||
describe('TreeComponent', () => {
|
||||
let component: TreeComponent;
|
||||
let fixture: ComponentFixture<TreeComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TreeComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TreeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
40
src/components/tree/tree.component.ts
Normal file
40
src/components/tree/tree.component.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TreeNode } from '../../models/tree-node';
|
||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||
import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons';
|
||||
import { Action } from '../../models/action';
|
||||
|
||||
@Component({
|
||||
selector: 'fbi-tree',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FontAwesomeModule],
|
||||
templateUrl: './tree.component.html',
|
||||
styleUrl: './tree.component.scss',
|
||||
})
|
||||
export class TreeComponent {
|
||||
@Input() node!: TreeNode;
|
||||
@Output() actionClick = new EventEmitter<{
|
||||
action: Action;
|
||||
node: TreeNode;
|
||||
}>();
|
||||
|
||||
faCaretDown = faCaretDown;
|
||||
faCaretRight = faCaretRight;
|
||||
|
||||
toggle(event: MouseEvent): void {
|
||||
if (!this.node.leaf) {
|
||||
event.stopPropagation();
|
||||
this.node.expanded = !this.node.expanded;
|
||||
}
|
||||
}
|
||||
|
||||
passActionClick(event: { action: Action; node: TreeNode }): void {
|
||||
this.actionClick.emit(event);
|
||||
}
|
||||
|
||||
onActionClick(event: MouseEvent, action: Action): void {
|
||||
event.stopPropagation();
|
||||
this.actionClick.emit({ action: action, node: this.node });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user