diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..38981d2
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,20 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "chrome",
+ "request": "attach",
+ "name": "Unit tests",
+ "address": "localhost",
+ "port": 9333,
+ "sourceMaps": true,
+ "webRoot": "${workspaceFolder}",
+ "pathMapping": {
+ "/_karma_webpack_": "${workspaceFolder}"
+ }
+ }
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 864b7ce..40aae00 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,5 @@
{
"prettier.configPath": "./src/.prettierrc",
- "editor.formatOnSave": true
+ "editor.formatOnSave": true,
+ "javascript.implicitProjectConfig.experimentalDecorators": true
}
\ No newline at end of file
diff --git a/README.md b/README.md
index 7fd5e35..f6a79b3 100644
--- a/README.md
+++ b/README.md
@@ -1,41 +1,45 @@
# ng2-fittext
-An Angular2 directive written in pure typescript (and without jquery!), for autoscale the font size of an element to fit an upper level container.
+An Angular2 directive written in pure typescript (and without jquery!), to autoscale the font size of an element so that it fits an upper level container.
### Demo
+
http://plnkr.co/edit/v0TQaYepV4E2Heur02j5?p=preview
### Installation
Install the library
+
```sh
$ npm install --save ng2-fittext
```
### Usage
-1) Declare it in your module
- ```sh
- import {Ng2FittextModule} from "ng2-fittext";
- @NgModule({
- imports: [
- Ng2FittextModule
- ]
- })
+
+1. Declare it in your module
+ ```sh
+ import {Ng2FittextModule} from "ng2-fittext";
+ @NgModule({
+ imports: [
+ Ng2FittextModule
+ ]
+ })
```
-2) Use it in your components
- ```sh
+2. Use it in your components
+ ```sh
import {Component} from '@angular/core';
- @Component({
- selector: 'label',
- template: `
`
- })
- export class LabelComponent {}
+ @Component({
+ selector: 'label',
+ template: ``
+ })
+ export class LabelComponent {}
```
### Examples
-Fit with the parent element (this works if you have a variable number of element between element and parent)
+
+Fit to the parent element (this works if you have a variable number of elements between your element and its parent)
```sh
import {Component} from '@angular/core';
@@ -61,6 +65,7 @@ import {Component} from '@angular/core';
export class InputBoxComponent {}
```
+
**NEW! Support for maxFontSize!**
```sh
@@ -74,46 +79,40 @@ import {Component} from '@angular/core';
export class InputBoxComponent {}
```
+Input Parameters:
- Input Parameters:
+| Parameter | Description | Values |
+| ------------------------------- | --------------------------------------------------------------------------------------- | ----------------------------- |
+| fittext | the directive selector | true/false |
+| container | the container to fit (if not present it fits to the parent container by default) | ElementRef |
+| activateOnResize | enable/disable the autofit in case of window resize | true or false (default false) |
+| activateOnInputEvents | enable/disable the autofit in case of input box events (keydown, keyup etc..) | true or false (default false) |
+| maxFontSize | maximum font size | number, default is 1000 |
+| **!deprecated!** useMaxFontSize | use max font size if is true | deprecated! |
+| minFontSize | minimum font size | number, default is 7 |
+| modelToWatch | pass model to watch, when this model changes -> font size is automatically recalculated | any type of model |
- | Parameter | Description | Values |
- | --- | --- | --- |
- | fittext | is the selector of the directive | true/false
- | container | the container to fit (if not present it fit default to parent container)| ElementRef
- | activateOnResize | enable/disable the autofit in case of window resize | true or false (default false)
- | activateOnInputEvents | enbale/disable the autofit in case of input box events (keydown, keyup etc..) | true or false (default false)
- | maxFontSize | maximal font size | number, default is 1000
- | **!deprecated!** useMaxFontSize | use max font size if is true | deprecated!
- | minFontSize | minimal font size | number, default is 7
- | modelToWatch | pass model to watch, when this model changes -> font size is automatically recalculated | any type of model
+Output Parameters:
+| Parameter | Description | Values |
+| --------------- | ----------------- | ------ |
+| fontSizeChanged | current font size | string |
- Output Parameters:
-
- | Parameter | Description | Values |
- | --- | --- | --- |
- | fontSizeChanged | current font size | string
-
### Development
Want to contribute? Great!
Simply, clone the repository!
-I created this library because I always spended too much time for solve this problem and because i didn't find nothing on the web (13/03/2017) that do this without jquery and easily integrable in angular2.
-For sure is not a good implementation, maybe is not the best way to do it, but, it do the job.
+I created this library because I always spent too much time to solve this problem and didn't find anything on the web (13/03/2017) that does this without jquery and that is also easily integrable in angular2.
+For sure it is not the best implementation, maybe is not the best way to do it, but, it gets the job done.
### Todos
- - Write tests
- - Find a better algorithm to find the font-size who fits better the container
+- Write tests
+- Find a better algorithm to find the font-size who fits better the container
-License
-----
+## License
MIT
-
**Lorenzo I.**
-
-
diff --git a/karma.conf.js b/karma.conf.js
index b6e0042..13f7a1e 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -1,7 +1,7 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
-module.exports = function (config) {
+module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
@@ -10,22 +10,28 @@ module.exports = function (config) {
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
- require('@angular-devkit/build-angular/plugins/karma')
+ require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
- clearContext: false // leave Jasmine Spec Runner output visible in browser
+ clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
- fixWebpackSourcePaths: true
+ fixWebpackSourcePaths: true,
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
- browsers: ['Chrome'],
- singleRun: false
+ browsers: ['Chrome', 'ChromeHeadlessDebug'],
+ customLaunchers: {
+ ChromeHeadlessDebug: {
+ base: 'ChromeHeadless',
+ flags: ['--remote-debugging-port=9333'],
+ },
+ },
+ singleRun: false,
});
-};
\ No newline at end of file
+};
diff --git a/package.json b/package.json
index ca68a8d..f8c7333 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"start": "./node_modules/.bin/ng serve",
"build": "./node_modules/.bin/ng build --aot --prod",
"test": "./node_modules/.bin/ng test",
+ "test:headless": "./node_modules/.bin/ng test --browsers=ChromeHeadlessDebug",
"lint": "./node_modules/.bin/ng lint",
"e2e": "./node_modules/.bin/ng e2e",
"pack": "./node_modules/.bin/ng-packagr -p ./src/lib/package.json"
diff --git a/src/lib/directives/ng2-fittext.directive.ts b/src/lib/directives/ng2-fittext.directive.ts
index 3d7b7e6..24ee24e 100644
--- a/src/lib/directives/ng2-fittext.directive.ts
+++ b/src/lib/directives/ng2-fittext.directive.ts
@@ -29,7 +29,7 @@ export class Ng2FittextDirective
@Input('modelToWatch') modelToWatch: any;
- @Output() fontSizeChanged = new EventEmitter();
+ @Output() fontSizeChanged: EventEmitter = new EventEmitter();
private fontSize = 1000;
private speed = 1.05;
@@ -37,29 +37,32 @@ export class Ng2FittextDirective
constructor(public el: ElementRef, public renderer: Renderer2) {}
- setFontSize(fontSize: number) {
- if (this.isVisible() && !this.done) {
+ setFontSize(fontSize: number): void {
+ if (this.isVisible() && !this.isDone()) {
if (fontSize < this.minFontSize) {
fontSize = this.minFontSize;
}
if (fontSize > this.maxFontSize) {
fontSize = this.maxFontSize;
}
-
this.fontSize = fontSize;
this.fontSizeChanged.emit(fontSize);
- return this.el.nativeElement.style.setProperty(
+ this.el.nativeElement.style.setProperty(
'font-size',
fontSize.toString() + 'px'
);
}
}
- calculateFontSize(fontSize: number, speed: number) {
+ getFontSize(): number {
+ return this.fontSize;
+ }
+
+ calculateFontSize(fontSize: number, speed: number): number {
return Math.floor(fontSize / speed);
}
- checkOverflow(parent: any, children: any) {
+ checkOverflow(parent: any, children: any): boolean {
const overflowX = children.scrollWidth - parent.clientWidth;
const overflowY = children.clientHeight - parent.clientHeight;
return overflowX > 1 || overflowY > 1;
@@ -95,7 +98,7 @@ export class Ng2FittextDirective
}
ngAfterViewInit() {
- if (this.isVisible() && !this.done) {
+ if (this.isVisible() && !this.isDone()) {
if (this.fittext) {
const overflow = this.container
? this.checkOverflow(this.container, this.el.nativeElement)
@@ -134,7 +137,7 @@ export class Ng2FittextDirective
}
}
- private getStartFontSizeFromHeight(): number {
+ getStartFontSizeFromHeight(): number {
return this.container
? this.container.clientHeight
: this.el.nativeElement.parentElement.clientHeight;
@@ -146,7 +149,11 @@ export class Ng2FittextDirective
: this.el.nativeElement.parentElement.clientWidth;
}
- private isVisible(): boolean {
+ isDone(): boolean {
+ return this.done;
+ }
+
+ isVisible(): boolean {
return this.getStartFontSizeFromHeight() > 0;
}
}
diff --git a/src/lib/directives/specs/ng2-fittext.directive.spec.ts b/src/lib/directives/specs/ng2-fittext.directive.spec.ts
new file mode 100644
index 0000000..32c2ce7
--- /dev/null
+++ b/src/lib/directives/specs/ng2-fittext.directive.spec.ts
@@ -0,0 +1,197 @@
+import { Ng2FittextDirective } from '../ng2-fittext.directive';
+import { Renderer2, ElementRef } from '@angular/core';
+
+describe('Class: Ng2FittextDirective', () => {
+ let ng2FittextDirective: Ng2FittextDirective;
+ let elMock: ElementRef;
+ let rendererMock: Renderer2;
+
+ beforeEach(() => {
+ elMock = {} as ElementRef;
+ rendererMock = {} as Renderer2;
+ ng2FittextDirective = new Ng2FittextDirective(elMock, rendererMock);
+ });
+
+ describe('Method: setFontSize', () => {
+ let newFontSize: number;
+ let isVisibleSpy: jasmine.Spy;
+ let isDoneSpy: jasmine.Spy;
+
+ beforeEach(() => {
+ newFontSize = 100;
+ isVisibleSpy = spyOn(ng2FittextDirective, 'isVisible').and.returnValue(
+ true
+ );
+ isDoneSpy = spyOn(ng2FittextDirective, 'isDone').and.returnValue(false);
+ elMock.nativeElement = {
+ style: {
+ setProperty: () => {},
+ },
+ };
+ });
+
+ it('Should not change the font size if the element is not visible', () => {
+ isVisibleSpy.and.returnValue(false);
+ const previousFontSize: number = ng2FittextDirective.getFontSize();
+ ng2FittextDirective.setFontSize(newFontSize);
+ expect(ng2FittextDirective.getFontSize()).toEqual(previousFontSize);
+ });
+
+ it('Should not change the font size if the fitting operation is done', () => {
+ isDoneSpy.and.returnValue(true);
+ const previousFontSize: number = ng2FittextDirective.getFontSize();
+ ng2FittextDirective.setFontSize(newFontSize);
+ expect(ng2FittextDirective.getFontSize()).toEqual(previousFontSize);
+ });
+
+ it('Should use the minFontSize property value if the specified font size is smaller', () => {
+ const minFontSize: number = ng2FittextDirective.minFontSize;
+ newFontSize = 5;
+ ng2FittextDirective.setFontSize(newFontSize);
+ const currentFontSize: number = ng2FittextDirective.getFontSize();
+ expect(currentFontSize).toEqual(minFontSize);
+ });
+
+ it('Should use the maxFontSize property value if the specified font size is bigger', () => {
+ const maxFontSize: number = ng2FittextDirective.maxFontSize;
+ newFontSize = 1001;
+ ng2FittextDirective.setFontSize(newFontSize);
+ const currentFontSize: number = ng2FittextDirective.getFontSize();
+ expect(currentFontSize).toEqual(maxFontSize);
+ });
+
+ it('Should set a new fontSize value', () => {
+ newFontSize = 500;
+ ng2FittextDirective.setFontSize(newFontSize);
+ const currentFontSize: number = ng2FittextDirective.getFontSize();
+ expect(currentFontSize).toEqual(newFontSize);
+ });
+
+ it('Should emit the font size change', () => {
+ newFontSize = 500;
+ spyOn(ng2FittextDirective.fontSizeChanged, 'emit');
+ ng2FittextDirective.setFontSize(newFontSize);
+ expect(ng2FittextDirective.fontSizeChanged.emit).toHaveBeenCalledWith(
+ newFontSize
+ );
+ });
+
+ it('Should update the nativeElement with the new font size', () => {
+ newFontSize = 500;
+ spyOn(ng2FittextDirective.el.nativeElement.style, 'setProperty');
+ ng2FittextDirective.setFontSize(newFontSize);
+ expect(
+ ng2FittextDirective.el.nativeElement.style.setProperty
+ ).toHaveBeenCalledWith('font-size', `${newFontSize}px`);
+ });
+ });
+
+ describe('Method: getFontSize', () => {
+ it('Should return the current font size', () => {
+ expect(ng2FittextDirective.getFontSize()).toEqual(1000);
+ });
+ });
+
+ describe('Method: calculateFontSize', () => {
+ it('Should return the font size rounded down', () => {
+ expect(ng2FittextDirective.calculateFontSize(10, 3)).toEqual(3);
+ expect(ng2FittextDirective.calculateFontSize(9, 3)).toEqual(3);
+ expect(ng2FittextDirective.calculateFontSize(8, 3)).toEqual(2);
+ });
+ });
+
+ describe('Method: checkOverflow', () => {
+ let parentElementMock: any;
+ let childrenElementMock: any;
+
+ beforeEach(() => {
+ parentElementMock = {
+ clientWidth: 0,
+ clientHeight: 0,
+ };
+ childrenElementMock = {
+ scrollWidth: 0,
+ clientHeight: 0,
+ };
+ });
+
+ it('Should return false if no overflow is present', () => {
+ expect(
+ ng2FittextDirective.checkOverflow(
+ parentElementMock,
+ childrenElementMock
+ )
+ ).toBe(false);
+ });
+
+ it('Should return true if x axis has overflow', () => {
+ childrenElementMock.scrollWidth = 2;
+ expect(
+ ng2FittextDirective.checkOverflow(
+ parentElementMock,
+ childrenElementMock
+ )
+ ).toBe(true);
+ });
+
+ it('Should return true if y axis has overflow', () => {
+ childrenElementMock.clientHeight = 2;
+ expect(
+ ng2FittextDirective.checkOverflow(
+ parentElementMock,
+ childrenElementMock
+ )
+ ).toBe(true);
+ });
+ });
+
+ describe('Method: getStartFontSizeFromHeight', () => {
+ it('Should return the container clientHeight value if the container is present', () => {
+ const containerClientHeight = 10;
+ ng2FittextDirective.container = {
+ clientHeight: containerClientHeight,
+ } as HTMLElement;
+ expect(ng2FittextDirective.getStartFontSizeFromHeight()).toEqual(
+ containerClientHeight
+ );
+ });
+
+ it('Should return the parentElement clientHeight value if no container is present', () => {
+ const parentlientHeight = 11;
+ elMock.nativeElement = {
+ parentElement: {
+ clientHeight: parentlientHeight,
+ },
+ } as HTMLElement;
+ expect(ng2FittextDirective.getStartFontSizeFromHeight()).toEqual(
+ parentlientHeight
+ );
+ });
+ });
+
+ describe('Method: isDone', () => {
+ it('Should return the done property value', () => {
+ const defaultDoneValue = false;
+ expect(ng2FittextDirective.isDone()).toBe(defaultDoneValue);
+ });
+ });
+
+ describe('Method: isVisible', () => {
+ it('Should return the true if getStartFontSizeFromHeight() is greater than zero', () => {
+ spyOn(ng2FittextDirective, 'getStartFontSizeFromHeight').and.returnValue(
+ 1
+ );
+ expect(ng2FittextDirective.isVisible()).toBe(true);
+ });
+
+ it('Should return the false if getStartFontSizeFromHeight() is smaller or equal to zero', () => {
+ const spy = spyOn(
+ ng2FittextDirective,
+ 'getStartFontSizeFromHeight'
+ ).and.returnValue(0);
+ expect(ng2FittextDirective.isVisible()).toBe(false);
+ spy.and.returnValue(-1);
+ expect(ng2FittextDirective.isVisible()).toBe(false);
+ });
+ });
+});