This commit is contained in:
CHEVALLIER Abel
2025-11-13 16:23:22 +01:00
parent de9c515a47
commit cb235644dc
34924 changed files with 3811102 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
nz-input-group {
margin-bottom: 8px;
}
::ng-deep .highlight {
color: #f50;
}

View File

@@ -0,0 +1,22 @@
<nz-input-group [nzSuffix]="suffixIcon">
<input type="text" nz-input placeholder="Search" ngModel (ngModelChange)="searchValue$.next($event)" />
</nz-input-group>
<ng-template #suffixIcon>
<nz-icon nzType="search" />
</ng-template>
<nz-tree-view [nzTreeControl]="treeControl" [nzDataSource]="dataSource" nzNoAnimation>
<nz-tree-node *nzTreeNodeDef="let node" nzTreeNodePadding>
<nz-tree-node-toggle nzTreeNodeNoopToggle></nz-tree-node-toggle>
<span [innerHTML]="node.name | nzHighlight: searchValue : 'i' : 'highlight'"></span>
</nz-tree-node>
<nz-tree-node *nzTreeNodeDef="let node; when: hasChild" nzTreeNodePadding>
<nz-tree-node-toggle>
<nz-icon nzType="caret-down" nzTreeNodeToggleRotateIcon />
</nz-tree-node-toggle>
<span [innerHTML]="node.name | nzHighlight: searchValue : 'i' : 'highlight'"></span>
</nz-tree-node>
</nz-tree-view>

View File

@@ -0,0 +1,23 @@
import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component';
describe('<%= classify(name) %>Component', () => {
let component: <%= classify(name) %>Component;
let fixture: ComponentFixture<<%= classify(name) %>Component>;
beforeEach(fakeAsync(() => {
TestBed.configureTestingModule({
declarations: [ <%= classify(name) %>Component ]
})
.compileComponents();
;
fixture = TestBed.createComponent(<%= classify(name) %>Component);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it('should compile', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,180 @@
import { FlatTreeControl } from '@angular/cdk/tree';
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BehaviorSubject, combineLatest } from 'rxjs';
import { auditTime, map } from 'rxjs/operators';
import { NzHighlightPipe } from 'ng-zorro-antd/core/highlight';
import { NzNoAnimationDirective } from 'ng-zorro-antd/core/no-animation';
import { NzIconModule } from 'ng-zorro-antd/icon';
import { NzInputModule } from 'ng-zorro-antd/input';
import { NzTreeFlatDataSource, NzTreeFlattener, NzTreeViewModule } from 'ng-zorro-antd/tree-view';
interface TreeNode {
name: string;
children?: TreeNode[];
}
const TREE_DATA: TreeNode[] = [
{
name: '0-0',
children: [{ name: '0-0-0' }, { name: '0-0-1' }, { name: '0-0-2' }]
},
{
name: '0-1',
children: [
{
name: '0-1-0',
children: [{ name: '0-1-0-0' }, { name: '0-1-0-1' }]
},
{
name: '0-1-1',
children: [{ name: '0-1-1-0' }, { name: '0-1-1-1' }]
}
]
}
];
interface FlatNode {
expandable: boolean;
name: string;
level: number;
}
class FilteredTreeResult {
constructor(
public treeData: TreeNode[],
public needsToExpanded: TreeNode[] = []
) {}
}
/**
* From https://stackoverflow.com/a/45290208/6851836
*/
function filterTreeData(data: TreeNode[], value: string): FilteredTreeResult {
const needsToExpanded = new Set<TreeNode>();
const _filter = (node: TreeNode, result: TreeNode[]): TreeNode[] => {
if (node.name.search(value) !== -1) {
result.push(node);
return result;
}
if (Array.isArray(node.children)) {
const nodes = node.children.reduce((a, b) => _filter(b, a), [] as TreeNode[]);
if (nodes.length) {
const parentNode = { ...node, children: nodes };
needsToExpanded.add(parentNode);
result.push(parentNode);
}
}
return result;
};
const treeData = data.reduce((a, b) => _filter(b, a), [] as TreeNode[]);
return new FilteredTreeResult(treeData, [...needsToExpanded]);
}
@Component({
selector: '<%= selector %>',
imports: [FormsModule, NzInputModule, NzIconModule, NzTreeViewModule, NzNoAnimationDirective, NzHighlightPipe],
<% if(inlineTemplate) { %>template: `
<nz-input-group [nzSuffix]="suffixIcon">
<input type="text" nz-input placeholder="Search" ngModel (ngModelChange)="searchValue$.next($event)" />
</nz-input-group>
<ng-template #suffixIcon>
<nz-icon nzType="search" />
</ng-template>
<nz-tree-view [nzTreeControl]="treeControl" [nzDataSource]="dataSource" nzNoAnimation>
<nz-tree-node *nzTreeNodeDef="let node" nzTreeNodePadding>
<nz-tree-node-toggle nzTreeNodeNoopToggle></nz-tree-node-toggle>
<span [innerHTML]="node.name | nzHighlight: searchValue : 'i' : 'highlight'"></span>
</nz-tree-node>
<nz-tree-node *nzTreeNodeDef="let node; when: hasChild" nzTreeNodePadding>
<nz-tree-node-toggle>
<nz-icon nzType="caret-down" nzTreeNodeToggleRotateIcon />
</nz-tree-node-toggle>
<span [innerHTML]="node.name | nzHighlight: searchValue : 'i' : 'highlight'"></span>
</nz-tree-node>
</nz-tree-view>
`<% } else { %>templateUrl: './<%= dasherize(name) %>.component.html'<% } %>,
<% if(inlineStyle) { %>styles: [`
nz-input-group {
margin-bottom: 8px;
}
::ng-deep .highlight {
color: #f50;
}
`]<% } else { %>styleUrls: ['./<%= dasherize(name) %>.component.<%= style %>']<% } %>
})
export class <%= classify(name) %>Component {
flatNodeMap = new Map<FlatNode, TreeNode>();
nestedNodeMap = new Map<TreeNode, FlatNode>();
expandedNodes: TreeNode[] = [];
searchValue = '';
originData$ = new BehaviorSubject(TREE_DATA);
searchValue$ = new BehaviorSubject<string>('');
transformer = (node: TreeNode, level: number): FlatNode => {
const existingNode = this.nestedNodeMap.get(node);
const flatNode =
existingNode && existingNode.name === node.name
? existingNode
: {
expandable: !!node.children && node.children.length > 0,
name: node.name,
level
};
this.flatNodeMap.set(flatNode, node);
this.nestedNodeMap.set(node, flatNode);
return flatNode;
};
treeControl = new FlatTreeControl<FlatNode, TreeNode>(
node => node.level,
node => node.expandable,
{
trackBy: flatNode => this.flatNodeMap.get(flatNode)!
}
);
treeFlattener = new NzTreeFlattener<TreeNode, FlatNode, TreeNode>(
this.transformer,
node => node.level,
node => node.expandable,
node => node.children
);
dataSource = new NzTreeFlatDataSource(this.treeControl, this.treeFlattener);
filteredData$ = combineLatest([
this.originData$,
this.searchValue$.pipe(
auditTime(300),
map(value => (this.searchValue = value))
)
]).pipe(map(([data, value]) => (value ? filterTreeData(data, value) : new FilteredTreeResult(data))));
constructor() {
this.filteredData$.subscribe(result => {
this.dataSource.setData(result.treeData);
const hasSearchValue = !!this.searchValue;
if (hasSearchValue) {
if (this.expandedNodes.length === 0) {
this.expandedNodes = this.treeControl.expansionModel.selected;
this.treeControl.expansionModel.clear();
}
this.treeControl.expansionModel.select(...result.needsToExpanded);
} else {
if (this.expandedNodes.length) {
this.treeControl.expansionModel.clear();
this.treeControl.expansionModel.select(...this.expandedNodes);
this.expandedNodes = [];
}
}
});
}
hasChild = (_: number, node: FlatNode): boolean => node.expandable;
}

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
const schematics_1 = require("@angular-devkit/schematics");
const build_component_1 = require("../../utils/build-component");
function default_1(options) {
return (0, schematics_1.chain)([
(0, build_component_1.buildComponent)(Object.assign({}, options), {
template: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html.template',
stylesheet: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__style__.template'
})
]);
}
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../schematics/demo/tree-view-search/index.ts"],"names":[],"mappings":";;AAQA,4BAUC;AAlBD,2DAGoC;AACpC,iEAA6D;AAI7D,mBAAwB,OAAe;IACrC,OAAO,IAAA,kBAAK,EAAC;QACX,IAAA,gCAAc,oBACP,OAAO,GACZ;YACE,QAAQ,EAAE,kFAAkF;YAC5F,UAAU,EAAE,uFAAuF;SACpG,CACF;KACF,CAAC,CAAC;AACL,CAAC"}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=schema.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../../schematics/demo/tree-view-search/schema.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1 @@
{"$schema":"http://json-schema.org/schema","$id":"search-tree-view","title":"NG-ZORRO search tree-view","type":"object","properties":{"path":{"type":"string","format":"path","description":"The path to create the component.","visible":false},"project":{"type":"string","description":"The name of the project.","$default":{"$source":"projectName"}},"name":{"type":"string","description":"The name of the component.","$default":{"$source":"argv","index":0},"x-prompt":"What should be the name of the component?"},"inlineStyle":{"description":"When true, includes styles inline in the component.ts file. Only CSS styles can be included inline. By default, an external styles file is created and referenced in the component.ts file.","type":"boolean","default":false,"alias":"s"},"inlineTemplate":{"description":"When true, includes template inline in the component.ts file. By default, an external template file is created and referenced in the component.ts file.","type":"boolean","default":false,"alias":"t"},"standalone":{"description":"Whether the generated component is standalone.","type":"boolean"},"prefix":{"type":"string","description":"The prefix to apply to the generated component selector.","alias":"p","oneOf":[{"maxLength":0},{"minLength":1,"format":"html-selector"}]},"styleext":{"description":"The file extension to use for style files.","type":"string","default":"css","x-deprecated":"Use \"style\" instead."},"style":{"description":"The file extension or preprocessor to use for style files.","type":"string","default":"css","enum":["css","scss","sass","less","styl"]},"spec":{"type":"boolean","description":"When true (the default), generates a \"spec.ts\" test file for the new component.","default":true,"x-deprecated":"Use \"skipTests\" instead."},"skipTests":{"type":"boolean","description":"When true, does not create \"spec.ts\" test files for the new component."},"flat":{"type":"boolean","description":"Flag to indicate if a dir is created.","default":false},"skipImport":{"type":"boolean","description":"When true, does not import this component into the owning NgModule."},"selector":{"type":"string","format":"html-selector","description":"The selector to use for the component."},"module":{"type":"string","description":"Allows specification of the declaring module.","alias":"m"},"export":{"type":"boolean","default":false,"description":"When true, the declaring NgModule exports this component."},"entryComponent":{"type":"boolean","default":false,"description":"When true, the new component is the entry component of the declaring NgModule."},"classnameWithModule":{"type":"boolean","description":"When true, Use module class name as additional prefix for the component classname.","default":false}},"required":["name"]}