diff --git a/npm/ng-packs/nx/ng-packs/angular.json b/npm/ng-packs/nx/ng-packs/angular.json index c5e3310001..30bbb732d2 100644 --- a/npm/ng-packs/nx/ng-packs/angular.json +++ b/npm/ng-packs/nx/ng-packs/angular.json @@ -42,6 +42,47 @@ } } }, + "components": { + "projectType": "library", + "root": "packages/components", + "sourceRoot": "packages/components/src", + "prefix": "abp", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "packages/components/tsconfig.lib.json", + "project": "packages/components/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "packages/components/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "packages/components/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@nrwl/jest:jest", + "outputs": ["coverage/packages/components"], + "options": { + "jestConfig": "packages/components/jest.config.js", + "passWithNoTests": true + } + }, + "lint": { + "builder": "@nrwl/linter:eslint", + "options": { + "lintFilePatterns": [ + "packages/components/src/**/*.ts", + "packages/components/src/**/*.html" + ] + } + } + } + }, "core": { "projectType": "library", "root": "packages/core", diff --git a/npm/ng-packs/nx/ng-packs/nx.json b/npm/ng-packs/nx/ng-packs/nx.json index 63c85f8a6d..db5d09d7be 100644 --- a/npm/ng-packs/nx/ng-packs/nx.json +++ b/npm/ng-packs/nx/ng-packs/nx.json @@ -35,6 +35,10 @@ "tags": [], "implicitDependencies": ["core", "theme-shared"] }, + "components": { + "tags": [], + "implicitDependencies": ["core", "theme-shared"] + }, "core": { "tags": [] }, diff --git a/npm/ng-packs/nx/ng-packs/packages/components/.eslintrc.json b/npm/ng-packs/nx/ng-packs/packages/components/.eslintrc.json new file mode 100644 index 0000000000..9c51584494 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nrwl/nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "abp", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "abp", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nrwl/nx/angular-template"], + "rules": {} + } + ] +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/README.md b/npm/ng-packs/nx/ng-packs/packages/components/README.md new file mode 100644 index 0000000000..283812b9e0 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/README.md @@ -0,0 +1,3 @@ +

@abp/ng.components

