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