File
Index
Properties
|
|
Inputs
|
|
Outputs
|
|
Accessors
|
|
_hasChild
|
Default value : () => {...}
|
|
aclRuleTreeNodes
|
Type : AclRuleTreeNode[]
|
Default value : []
|
|
import { AclRule, AclRuleState } from '@allianz/taly-acl';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import {
NxFlatTreeControl,
NxFlatTreeNode,
NxTreeFlatDataSource,
NxTreeNode
} from '@aposin/ng-aquila/tree';
import { Observable } from 'rxjs';
import { AclService } from '../../services/acl.service';
interface AclRuleTreeNode extends NxTreeNode {
children?: AclRuleTreeNode[];
resource: string;
fullPath: string;
state?: Observable<AclRuleState>;
active?: boolean;
ruleDetails?: RuleDetail[];
}
export interface AclRuleFlatTreeNode extends NxFlatTreeNode {
resource: string;
fullPath: string;
state?: AclRuleState;
active?: boolean;
ruleDetails?: RuleDetail[];
}
interface RuleDetail {
condition: string;
state: string;
defaultRule?: boolean;
active: boolean;
}
@Component({
selector: 'acl-rules',
templateUrl: './rules.component.html',
styleUrls: ['./rules.component.scss'],
standalone: false
})
export class RulesComponent {
@Input() set rules(value: AclRule[]) {
this.aclRuleTreeNodes = this.createTreeNodes(value);
this._dataSource = new NxTreeFlatDataSource(this._treeControl, this.aclRuleTreeNodes);
this._treeControl.expandAll();
}
@Output() openRuleEditor = new EventEmitter<AclRuleFlatTreeNode>();
aclRuleTreeNodes: AclRuleTreeNode[] = [];
_dataSource!: NxTreeFlatDataSource<AclRuleTreeNode, AclRuleFlatTreeNode>;
_treeControl: NxFlatTreeControl<AclRuleFlatTreeNode>;
constructor(private readonly aclService: AclService) {
this._treeControl = new NxFlatTreeControl();
}
_hasChild = (_: number, node: NxFlatTreeNode) => node.expandable;
private createTreeNodes(rules: AclRule[]) {
const root: AclRuleTreeNode = { resource: '', fullPath: '', children: [] };
// record of all available nodes in the tree
// it's only used to identify if the node is already exist
const treeNodeRecord: Record<string, AclRuleTreeNode> = { '': root };
rules.forEach((rule) => {
const resources = rule.path.split('/');
let parent = root;
let fullPath = '';
resources.forEach((resource, index) => {
fullPath = fullPath ? `${fullPath}/${resource}` : resource;
let node = treeNodeRecord[fullPath];
// if the node is not exist, create a new one
if (!node) {
node = { resource, fullPath };
treeNodeRecord[fullPath] = node;
parent.children = parent.children || [];
parent.children.push(node);
}
// store rule info in the last node of each path
// e.g. given rule page-a/bb-a/form-a, some-condition, hidden => some-condition and hidden will be stored in the form-a node only
if (index === resources.length - 1) {
node.state = this.aclService.currentState$(node.fullPath);
// an initial active status is always true
// if the toggle in the main view is switched off, the corresponding ACL resource will become visible & editable
node.active = true;
node.ruleDetails ??= [];
node.ruleDetails.push({
condition: rule.condition,
state: rule.state,
active: rule.active,
defaultRule: rule.defaultRule
});
}
// store the current node as a parent of the next node
parent = node;
});
});
return root.children || [];
}
}
<nx-tree [dataSource]="_dataSource" [treeControl]="_treeControl">
<nx-tree-node *nxTreeNodeDef="let node">
<button
nxAction
nxTreeNodeActionItem
nxTreeNodePadding
title="{{ node.fullPath }}"
type="button"
>
<div class="node-row">
<div class="resource">
<span>{{ node.resource }}</span>
</div>
<button class="state-button" (click)="openRuleEditor.emit(node)">
{{ node.state | async }}
</button>
</div>
</button>
</nx-tree-node>
<nx-tree-node *nxTreeNodeDef="let node; when: _hasChild">
<button
nxAction
nxTreeNodeActionItem
nxTreeNodeToggle
nxTreeNodePadding
expandable
[expanded]="_treeControl.isExpanded(node)"
title="{{ node.fullPath }}"
type="button"
>
<div class="node-row">
<div class="resource">
<span>{{ node.resource }}</span>
</div>
<button
class="state-button"
(click)="openRuleEditor.emit(node)"
*ngIf="node.ruleDetails?.length"
>
{{ node.state | async }}
</button>
</div>
</button>
</nx-tree-node>
</nx-tree>
@use '../../inspector.colors.scss' as *;
@use 'sass:color';
.node-row {
display: grid;
grid-template-columns: minmax(120px, 1fr) 60px;
gap: 8px;
}
.resource {
overflow-x: hidden;
text-overflow: ellipsis;
}
.state-button {
font-weight: 600;
color: $debug-color-cta;
cursor: pointer;
border-radius: 3px;
transition: background-color 0.3s ease;
text-align: center;
background-color: transparent;
border: none;
&:hover {
color: $debug-color-cta-hover;
background-color: $debug-color-dark-hover;
}
}
:host {
.nx-action {
background-color: $debug-color-light;
color: #fff;
padding: 2px 16px 2px 0;
cursor: auto;
font-size: 13px;
line-height: 18px;
&:hover {
background-color: color.adjust($debug-color-light, $lightness: 5%);
color: #fff;
}
}
}
Legend
Html element with directive