mirror of https://github.com/abpframework/abp.git
committed by
GitHub
21 changed files with 1304 additions and 850 deletions
@ -0,0 +1,3 @@ |
|||
<h1> @abp/ng.components </h1> |
|||
|
|||
[docs.abp.io](https://docs.abp.io) |
|||
@ -0,0 +1,6 @@ |
|||
const jestConfig = require('../../jest.config'); |
|||
|
|||
module.exports = { |
|||
...jestConfig, |
|||
name: 'components', |
|||
}; |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json", |
|||
"dest": "../../dist/components", |
|||
"assets": ["./styles/**/*"], |
|||
"lib": { |
|||
"entryFile": "src/public-api.ts" |
|||
}, |
|||
"whitelistedNonPeerDependencies": ["ng-zorro-antd"] |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
module.exports = { |
|||
entryPoints: { |
|||
'.': {}, |
|||
'./tree': {}, |
|||
'./dist': { ignore: true }, |
|||
}, |
|||
}; |
|||
@ -0,0 +1,20 @@ |
|||
{ |
|||
"name": "@abp/ng.components", |
|||
"version": "3.0.4", |
|||
"homepage": "https://abp.io", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "https://github.com/abpframework/abp.git" |
|||
}, |
|||
"peerDependencies": { |
|||
"@abp/ng.core": ">=3.0.0", |
|||
"@ng-bootstrap/ng-bootstrap": ">=6.0.0" |
|||
}, |
|||
"dependencies": { |
|||
"ng-zorro-antd": "^9.3.0", |
|||
"tslib": "^2.0.0" |
|||
}, |
|||
"publishConfig": { |
|||
"access": "public" |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
/* |
|||
* Public API Surface of components |
|||
*/ |
|||
|
|||
export {}; |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", |
|||
"dest": "../../dist/components/tree", |
|||
"lib": { |
|||
"entryFile": "src/public-api.ts" |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<nz-tree |
|||
[nzCheckStrictly]="checkStrictly" |
|||
[nzCheckable]="checkable" |
|||
[nzCheckedKeys]="checkedKeys" |
|||
[nzData]="nodes" |
|||
[nzTreeTemplate]="treeTemplate" |
|||
[nzExpandedKeys]="expandedKeys" |
|||
(nzExpandChange)="onExpandedKeysChange($event)" |
|||
(nzCheckBoxChange)="onCheckboxChange($event)" |
|||
></nz-tree> |
|||
<ng-template #treeTemplate let-node> |
|||
<div |
|||
class="node-wrapper" |
|||
[class.selected]="isNodeSelected(node)" |
|||
[title]="node.title" |
|||
(click)="onSelectedNodeChange(node)" |
|||
> |
|||
<span>{{ node.title }}</span> |
|||
|
|||
<div *ngIf="menu" class="ellipsis" ngbDropdown placement="bottom" container="body"> |
|||
<i class="fas fa-ellipsis-h" ngbDropdownToggle [class.dropdown-toggle]="false"></i> |
|||
<div ngbDropdownMenu> |
|||
<ng-template *ngTemplateOutlet="menu; context: { $implicit: node }"></ng-template> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</ng-template> |
|||
@ -0,0 +1,50 @@ |
|||
.ant-tree { |
|||
color: inherit; |
|||
|
|||
.ant-tree-node-content-wrapper.ant-tree-node-selected { |
|||
background-color: transparent; |
|||
} |
|||
|
|||
.ant-tree-switcher { |
|||
line-height: 17px; |
|||
} |
|||
|
|||
.ant-tree-node-content-wrapper { |
|||
width: 100%; |
|||
padding: 0; |
|||
} |
|||
|
|||
.node-wrapper { |
|||
width: 100%; |
|||
position: relative; |
|||
display: inline-block; |
|||
margin: 0; |
|||
padding: 0 5px; |
|||
line-height: 24px; |
|||
text-decoration: none; |
|||
vertical-align: top; |
|||
border-radius: 2px; |
|||
cursor: pointer; |
|||
padding-left: 8px; |
|||
border: 1px solid transparent; |
|||
|
|||
&:hover { |
|||
background-color: transparent; |
|||
border: 1px solid var(--primary); |
|||
color: var(--text-color); |
|||
} |
|||
|
|||
&.selected { |
|||
background-color: transparent; |
|||
color: var(--white); |
|||
background-color: var(--primary); |
|||
} |
|||
} |
|||
|
|||
.ellipsis { |
|||
position: absolute; |
|||
right: 8px; |
|||
top: 1px; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
import { |
|||
Component, |
|||
ContentChild, |
|||
EventEmitter, |
|||
Input, |
|||
Output, |
|||
TemplateRef, |
|||
ViewEncapsulation, |
|||
} from '@angular/core'; |
|||
|
|||
@Component({ |
|||
selector: 'abp-tree', |
|||
templateUrl: 'tree.component.html', |
|||
styleUrls: [ |
|||
'../../../../../../node_modules/ng-zorro-antd/tree/style/index.min.css', |
|||
'tree.component.scss', |
|||
], |
|||
encapsulation: ViewEncapsulation.None, |
|||
}) |
|||
export class TreeComponent { |
|||
@ContentChild('menu') menu: TemplateRef<any>; |
|||
@Output() readonly checkedKeysChange = new EventEmitter(); |
|||
@Output() readonly expandedKeysChange = new EventEmitter<string[]>(); |
|||
@Output() readonly selectedNodeChange = new EventEmitter(); |
|||
@Input() checkable: boolean; |
|||
@Input() checkStrictly: boolean; |
|||
@Input() checkedKeys = []; |
|||
@Input() nodes = []; |
|||
@Input() expandedKeys: string[] = []; |
|||
@Input() selectedNode: any; |
|||
@Input() isNodeSelected = node => this.selectedNode?.id === node.key; |
|||
|
|||
onSelectedNodeChange(node) { |
|||
this.selectedNode = node.origin.entity; |
|||
this.selectedNodeChange.emit(node.origin.entity); |
|||
} |
|||
|
|||
onCheckboxChange(event) { |
|||
this.checkedKeys = [...event.keys]; |
|||
this.checkedKeysChange.emit(event.keys); |
|||
} |
|||
|
|||
onExpandedKeysChange(event) { |
|||
this.expandedKeys = [...event.keys]; |
|||
this.expandedKeysChange.emit(event.keys); |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
import { NgModule } from '@angular/core'; |
|||
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; |
|||
import { NzTreeModule } from 'ng-zorro-antd/tree'; |
|||
import { TreeComponent } from './components/tree.component'; |
|||
import { CommonModule } from '@angular/common'; |
|||
|
|||
@NgModule({ |
|||
imports: [CommonModule, NzTreeModule, NgbDropdownModule], |
|||
exports: [TreeComponent], |
|||
declarations: [TreeComponent], |
|||
}) |
|||
export class TreeModule {} |
|||
@ -0,0 +1,95 @@ |
|||
export abstract class BaseNode { |
|||
name?: string; |
|||
displayName?: string; |
|||
|
|||
constructor(public id: string, public parentId: string | null) {} |
|||
} |
|||
|
|||
class TreeNode<T extends BaseNode> extends BaseNode { |
|||
title: string | undefined; |
|||
key: string; |
|||
icon: string | null = null; |
|||
children: TreeNode<T>[] = []; |
|||
isLeaf = true; |
|||
checked = false; |
|||
selected = false; |
|||
expanded = false; |
|||
selectable = true; |
|||
disabled = false; |
|||
disableCheckbox = false; |
|||
parentNode?: TreeNode<T> | null; |
|||
|
|||
constructor(public entity: T, private nameResolver = ent => ent.displayName || ent.name) { |
|||
super(entity.id, entity.parentId); |
|||
this.key = entity.id; |
|||
this.title = nameResolver(entity); |
|||
} |
|||
} |
|||
|
|||
export class TreeAdapter<T extends BaseNode = BaseNode> { |
|||
private tree: TreeNode<T>[]; |
|||
|
|||
constructor(private list: T[] = []) { |
|||
this.tree = createTreeFromList(this.list); |
|||
} |
|||
|
|||
getList() { |
|||
return this.list; |
|||
} |
|||
|
|||
getTree() { |
|||
return this.tree; |
|||
} |
|||
|
|||
handleDrop(node: TreeNode<T>) { |
|||
const { key, parentNode } = node; |
|||
const index = this.list.findIndex(({ id }) => id === key); |
|||
this.list[index].parentId = parentNode ? parentNode.key : null; |
|||
this.tree = createTreeFromList(this.list); |
|||
} |
|||
|
|||
handleRemove(node: TreeNode<T>) { |
|||
const { key, parentNode } = node; |
|||
this.list = this.list.filter(({ id, parentId }) => id !== key && parentId !== key); |
|||
this.tree = createTreeFromList(this.list); |
|||
} |
|||
} |
|||
|
|||
// UTILITY FUNCTIONS
|
|||
|
|||
function createTreeFromList<T extends BaseNode>(list: T[]): TreeNode<T>[] { |
|||
const map = createMapFromList(list); |
|||
const tree: TreeNode<T>[] = []; |
|||
|
|||
list.forEach(row => { |
|||
const parentId = row.parentId; |
|||
const node = map.get(row.id); |
|||
if (parentId) { |
|||
const parent = map.get(parentId); |
|||
parent.children.push(node); |
|||
parent.isLeaf = false; |
|||
} else { |
|||
tree.push(node); |
|||
} |
|||
}); |
|||
|
|||
return tree; |
|||
} |
|||
|
|||
function createListFromTree<T extends BaseNode>(tree: TreeNode<T>[], list: T[] = []): T[] { |
|||
tree.forEach(node => { |
|||
list.push({ ...node.entity, parentId: node.parentId }); |
|||
if (node.children) createListFromTree(node.children, list); |
|||
}); |
|||
|
|||
return list; |
|||
} |
|||
|
|||
function createMapFromList<T extends BaseNode>( |
|||
list: T[], |
|||
map = new Map<string, TreeNode<T>>(), |
|||
): Map<string, TreeNode<T>> { |
|||
list.forEach(row => map.set(row.id, new TreeNode(row))); |
|||
|
|||
return map; |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
export * from './lib/tree.module'; |
|||
export * from './lib/components/tree.component'; |
|||
export * from './lib/utils/nz-tree-adapter'; |
|||
@ -0,0 +1,19 @@ |
|||
{ |
|||
"extends": "../../tsconfig.prod.json", |
|||
"compilerOptions": { |
|||
"outDir": "../../out-tsc/lib", |
|||
"target": "es2015", |
|||
"declaration": true, |
|||
"inlineSources": true, |
|||
"types": [], |
|||
"lib": ["dom", "es2018"] |
|||
}, |
|||
"angularCompilerOptions": { |
|||
"skipTemplateCodegen": true, |
|||
"strictMetadataEmit": true, |
|||
"fullTemplateTypeCheck": true, |
|||
"strictInjectionParameters": true, |
|||
"enableResourceInlining": true |
|||
}, |
|||
"exclude": ["src/test.ts", "**/*.spec.ts"] |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"extends": "./tsconfig.lib.json", |
|||
"angularCompilerOptions": { |
|||
"enableIvy": false |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
{ |
|||
"extends": "../../tsconfig.prod.json", |
|||
"compilerOptions": { |
|||
"emitDecoratorMetadata": true, |
|||
"esModuleInterop": true, |
|||
"outDir": "../../dist/out-tsc", |
|||
"module": "commonjs", |
|||
"types": ["jest", "node"] |
|||
}, |
|||
"include": ["**/*.spec.ts", "**/*.d.ts"] |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"extends": "../../tslint.json", |
|||
"rules": { |
|||
"directive-selector": [true, "attribute", "abp", "camelCase"], |
|||
"component-selector": [true, "element", "abp", "kebab-case"] |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue