mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(tree): support units different than px for indentation (#12991)
Currently the tree assumes that the indentation level will always be in pixels, however this makes it hard to work with on responsive layouts. These changes add support for different CSS units.
This commit is contained in:
committed by
mmalerba
parent
4e05745ed6
commit
590d2949d1
+23
-5
@@ -13,6 +13,9 @@ import {takeUntil} from 'rxjs/operators';
|
||||
import {Subject} from 'rxjs';
|
||||
import {CdkTree, CdkTreeNode} from './tree';
|
||||
|
||||
/** Regex used to split a string on its CSS units. */
|
||||
const cssUnitPattern = /([A-Za-z%]+)$/;
|
||||
|
||||
/**
|
||||
* Indent for the children tree dataNodes.
|
||||
* This directive will add left-padding to the node to show hierarchy.
|
||||
@@ -24,6 +27,9 @@ export class CdkTreeNodePadding<T> implements OnDestroy {
|
||||
/** Subject that emits when the component has been destroyed. */
|
||||
private _destroyed = new Subject<void>();
|
||||
|
||||
/** CSS units used for the indentation value. */
|
||||
indentUnits = 'px';
|
||||
|
||||
/** The level of depth of the tree node. The padding will be `level * indent` pixels. */
|
||||
@Input('cdkTreeNodePadding')
|
||||
get level(): number { return this._level; }
|
||||
@@ -33,11 +39,23 @@ export class CdkTreeNodePadding<T> implements OnDestroy {
|
||||
}
|
||||
_level: number;
|
||||
|
||||
/** The indent for each level. Default number 40px from material design menu sub-menu spec. */
|
||||
// TODO(tinayuangao): Make indent working with a string with unit, e.g. 10em
|
||||
/**
|
||||
* The indent for each level. Can be a number or a CSS string.
|
||||
* Default number 40px from material design menu sub-menu spec.
|
||||
*/
|
||||
@Input('cdkTreeNodePaddingIndent')
|
||||
get indent(): number { return this._indent; }
|
||||
set indent(value: number) {
|
||||
get indent(): number | string { return this._indent; }
|
||||
set indent(indent: number | string) {
|
||||
let value = indent;
|
||||
let units = 'px';
|
||||
|
||||
if (typeof indent === 'string') {
|
||||
const parts = indent.split(cssUnitPattern);
|
||||
value = parts[0];
|
||||
units = parts[1] || units;
|
||||
}
|
||||
|
||||
this.indentUnits = units;
|
||||
this._indent = coerceNumberProperty(value);
|
||||
this._setPadding();
|
||||
}
|
||||
@@ -65,7 +83,7 @@ export class CdkTreeNodePadding<T> implements OnDestroy {
|
||||
? this._tree.treeControl.getLevel(this._treeNode.data)
|
||||
: null;
|
||||
const level = this._level || nodeLevel;
|
||||
return level ? `${level * this._indent}px` : null;
|
||||
return level ? `${level * this._indent}${this.indentUnits}` : null;
|
||||
}
|
||||
|
||||
_setPadding() {
|
||||
|
||||
@@ -62,7 +62,7 @@ A node definition is specified via any element with `cdkNodeDef`. This directive
|
||||
data to be used in any bindings in the node template.
|
||||
|
||||
```html
|
||||
<cdk-tree-node *cdkNodeDef=“let node”>
|
||||
<cdk-tree-node *cdkNodeDef="let node">
|
||||
{{node.key}}: {{node.value}}
|
||||
</cdk-tree-node>
|
||||
```
|
||||
@@ -80,7 +80,7 @@ When using nested tree nodes, the node template must contain a `cdkTreeNodeOutle
|
||||
where the children of the node will be rendered.
|
||||
|
||||
```html
|
||||
<cdk-nested-tree-node *cdkNodeDef=“let node”>
|
||||
<cdk-nested-tree-node *cdkNodeDef="let node">
|
||||
{{node.value}}
|
||||
<ng-container cdkTreeNodeOutlet></ng-container>
|
||||
</cdk-nested-tree-node>
|
||||
@@ -94,7 +94,7 @@ The toggle toggles the expand/collapse functions in TreeControl and is able to e
|
||||
a tree node recursively by setting `[cdkTreeNodeToggleRecursive]` to true.
|
||||
|
||||
```html
|
||||
<cdk-tree-node *cdkNodeDef=“let node” cdkTreeNodeToggle [cdkTreeNodeToggleRecursive]="true">
|
||||
<cdk-tree-node *cdkNodeDef="let node" cdkTreeNodeToggle [cdkTreeNodeToggleRecursive]="true">
|
||||
{{node.value}}
|
||||
</cdk-tree-node>
|
||||
```
|
||||
@@ -104,7 +104,7 @@ For best accessibility, `cdkTreeNodeToggle` should be on a button element and ha
|
||||
`aria-label`.
|
||||
|
||||
```html
|
||||
<cdk-tree-node *cdkNodeDef=“let node”>
|
||||
<cdk-tree-node *cdkNodeDef="let node">
|
||||
<button cdkTreeNodeToggle aria-label="toggle tree node" [cdkTreeNodeToggleRecursive]="true">
|
||||
<mat-icon>expand</mat-icon>
|
||||
</button>
|
||||
@@ -118,7 +118,7 @@ The cdkTreeNodePadding can be placed in a flat tree's node template to display t
|
||||
information of a flat tree node.
|
||||
|
||||
```html
|
||||
<cdk-tree-node *cdkNodeDef=“let node” cdkNodePadding>
|
||||
<cdk-tree-node *cdkNodeDef="let node" cdkNodePadding>
|
||||
{{node.value}}
|
||||
</cdk-tree-node>
|
||||
|
||||
@@ -134,10 +134,10 @@ for a particular data node via the `when` predicate of the template.
|
||||
|
||||
|
||||
```html
|
||||
<cdk-tree-node *cdkNodeDef=“let node” cdkTreeNodePadding>
|
||||
<cdk-tree-node *cdkNodeDef="let node" cdkTreeNodePadding>
|
||||
{{node.value}}
|
||||
</cdk-tree-node>
|
||||
<cdk-tree-node *cdkNodeDef=“let node; when: isSpecial” cdkTreeNodePadding>
|
||||
<cdk-tree-node *cdkNodeDef="let node; when: isSpecial" cdkTreeNodePadding>
|
||||
[ A special node {{node.value}} ]
|
||||
</cdk-tree-node>
|
||||
```
|
||||
|
||||
+41
-14
@@ -90,7 +90,7 @@ describe('CdkTree', () => {
|
||||
expect(dataSource.data.length).toBe(3);
|
||||
|
||||
let data = dataSource.data;
|
||||
expectFlatTreeToMatch(treeElement, 28,
|
||||
expectFlatTreeToMatch(treeElement, 28, 'px',
|
||||
[`${data[0].pizzaTopping} - ${data[0].pizzaCheese} + ${data[0].pizzaBase}`],
|
||||
[`${data[1].pizzaTopping} - ${data[1].pizzaCheese} + ${data[1].pizzaBase}`],
|
||||
[`${data[2].pizzaTopping} - ${data[2].pizzaCheese} + ${data[2].pizzaBase}`]);
|
||||
@@ -100,12 +100,37 @@ describe('CdkTree', () => {
|
||||
|
||||
data = dataSource.data;
|
||||
expect(data.length).toBe(4);
|
||||
expectFlatTreeToMatch(treeElement, 28,
|
||||
expectFlatTreeToMatch(treeElement, 28, 'px',
|
||||
[`${data[0].pizzaTopping} - ${data[0].pizzaCheese} + ${data[0].pizzaBase}`],
|
||||
[`${data[1].pizzaTopping} - ${data[1].pizzaCheese} + ${data[1].pizzaBase}`],
|
||||
[`${data[2].pizzaTopping} - ${data[2].pizzaCheese} + ${data[2].pizzaBase}`],
|
||||
[_, `${data[3].pizzaTopping} - ${data[3].pizzaCheese} + ${data[3].pizzaBase}`]);
|
||||
});
|
||||
|
||||
it('should be able to use units different from px for the indentation', () => {
|
||||
component.indent = '15rem';
|
||||
fixture.detectChanges();
|
||||
|
||||
const data = dataSource.data;
|
||||
|
||||
expectFlatTreeToMatch(treeElement, 15, 'rem',
|
||||
[`${data[0].pizzaTopping} - ${data[0].pizzaCheese} + ${data[0].pizzaBase}`],
|
||||
[`${data[1].pizzaTopping} - ${data[1].pizzaCheese} + ${data[1].pizzaBase}`],
|
||||
[`${data[2].pizzaTopping} - ${data[2].pizzaCheese} + ${data[2].pizzaBase}`]);
|
||||
});
|
||||
|
||||
it('should default to px if no unit is set for string value indentation', () => {
|
||||
component.indent = '17';
|
||||
fixture.detectChanges();
|
||||
|
||||
const data = dataSource.data;
|
||||
|
||||
expectFlatTreeToMatch(treeElement, 17, 'px',
|
||||
[`${data[0].pizzaTopping} - ${data[0].pizzaCheese} + ${data[0].pizzaBase}`],
|
||||
[`${data[1].pizzaTopping} - ${data[1].pizzaCheese} + ${data[1].pizzaBase}`],
|
||||
[`${data[2].pizzaTopping} - ${data[2].pizzaCheese} + ${data[2].pizzaBase}`]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with toggle', () => {
|
||||
@@ -137,7 +162,7 @@ describe('CdkTree', () => {
|
||||
|
||||
data = dataSource.data;
|
||||
expect(data.length).toBe(4);
|
||||
expectFlatTreeToMatch(treeElement, 40,
|
||||
expectFlatTreeToMatch(treeElement, 40, 'px',
|
||||
[`${data[0].pizzaTopping} - ${data[0].pizzaCheese} + ${data[0].pizzaBase}`],
|
||||
[`${data[1].pizzaTopping} - ${data[1].pizzaCheese} + ${data[1].pizzaBase}`],
|
||||
[`${data[2].pizzaTopping} - ${data[2].pizzaCheese} + ${data[2].pizzaBase}`],
|
||||
@@ -170,7 +195,7 @@ describe('CdkTree', () => {
|
||||
|
||||
data = dataSource.data;
|
||||
expect(data.length).toBe(4);
|
||||
expectFlatTreeToMatch(treeElement, 40,
|
||||
expectFlatTreeToMatch(treeElement, 40, 'px',
|
||||
[`${data[0].pizzaTopping} - ${data[0].pizzaCheese} + ${data[0].pizzaBase}`],
|
||||
[`${data[1].pizzaTopping} - ${data[1].pizzaCheese} + ${data[1].pizzaBase}`],
|
||||
[`${data[2].pizzaTopping} - ${data[2].pizzaCheese} + ${data[2].pizzaBase}`],
|
||||
@@ -214,7 +239,7 @@ describe('CdkTree', () => {
|
||||
expect(dataSource.data.length).toBe(3);
|
||||
|
||||
let data = dataSource.data;
|
||||
expectFlatTreeToMatch(treeElement, 28,
|
||||
expectFlatTreeToMatch(treeElement, 28, 'px',
|
||||
[`[topping_1] - [cheese_1] + [base_1]`],
|
||||
[`[topping_2] - [cheese_2] + [base_2]`],
|
||||
[`[topping_3] - [cheese_3] + [base_3]`]);
|
||||
@@ -225,7 +250,7 @@ describe('CdkTree', () => {
|
||||
treeElement = fixture.nativeElement.querySelector('cdk-tree');
|
||||
data = dataSource.data;
|
||||
expect(data.length).toBe(4);
|
||||
expectFlatTreeToMatch(treeElement, 28,
|
||||
expectFlatTreeToMatch(treeElement, 28, 'px',
|
||||
[`[topping_1] - [cheese_1] + [base_1]`],
|
||||
[`[topping_2] - [cheese_2] + [base_2]`],
|
||||
[_, `topping_4 - cheese_4 + base_4`],
|
||||
@@ -253,7 +278,7 @@ describe('CdkTree', () => {
|
||||
expect(dataSource.data.length).toBe(3);
|
||||
|
||||
let data = dataSource.data;
|
||||
expectFlatTreeToMatch(treeElement, 28,
|
||||
expectFlatTreeToMatch(treeElement, 28, 'px',
|
||||
[`[topping_1] - [cheese_1] + [base_1]`],
|
||||
[`[topping_2] - [cheese_2] + [base_2]`],
|
||||
[`[topping_3] - [cheese_3] + [base_3]`]);
|
||||
@@ -264,7 +289,7 @@ describe('CdkTree', () => {
|
||||
treeElement = fixture.nativeElement.querySelector('cdk-tree');
|
||||
data = dataSource.data;
|
||||
expect(data.length).toBe(4);
|
||||
expectFlatTreeToMatch(treeElement, 28,
|
||||
expectFlatTreeToMatch(treeElement, 28, 'px',
|
||||
[`[topping_1] - [cheese_1] + [base_1]`],
|
||||
[`[topping_2] - [cheese_2] + [base_2]`],
|
||||
[_, `[topping_4] - [cheese_4] + [base_4]`],
|
||||
@@ -292,7 +317,7 @@ describe('CdkTree', () => {
|
||||
expect(dataSource.data.length).toBe(3);
|
||||
|
||||
let data = dataSource.data;
|
||||
expectFlatTreeToMatch(treeElement, 28,
|
||||
expectFlatTreeToMatch(treeElement, 28, 'px',
|
||||
[`[topping_1] - [cheese_1] + [base_1]`],
|
||||
[`[topping_2] - [cheese_2] + [base_2]`],
|
||||
[`[topping_3] - [cheese_3] + [base_3]`]);
|
||||
@@ -303,7 +328,7 @@ describe('CdkTree', () => {
|
||||
treeElement = fixture.nativeElement.querySelector('cdk-tree');
|
||||
data = dataSource.data;
|
||||
expect(data.length).toBe(4);
|
||||
expectFlatTreeToMatch(treeElement, 28,
|
||||
expectFlatTreeToMatch(treeElement, 28, 'px',
|
||||
[`[topping_1] - [cheese_1] + [base_1]`],
|
||||
[`[topping_2] - [cheese_2] + [base_2]`],
|
||||
[_, `[topping_4] - [cheese_4] + [base_4]`],
|
||||
@@ -990,7 +1015,9 @@ function getNodes(treeElement: Element): Element[] {
|
||||
return [].slice.call(treeElement.querySelectorAll('.cdk-tree-node'))!;
|
||||
}
|
||||
|
||||
function expectFlatTreeToMatch(treeElement: Element, expectedPaddingIndent: number = 28,
|
||||
function expectFlatTreeToMatch(treeElement: Element,
|
||||
expectedPaddingIndent = 28,
|
||||
expectedPaddingUnits = 'px',
|
||||
...expectedTree: any[]) {
|
||||
const missedExpectations: string[] = [];
|
||||
|
||||
@@ -1005,7 +1032,7 @@ function expectFlatTreeToMatch(treeElement: Element, expectedPaddingIndent: numb
|
||||
|
||||
function checkLevel(node: Element, expectedNode: any[]) {
|
||||
const actualLevel = (node as HTMLElement).style.paddingLeft;
|
||||
const expectedLevel = `${(expectedNode.length) * expectedPaddingIndent}px`;
|
||||
const expectedLevel = `${(expectedNode.length) * expectedPaddingIndent}${expectedPaddingUnits}`;
|
||||
if (actualLevel != expectedLevel) {
|
||||
missedExpectations.push(
|
||||
`Expected node level to be ${expectedLevel} but was ${actualLevel}`);
|
||||
@@ -1074,7 +1101,7 @@ function expectNestedTreeToMatch(treeElement: Element, ...expectedTree: any[]) {
|
||||
template: `
|
||||
<cdk-tree [dataSource]="dataSource" [treeControl]="treeControl">
|
||||
<cdk-tree-node *cdkTreeNodeDef="let node" class="customNodeClass"
|
||||
cdkTreeNodePadding [cdkTreeNodePaddingIndent]="28"
|
||||
cdkTreeNodePadding [cdkTreeNodePaddingIndent]="indent"
|
||||
cdkTreeNodeToggle>
|
||||
{{node.pizzaTopping}} - {{node.pizzaCheese}} + {{node.pizzaBase}}
|
||||
</cdk-tree-node>
|
||||
@@ -1086,8 +1113,8 @@ class SimpleCdkTreeApp {
|
||||
isExpandable = (node: TestData) => node.children.length > 0;
|
||||
|
||||
treeControl: TreeControl<TestData> = new FlatTreeControl(this.getLevel, this.isExpandable);
|
||||
|
||||
dataSource: FakeDataSource | null = new FakeDataSource(this.treeControl);
|
||||
indent: number | string = 28;
|
||||
|
||||
@ViewChild(CdkTree) tree: CdkTree<TestData>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user