Skip to content

Commit

Permalink
feat(module:typography): add typography component (NG-ZORRO#3119)
Browse files Browse the repository at this point in the history
* feat(module:typography): add typography component

* test(module:typography): add test

* style: fix lint

* docs: add static path

* test(module:typography): add test

* docs: fix API

* test(module:typography): fix ci test

* test(module:typography): fix ci test

* fix(module:typography): fix ellipsis content

* Update components/components.less

Co-Authored-By: vthinkxie <vthinkxie@users.noreply.github.com>
  • Loading branch information
2 people authored and Ricbet committed Apr 9, 2020
1 parent b08db92 commit 5f0938b
Show file tree
Hide file tree
Showing 46 changed files with 1,909 additions and 2 deletions.
1 change: 1 addition & 0 deletions components/components.less
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
@import "./timeline/style/entry.less";
@import "./tooltip/style/entry.less";
@import "./transfer/style/entry.less";
@import "./typography/style/entry.less";
@import "./upload/style/entry.less";
@import "./auto-complete/style/entry.less";
@import "./cascader/style/entry.less";
Expand Down
1 change: 1 addition & 0 deletions components/core/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export * from './wave/public-api';
export * from './dropdown/public-api';
export * from './logger/public-api';
export * from './responsive/public-api';
export * from './trans-button/public-api';
52 changes: 52 additions & 0 deletions components/core/services/nz-copy-to-clipboard.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* @license
* Copyright Alibaba.com All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { DOCUMENT } from '@angular/common';
import { Inject, Injectable } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class NzCopyToClipboardService {
// tslint:disable-next-line:no-any
constructor(@Inject(DOCUMENT) private document: any) {}

copy(text: string): Promise<string> {
return new Promise<string>(
(resolve, reject): void => {
let copyTextArea = null;
try {
// tslint:disable-next-line no-any
copyTextArea = this.document.createElement('textarea') as any;
copyTextArea.style!.all = 'unset';
copyTextArea.style.position = 'fixed';
copyTextArea.style.top = '0';
copyTextArea.style.clip = 'rect(0, 0, 0, 0)';
copyTextArea.style.whiteSpace = 'pre';
copyTextArea.style.webkitUserSelect = 'text';
copyTextArea.style!.MozUserSelect = 'text';
copyTextArea.style.msUserSelect = 'text';
copyTextArea.style.userSelect = 'text';
this.document.body.appendChild(copyTextArea);
copyTextArea.value = text;
copyTextArea.select();

const successful = this.document.execCommand('copy');
if (!successful) {
reject(text);
}
resolve(text);
} finally {
if (copyTextArea) {
this.document.body.removeChild(copyTextArea);
}
}
}
);
}
}
1 change: 1 addition & 0 deletions components/core/services/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@

export * from './nz-measure-scrollbar.service';
export * from './update-host-class.service';
export * from './nz-copy-to-clipboard.service';
9 changes: 9 additions & 0 deletions components/core/trans-button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license
* Copyright Alibaba.com All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

export * from './public-api';
20 changes: 20 additions & 0 deletions components/core/trans-button/nz-trans-button.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @license
* Copyright Alibaba.com All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { Directive } from '@angular/core';

@Directive({
selector: 'button[nz-trans-button]',
host: {
'[style.border]': '"0"',
'[style.background]': '"transparent"',
'[style.padding]': '"0"',
'[style.line-height]': '"inherit"'
}
})
export class NzTransButtonDirective {}
9 changes: 9 additions & 0 deletions components/core/trans-button/public-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license
* Copyright Alibaba.com All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

export { NzTransButtonDirective } from './nz-trans-button.directive';
2 changes: 2 additions & 0 deletions components/core/util/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ export * from './scroll-into-view-if-needed';
export * from './textarea-caret-position';
export * from './throttleByAnimationFrame';
export * from './time';
export * from './style-checke';
export * from './text-measure';
17 changes: 17 additions & 0 deletions components/core/util/style-checke.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @license
* Copyright Alibaba.com All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

export function isStyleSupport(styleName: string | string[]): boolean {
if (typeof window !== 'undefined' && window.document && window.document.documentElement) {
const styleNameList = Array.isArray(styleName) ? styleName : [styleName];
const { documentElement } = window.document;

return styleNameList.some(name => name in documentElement.style);
}
return false;
}
244 changes: 244 additions & 0 deletions components/core/util/text-measure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/**
* @license
* Copyright Alibaba.com All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

export interface MeasureResult {
finished: boolean;
node: Node | null;
}

// We only handle element & text node.
const ELEMENT_NODE = 1;
const TEXT_NODE = 3;
const COMMENT_NODE = 8;

let ellipsisContainer: HTMLParagraphElement;

const wrapperStyle = {
padding: '0',
margin: '0',
display: 'inline',
lineHeight: 'inherit'
};

function pxToNumber(value: string | null): number {
if (!value) {
return 0;
}

const match = value.match(/^\d*(\.\d*)?/);

return match ? Number(match[0]) : 0;
}

function styleToString(style: CSSStyleDeclaration): string {
// There are some different behavior between Firefox & Chrome.
// We have to handle this ourself.
const styleNames: string[] = Array.prototype.slice.apply(style);
return styleNames.map(name => `${name}: ${style.getPropertyValue(name)};`).join('');
}

function mergeChildren(children: Node[]): Node[] {
const childList: Node[] = [];

children.forEach((child: Node) => {
const prevChild = childList[childList.length - 1];
if (prevChild && child.nodeType === TEXT_NODE && prevChild.nodeType === TEXT_NODE) {
(prevChild as Text).data += (child as Text).data;
} else {
childList.push(child);
}
});

return childList;
}

export function measure(
originEle: HTMLElement,
rows: number,
contentNodes: Node[],
fixedContent: HTMLElement[],
ellipsisStr: string
): { contentNodes: Node[]; text: string; ellipsis: boolean } {
if (!ellipsisContainer) {
ellipsisContainer = document.createElement('div');
ellipsisContainer.setAttribute('aria-hidden', 'true');
document.body.appendChild(ellipsisContainer);
}

// Get origin style
const originStyle = window.getComputedStyle(originEle);
const originCSS = styleToString(originStyle);
const lineHeight = pxToNumber(originStyle.lineHeight);
const maxHeight =
lineHeight * (rows + 1) + pxToNumber(originStyle.paddingTop) + pxToNumber(originStyle.paddingBottom);
// Set shadow
ellipsisContainer.setAttribute('style', originCSS);
ellipsisContainer.style.position = 'fixed';
ellipsisContainer.style.left = '0';
ellipsisContainer.style.height = 'auto';
ellipsisContainer.style.minHeight = 'auto';
ellipsisContainer.style.maxHeight = 'auto';
ellipsisContainer.style.top = '-999999px';
ellipsisContainer.style.zIndex = '-1000';

// clean up css overflow
ellipsisContainer.style.textOverflow = 'clip';
ellipsisContainer.style.whiteSpace = 'normal';
// tslint:disable-next-line no-any
(ellipsisContainer.style as any).webkitLineClamp = 'none';

const contentList = mergeChildren(contentNodes);
const container = document.createElement('div');
const contentContainer = document.createElement('span');
const fixedContainer = document.createElement('span');

// Add styles in container
Object.assign(container.style, wrapperStyle);
Object.assign(contentContainer.style, wrapperStyle);
Object.assign(fixedContainer.style, wrapperStyle);

contentList.forEach(n => {
contentContainer.appendChild(n);
});
fixedContent.forEach(node => {
fixedContainer.appendChild(node.cloneNode(true));
});
container.appendChild(contentContainer);
container.appendChild(fixedContainer);

// Render in the fake container
ellipsisContainer.appendChild(container);

// Check if ellipsis in measure div is height enough for content
function inRange(): boolean {
return ellipsisContainer.offsetHeight < maxHeight;
}

if (inRange()) {
const text = ellipsisContainer.innerHTML;
ellipsisContainer.removeChild(container);
return { contentNodes, text, ellipsis: false };
}

// We should clone the childNode since they're controlled by React and we can't reuse it without warning
const childNodes: ChildNode[] = Array.prototype.slice
.apply(ellipsisContainer.childNodes[0].childNodes[0].cloneNode(true).childNodes)
.filter(({ nodeType }: ChildNode) => nodeType !== COMMENT_NODE);
const fixedNodes: ChildNode[] = Array.prototype.slice.apply(
ellipsisContainer.childNodes[0].childNodes[1].cloneNode(true).childNodes
);
ellipsisContainer.removeChild(container);

// ========================= Find match ellipsis content =========================
ellipsisContainer.innerHTML = '';

// Create origin content holder
const ellipsisContentHolder = document.createElement('span');
ellipsisContainer.appendChild(ellipsisContentHolder);
const ellipsisTextNode = document.createTextNode(ellipsisStr);
ellipsisContentHolder.appendChild(ellipsisTextNode);

fixedNodes.forEach(childNode => {
ellipsisContainer.appendChild(childNode);
});

// Append before fixed nodes
function appendChildNode(node: ChildNode): void {
ellipsisContentHolder.insertBefore(node, ellipsisTextNode);
}

// Get maximum text
function measureText(
textNode: Text,
fullText: string,
startLoc: number = 0,
endLoc: number = fullText.length,
lastSuccessLoc: number = 0
): MeasureResult {
const midLoc = Math.floor((startLoc + endLoc) / 2);
const currentText = fullText.slice(0, midLoc);
textNode.textContent = currentText;

if (startLoc >= endLoc - 1) {
// Loop when step is small
for (let step = endLoc; step >= startLoc; step -= 1) {
const currentStepText = fullText.slice(0, step);
textNode.textContent = currentStepText;

if (inRange()) {
return step === fullText.length
? {
finished: false,
node: document.createTextNode(fullText)
}
: {
finished: true,
node: document.createTextNode(currentStepText)
};
}
}
}
if (inRange()) {
return measureText(textNode, fullText, midLoc, endLoc, midLoc);
} else {
return measureText(textNode, fullText, startLoc, midLoc, lastSuccessLoc);
}
}

function measureNode(childNode: ChildNode, index: number): MeasureResult {
const type = childNode.nodeType;

if (type === ELEMENT_NODE) {
// We don't split element, it will keep if whole element can be displayed.
// appendChildNode(childNode);
if (inRange()) {
return {
finished: false,
node: contentList[index]
};
}

// Clean up if can not pull in
ellipsisContentHolder.removeChild(childNode);
return {
finished: true,
node: null
};
} else if (type === TEXT_NODE) {
const fullText = childNode.textContent || '';
const textNode = document.createTextNode(fullText);
appendChildNode(textNode);
return measureText(textNode, fullText);
}

// Not handle other type of content
// PS: This code should not be attached after react 16
return {
finished: false,
node: null
};
}

const ellipsisNodes: Node[] = [];
childNodes.some((childNode, index) => {
const { finished, node } = measureNode(childNode, index);
if (node) {
ellipsisNodes.push(node);
}
return finished;
});
const result = {
contentNodes: ellipsisNodes,
text: ellipsisContainer.innerHTML,
ellipsis: true
};
while (ellipsisContainer.firstChild) {
ellipsisContainer.removeChild(ellipsisContainer.firstChild);
}
return result;
}
9 changes: 9 additions & 0 deletions components/i18n/languages/en_US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,14 @@ export default {
},
Empty: {
description: 'No Data'
},
Text: {
edit: 'edit',
copy: 'copy',
copied: 'copy success',
expand: 'expand'
},
PageHeader: {
back: 'back'
}
};
Loading

0 comments on commit 5f0938b

Please sign in to comment.