+ +[docs.abp.io](https://docs.abp.io) diff --git a/npm/ng-packs/nx/ng-packs/packages/components/jest.config.js b/npm/ng-packs/nx/ng-packs/packages/components/jest.config.js new file mode 100644 index 0000000000..82ce530095 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/jest.config.js @@ -0,0 +1,20 @@ +module.exports = { + displayName: 'components', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + }, + coverageDirectory: '../../coverage/packages/components', + transform: { + '^.+\\.(ts|js|html)$': 'jest-preset-angular', + }, + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/npm/ng-packs/nx/ng-packs/packages/components/ng-package.json b/npm/ng-packs/nx/ng-packs/packages/components/ng-package.json new file mode 100644 index 0000000000..a5343f978a --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/ng-package.json @@ -0,0 +1,8 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/packages/components", + "lib": { + "entryFile": "src/public-api.ts" + }, + "allowedNonPeerDependencies": ["ng-zorro-antd"] +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/package.json b/npm/ng-packs/nx/ng-packs/packages/components/package.json new file mode 100644 index 0000000000..767261b15a --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/package.json @@ -0,0 +1,21 @@ +{ + "name": "@abp/ng.components", + "version": "4.4.0", + "homepage": "https://abp.io", + "repository": { + "type": "git", + "url": "https://github.com/abpframework/abp.git" + }, + "peerDependencies": { + "@abp/ng.core": ">=4.4.0", + "@abp/ng.theme.shared": ">=4.4.0", + "@ng-bootstrap/ng-bootstrap": ">=6.0.0" + }, + "dependencies": { + "ng-zorro-antd": "^11.0.0", + "tslib": "^2.0.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/page/ng-package.json b/npm/ng-packs/nx/ng-packs/packages/components/page/ng-package.json new file mode 100644 index 0000000000..530fd56dca --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/page/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/packages/components/page", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/page/src/page-part.directive.ts b/npm/ng-packs/nx/ng-packs/packages/components/page/src/page-part.directive.ts new file mode 100644 index 0000000000..c228189e0f --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/page/src/page-part.directive.ts @@ -0,0 +1,95 @@ +import { + Directive, + TemplateRef, + ViewContainerRef, + Input, + InjectionToken, + Optional, + Inject, + OnInit, + OnDestroy, + Injector, + OnChanges, + SimpleChanges, + SimpleChange, +} from '@angular/core'; +import { Observable, Subscription, of } from 'rxjs'; + +export interface PageRenderStrategy { + shouldRender(type?: string): boolean | Observable; + onInit?(type?: string, injector?: Injector, context?: any): void; + onDestroy?(type?: string, injector?: Injector, context?: any): void; + onContextUpdate?(change?: SimpleChange): void; +} + +export const PAGE_RENDER_STRATEGY = new InjectionToken('PAGE_RENDER_STRATEGY'); + +@Directive({ selector: '[abpPagePart]' }) +export class PagePartDirective implements OnInit, OnDestroy, OnChanges { + hasRendered = false; + type: string; + subscription: Subscription; + + @Input('abpPagePartContext') context: any; + @Input() set abpPagePart(type: string) { + this.type = type; + this.createRenderStream(type); + } + + render = (shouldRender: boolean) => { + if (shouldRender && !this.hasRendered) { + this.viewContainer.createEmbeddedView(this.templateRef); + this.hasRendered = true; + } else if (!shouldRender && this.hasRendered) { + this.viewContainer.clear(); + this.hasRendered = false; + } + }; + + constructor( + private templateRef: TemplateRef, + private viewContainer: ViewContainerRef, + @Optional() @Inject(PAGE_RENDER_STRATEGY) private renderLogic: PageRenderStrategy, + private injector: Injector, + ) {} + + ngOnChanges({ context }: SimpleChanges): void { + if (this.renderLogic?.onContextUpdate) { + this.renderLogic.onContextUpdate(context); + } + } + + ngOnInit() { + if (this.renderLogic?.onInit) { + this.renderLogic.onInit(this.type, this.injector, this.context); + } + } + + ngOnDestroy() { + this.clearSubscription(); + + if (this.renderLogic?.onDestroy) { + this.renderLogic.onDestroy(this.type, this.injector, this.context); + } + } + + shouldRender(type: string) { + if (this.renderLogic) { + const willRender = this.renderLogic.shouldRender(type); + return willRender instanceof Observable ? willRender : of(willRender); + } + return of(true); + } + + protected createRenderStream(type: string) { + this.clearSubscription(); + + this.subscription = this.shouldRender(type).subscribe(this.render); + } + + protected clearSubscription() { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/page/src/page-parts.component.ts b/npm/ng-packs/nx/ng-packs/packages/components/page/src/page-parts.component.ts new file mode 100644 index 0000000000..8438593268 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/page/src/page-parts.component.ts @@ -0,0 +1,34 @@ +import { Component, ViewEncapsulation } from '@angular/core'; + +export enum PageParts { + title = 'PageTitleContainerComponent', + breadcrumb = 'PageBreadcrumbContainerComponent', + toolbar = 'PageToolbarContainerComponent', +} + +@Component({ + selector: 'abp-page-title-container', + template: ` + + `, + encapsulation: ViewEncapsulation.None, +}) +export class PageTitleContainerComponent {} + +@Component({ + selector: 'abp-page-breadcrumb-container', + template: ` + + `, + encapsulation: ViewEncapsulation.None, +}) +export class PageBreadcrumbContainerComponent {} + +@Component({ + selector: 'abp-page-toolbar-container', + template: ` + + `, + encapsulation: ViewEncapsulation.None, +}) +export class PageToolbarContainerComponent {} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/page/src/page.component.html b/npm/ng-packs/nx/ng-packs/packages/components/page/src/page.component.html new file mode 100644 index 0000000000..fe5aff2e58 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/page/src/page.component.html @@ -0,0 +1,41 @@ +
+ + + + + + + + + + + +
+ + + + + +
+

+ {{ title }} +

+
+
+
+ + + +
+ +
+
+
+ + + +
+ +
+
+
diff --git a/npm/ng-packs/nx/ng-packs/packages/components/page/src/page.component.ts b/npm/ng-packs/nx/ng-packs/packages/components/page/src/page.component.ts new file mode 100644 index 0000000000..24b6d18ccf --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/page/src/page.component.ts @@ -0,0 +1,51 @@ +import { Component, Input, ViewEncapsulation, ContentChild } from '@angular/core'; +import { + PageTitleContainerComponent, + PageBreadcrumbContainerComponent, + PageToolbarContainerComponent, + PageParts, +} from './page-parts.component'; + +@Component({ + selector: 'abp-page', + templateUrl: './page.component.html', + encapsulation: ViewEncapsulation.None, +}) +export class PageComponent { + @Input() title: string; + + toolbarVisible = false; + _toolbarData: any; + @Input('toolbar') set toolbarData(val: any) { + this._toolbarData = val; + this.toolbarVisible = true; + } + + get toolbarData() { + return this._toolbarData; + } + + @Input('breadcrumb') breadcrumbVisible = true; + + pageParts = { + title: PageParts.title, + breadcrumb: PageParts.breadcrumb, + toolbar: PageParts.toolbar, + }; + + @ContentChild(PageTitleContainerComponent) customTitle: PageTitleContainerComponent; + @ContentChild(PageBreadcrumbContainerComponent) + customBreadcrumb: PageBreadcrumbContainerComponent; + @ContentChild(PageToolbarContainerComponent) customToolbar: PageToolbarContainerComponent; + + get shouldRenderRow() { + return !!( + this.title || + this.toolbarVisible || + this.breadcrumbVisible || + this.customTitle || + this.customBreadcrumb || + this.customToolbar + ); + } +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/page/src/page.module.ts b/npm/ng-packs/nx/ng-packs/packages/components/page/src/page.module.ts new file mode 100644 index 0000000000..3f12543be7 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/page/src/page.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { UiExtensionsModule } from '@abp/ng.theme.shared/extensions'; +import { ThemeSharedModule } from '@abp/ng.theme.shared'; +import { CoreModule } from '@abp/ng.core'; +import { PageComponent } from './page.component'; +import { + PageTitleContainerComponent, + PageBreadcrumbContainerComponent, + PageToolbarContainerComponent, +} from './page-parts.component'; +import { PagePartDirective } from './page-part.directive'; + +const exportedDeclarations = [ + PageComponent, + PageTitleContainerComponent, + PageBreadcrumbContainerComponent, + PageToolbarContainerComponent, + PagePartDirective, +]; + +@NgModule({ + declarations: [...exportedDeclarations], + imports: [CommonModule, UiExtensionsModule, CoreModule, ThemeSharedModule], + exports: [...exportedDeclarations], +}) +export class PageModule {} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/page/src/public-api.ts b/npm/ng-packs/nx/ng-packs/packages/components/page/src/public-api.ts new file mode 100644 index 0000000000..980f8de374 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/page/src/public-api.ts @@ -0,0 +1,4 @@ +export * from './page.module'; +export * from './page-part.directive'; +export * from './page-parts.component'; +export * from './page.component'; diff --git a/npm/ng-packs/nx/ng-packs/packages/components/src/public-api.ts b/npm/ng-packs/nx/ng-packs/packages/components/src/public-api.ts new file mode 100644 index 0000000000..2302a05a8c --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/src/public-api.ts @@ -0,0 +1,5 @@ +/* + * Public API Surface of components + */ + +export {}; diff --git a/npm/ng-packs/nx/ng-packs/packages/components/src/test-setup.ts b/npm/ng-packs/nx/ng-packs/packages/components/src/test-setup.ts new file mode 100644 index 0000000000..1100b3e8a6 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/src/test-setup.ts @@ -0,0 +1 @@ +import 'jest-preset-angular/setup-jest'; diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tree/ng-package.json b/npm/ng-packs/nx/ng-packs/packages/components/tree/ng-package.json new file mode 100644 index 0000000000..0a2a39f416 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tree/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/packages/components/tree", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/components/tree.component.html b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/components/tree.component.html new file mode 100644 index 0000000000..4f4aaf472d --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/components/tree.component.html @@ -0,0 +1,96 @@ + + +
+ + + +
+ +
+ +
+ +
+ +
+
+
+ + + {{ node.title }} + +
+
+ + + + + + + + + + + + + + + + diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/components/tree.component.scss b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/components/tree.component.scss new file mode 100644 index 0000000000..07ca018f0d --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/components/tree.component.scss @@ -0,0 +1,38 @@ +.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; + } + + .ellipsis { + position: absolute; + right: 8px; + top: 1px; + cursor: pointer; + } +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/components/tree.component.ts b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/components/tree.component.ts new file mode 100644 index 0000000000..3f6179a5b2 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/components/tree.component.ts @@ -0,0 +1,79 @@ +import { + Component, + ContentChild, + EventEmitter, + Input, + Output, + TemplateRef, + ViewEncapsulation, +} from '@angular/core'; +import { NzFormatEmitEvent, NzFormatBeforeDropEvent } from 'ng-zorro-antd/tree'; +import { of } from 'rxjs'; +import { TreeNodeTemplateDirective } from '../templates/tree-node-template.directive'; +import { ExpandedIconTemplateDirective } from '../templates/expanded-icon-template.directive'; +import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'; + +export type DropEvent = NzFormatEmitEvent & { pos: number }; + +@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 { + dropPosition: number; + + dropdowns = {} as { [key: string]: NgbDropdown }; + + @ContentChild('menu') menu: TemplateRef; + @ContentChild(TreeNodeTemplateDirective) customNodeTemplate: TreeNodeTemplateDirective; + @ContentChild(ExpandedIconTemplateDirective) expandedIconTemplate: ExpandedIconTemplateDirective; + @Output() readonly checkedKeysChange = new EventEmitter(); + @Output() readonly expandedKeysChange = new EventEmitter(); + @Output() readonly selectedNodeChange = new EventEmitter(); + @Output() readonly dropOver = new EventEmitter(); + @Input() noAnimation = true; + @Input() draggable: boolean; + @Input() checkable: boolean; + @Input() checkStrictly: boolean; + @Input() checkedKeys = []; + @Input() nodes = []; + @Input() expandedKeys: string[] = []; + @Input() selectedNode: any; + @Input() isNodeSelected = node => this.selectedNode?.id === node.key; + @Input() beforeDrop = (event: NzFormatBeforeDropEvent) => { + this.dropPosition = event.pos; + return of(false); + }; + + 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); + } + + onDrop(event: DropEvent) { + event.event.stopPropagation(); + event.event.preventDefault(); + event.pos = this.dropPosition; + + this.dropOver.emit(event); + } + + initDropdown(key: string, dropdown: NgbDropdown) { + this.dropdowns[key] = dropdown; + } +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/templates/expanded-icon-template.directive.ts b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/templates/expanded-icon-template.directive.ts new file mode 100644 index 0000000000..35706bd25f --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/templates/expanded-icon-template.directive.ts @@ -0,0 +1,8 @@ +import { Directive, TemplateRef } from '@angular/core'; + +@Directive({ + selector: '[abpTreeExpandedIconTemplate],[abp-tree-expanded-icon-template]', +}) +export class ExpandedIconTemplateDirective { + constructor(public template: TemplateRef) {} +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/templates/tree-node-template.directive.ts b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/templates/tree-node-template.directive.ts new file mode 100644 index 0000000000..56c66af04b --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/templates/tree-node-template.directive.ts @@ -0,0 +1,8 @@ +import { Directive, TemplateRef } from '@angular/core'; + +@Directive({ + selector: '[abpTreeNodeTemplate],[abp-tree-node-template]', +}) +export class TreeNodeTemplateDirective { + constructor(public template: TemplateRef) {} +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/tree.module.ts b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/tree.module.ts new file mode 100644 index 0000000000..2013c37588 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/tree.module.ts @@ -0,0 +1,19 @@ +import { CoreModule } from '@abp/ng.core'; +import { NgModule } from '@angular/core'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; +import { NzNoAnimationModule } from 'ng-zorro-antd/core/no-animation'; +import { NzTreeModule } from 'ng-zorro-antd/tree'; +import { TreeComponent } from './components/tree.component'; +import { ExpandedIconTemplateDirective } from './templates/expanded-icon-template.directive'; +import { TreeNodeTemplateDirective } from './templates/tree-node-template.directive'; + +const templates = [TreeNodeTemplateDirective, ExpandedIconTemplateDirective]; + +const exported = [...templates, TreeComponent]; + +@NgModule({ + imports: [CoreModule, NzTreeModule, NgbDropdownModule, NzNoAnimationModule], + exports: [...exported], + declarations: [...exported], +}) +export class TreeModule {} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/utils/nz-tree-adapter.ts b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/utils/nz-tree-adapter.ts new file mode 100644 index 0000000000..d5d32f0146 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/lib/utils/nz-tree-adapter.ts @@ -0,0 +1,112 @@ +export abstract class BaseNode { + name?: string; + displayName?: string; + + constructor(public id: string, public parentId: string | null) {} +} + +class TreeNode extends BaseNode { + title: string | undefined; + key: string; + icon: string | null = null; + children: TreeNode[] = []; + isLeaf = true; + checked = false; + selected = false; + expanded = false; + selectable = true; + disabled = false; + disableCheckbox = false; + parentNode?: TreeNode | 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 { + private tree: TreeNode[]; + + constructor(private list: T[] = []) { + this.tree = createTreeFromList(this.list); + } + + getList() { + return this.list; + } + + getTree() { + return this.tree; + } + + handleDrop({ key, parentNode }: TreeNode) { + const index = this.list.findIndex(({ id }) => id === key); + this.list[index].parentId = parentNode ? parentNode.key : null; + this.tree = createTreeFromList(this.list); + } + + handleRemove({ key }: TreeNode) { + this.updateTreeFromList(this.list.filter(item => item.id !== key)); + } + + handleUpdate({ key, children }: { key: string; children: T[] }) { + /** + * When we need to update a node with new children, first we need to remove any descendant nodes. + * If we remove immediate children and create a new tree, any other descendant nodes will be removed + * and we won't need to recursively remove sub children. + * Then, you simply add back the new children and create a new tree. + */ + const listWithDescendantNodesRemoved = this.updateTreeFromList( + this.list.filter(item => item.parentId !== key), + ); + this.updateTreeFromList(listWithDescendantNodesRemoved.concat(children)); + } + + updateTreeFromList(list: T[]) { + this.tree = createTreeFromList(list); + this.list = createListFromTree(this.tree); + return this.list; + } +} + +// UTILITY FUNCTIONS + +function createTreeFromList(list: T[]): TreeNode[] { + const map = createMapFromList(list); + const tree: TreeNode[] = []; + + list.forEach(row => { + const parentId = row.parentId; + const node = map.get(row.id); + if (parentId) { + const parent = map.get(parentId); + if (!parent) return; + parent.children.push(node); + parent.isLeaf = false; + } else { + tree.push(node); + } + }); + + return tree; +} + +function createListFromTree(tree: TreeNode[], list: T[] = []): T[] { + tree.forEach(node => { + list.push({ ...node.entity, parentId: node.parentId }); + if (node.children) createListFromTree(node.children, list); + }); + + return list; +} + +function createMapFromList( + list: T[], + map = new Map>(), +): Map> { + list.forEach(row => map.set(row.id, new TreeNode(row))); + + return map; +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tree/src/public-api.ts b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/public-api.ts new file mode 100644 index 0000000000..1f3dd48e00 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tree/src/public-api.ts @@ -0,0 +1,5 @@ +export * from './lib/tree.module'; +export * from './lib/components/tree.component'; +export * from './lib/utils/nz-tree-adapter'; +export * from './lib/templates/tree-node-template.directive'; +export * from './lib/templates/expanded-icon-template.directive'; diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.json b/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.json new file mode 100644 index 0000000000..62ebbd9464 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.lib.json b/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.lib.json new file mode 100644 index 0000000000..bbcc12b1c7 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.lib.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": ["dom", "es2018"] + }, + "exclude": ["src/test-setup.ts", "**/*.spec.ts"], + "include": ["**/*.ts"] +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.lib.prod.json b/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.lib.prod.json new file mode 100644 index 0000000000..331d0ecb05 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.lib.prod.json @@ -0,0 +1,11 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial", + "enableIvy": false + } +} diff --git a/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.spec.json b/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.spec.json new file mode 100644 index 0000000000..cfff29a544 --- /dev/null +++ b/npm/ng-packs/nx/ng-packs/packages/components/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] +} diff --git a/npm/ng-packs/nx/ng-packs/packages/theme-shared/testing/src/public-api.ts b/npm/ng-packs/nx/ng-packs/packages/theme-shared/testing/src/public-api.ts index 14355a8a28..dc639b1462 100644 --- a/npm/ng-packs/nx/ng-packs/packages/theme-shared/testing/src/public-api.ts +++ b/npm/ng-packs/nx/ng-packs/packages/theme-shared/testing/src/public-api.ts @@ -1 +1,3 @@ +import * as ThemeSharedTesting from './lib/models'; export * from './lib/theme-shared-testing.module'; +export { ThemeSharedTesting }; diff --git a/npm/ng-packs/nx/ng-packs/tsconfig.base.json b/npm/ng-packs/nx/ng-packs/tsconfig.base.json index 3b7724759e..cee7a51cd2 100644 --- a/npm/ng-packs/nx/ng-packs/tsconfig.base.json +++ b/npm/ng-packs/nx/ng-packs/tsconfig.base.json @@ -16,9 +16,17 @@ "baseUrl": ".", "paths": { "@abp/ng.core": ["dist/packages/core"], + "@abp/ng.core/locale": ["dist/packages/core/locale"], + "@abp/ng.core/testing": ["dist/packages/core/testing"], "@abp/ng.theme.shared": ["dist/packages/theme-shared"], + "@abp/ng.theme.shared/extensions": ["dist/packages/theme-shared/extensions"], + "@abp/ng.theme.shared/testing": ["dist/packages/theme-shared/testing"], + "@abp/ng.components": ["dist/packages/components"], + "@abp/ng.components/page": ["dist/packages/components/page"], + "@abp/ng.components/tree": ["dist/packages/components/tree"], "@abp/ng.account.core": ["dist/packages/account-core"], - "@abp/ng.theme.basic": ["dist/packages/theme-basic"] + "@abp/ng.theme.basic": ["dist/packages/theme-basic"], + "@abp/ng.theme.basic/testing": ["dist/packages/theme-basic/testing"] } }, "exclude": ["node_modules", "tmp"]