├── src
├── assets
│ └── .gitkeep
├── app
│ ├── my-component
│ │ ├── my-component.component.scss
│ │ ├── my-component.component.html
│ │ ├── my-component.component.ts
│ │ └── my-component.component.spec.ts
│ ├── my-other-component
│ │ ├── my-other-component.component.scss
│ │ ├── my-other-component.component.html
│ │ ├── my-other-component.component.ts
│ │ └── my-other-component.component.spec.ts
│ ├── app-routing.module.ts
│ ├── app.component.ts
│ ├── app.component.scss
│ ├── app.module.ts
│ ├── app.component.spec.ts
│ └── app.component.html
├── ngx-tabset
│ ├── index.ts
│ ├── license-banner.txt
│ ├── public_api.ts
│ ├── src
│ │ ├── ngx-tabset.ts
│ │ ├── components
│ │ │ ├── tab.ts
│ │ │ └── tab-set.ts
│ │ ├── modules
│ │ │ └── ngx-tabset.module.ts
│ │ └── ngx-tabset.scss
│ ├── tsconfig.json
│ ├── rollup.config.js
│ ├── rollup.es.config.js
│ ├── tests
│ │ └── components
│ │ │ └── ngx-tabset-component.spec.ts
│ ├── spec.bundle.js
│ ├── tsconfig-build.json
│ ├── .stylelintrc
│ ├── tslint.json
│ ├── package.json
│ ├── build.js
│ └── karma.conf.js
├── favicon.ico
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── tsconfig.app.json
├── styles.scss
├── tsconfig.spec.json
├── index.html
├── tslint.json
├── main.ts
├── browserslist
├── test.ts
├── karma.conf.js
└── polyfills.ts
├── .codecov.yml
├── e2e
├── src
│ ├── app.po.ts
│ └── app.e2e-spec.ts
└── tsconfig.e2e.json
├── .editorconfig
├── tsconfig.json
├── CHANGELOG.md
├── .gitignore
├── .travis.yml
├── LICENSE
├── package.json
├── .github
└── issue_template.md
├── tslint.json
├── angular.json
└── README.md
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/my-component/my-component.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/ngx-tabset/index.ts:
--------------------------------------------------------------------------------
1 | export * from './public_api';
2 |
--------------------------------------------------------------------------------
/src/app/my-other-component/my-other-component.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/my-component/my-component.component.html:
--------------------------------------------------------------------------------
1 |
2 | my-component works!
3 |
4 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maximelafarie/ngx-tabset/HEAD/src/favicon.ico
--------------------------------------------------------------------------------
/src/ngx-tabset/license-banner.txt:
--------------------------------------------------------------------------------
1 | /**
2 | * @license ngx-tabset
3 | * MIT license
4 | */
5 |
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/app/my-other-component/my-other-component.component.html:
--------------------------------------------------------------------------------
1 |
2 | my-other-component works!
3 |
4 |
--------------------------------------------------------------------------------
/src/ngx-tabset/public_api.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Entry point for all public APIs of the package.
3 | */
4 | export * from './src/ngx-tabset';
5 |
--------------------------------------------------------------------------------
/.codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | status:
3 | project:
4 | default:
5 | target: auto
6 | threshold: 10
7 | base: auto
8 |
--------------------------------------------------------------------------------
/src/ngx-tabset/src/ngx-tabset.ts:
--------------------------------------------------------------------------------
1 | // Public classes.
2 | export { TabsetComponent } from './components/tab-set';
3 | export { TabComponent } from './components/tab';
4 | export { TabsModule } from './modules/ngx-tabset.module';
5 |
--------------------------------------------------------------------------------
/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "types": []
6 | },
7 | "exclude": [
8 | "test.ts",
9 | "**/*.spec.ts"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | const routes: Routes = [];
5 |
6 | @NgModule({
7 | imports: [RouterModule.forRoot(routes)],
8 | exports: [RouterModule]
9 | })
10 | export class AppRoutingModule { }
11 |
--------------------------------------------------------------------------------
/src/styles.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Roboto');
2 |
3 | @import "ngx-tabset/src/ngx-tabset";
4 |
5 | html,
6 | body {
7 | padding: 0;
8 | margin: 0;
9 | }
10 |
11 | body {
12 | font-family: 'Roboto', sans-serif;
13 | background: #f39c12;
14 | }
15 |
16 | ngx-tab:not(.active) {
17 | opacity: 0;
18 | }
19 |
--------------------------------------------------------------------------------
/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "test.ts",
12 | "polyfills.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NgxTabsetDemo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 |
3 | describe('workspace-project App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to ngx-tabset-demo!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, VERSION } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.scss']
7 | })
8 | export class AppComponent {
9 | version = VERSION.full;
10 |
11 | public tabSelected(index: number): void {
12 | console.log('Tab ' + index + ' has been selected.');
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.error(err));
13 |
--------------------------------------------------------------------------------
/src/browserslist:
--------------------------------------------------------------------------------
1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 | #
5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
6 |
7 | > 0.5%
8 | last 2 versions
9 | Firefox ESR
10 | not dead
11 | not IE 9-11
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "module": "es2015",
9 | "moduleResolution": "node",
10 | "emitDecoratorMetadata": true,
11 | "experimentalDecorators": true,
12 | "target": "es5",
13 | "typeRoots": [
14 | "node_modules/@types"
15 | ],
16 | "lib": [
17 | "es2018",
18 | "dom"
19 | ]
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/my-component/my-component.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, OnDestroy } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-my-component',
5 | templateUrl: './my-component.component.html',
6 | styleUrls: ['./my-component.component.scss']
7 | })
8 | export class MyComponentComponent implements OnInit, OnDestroy {
9 |
10 | constructor() { }
11 |
12 | ngOnInit() {
13 | console.log('my-component init!');
14 | }
15 |
16 | ngOnDestroy() {
17 | console.log('my-component destroy!');
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/my-other-component/my-other-component.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, OnDestroy } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-my-other-component',
5 | templateUrl: './my-other-component.component.html',
6 | styleUrls: ['./my-other-component.component.scss']
7 | })
8 | export class MyOtherComponentComponent implements OnInit, OnDestroy {
9 |
10 | constructor() { }
11 |
12 | ngOnInit() {
13 | console.log('my-other-component init!');
14 | }
15 |
16 | ngOnDestroy() {
17 | console.log('my-other-component destroy!');
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/ngx-tabset/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "emitDecoratorMetadata": true,
5 | "experimentalDecorators": true,
6 | "strict": true,
7 | "module": "commonjs",
8 | "moduleResolution": "node",
9 | "rootDir": ".",
10 | "sourceMap": true,
11 | "inlineSources": true,
12 | "target": "es5",
13 | "skipLibCheck": true,
14 | "lib": [
15 | "es2015",
16 | "dom"
17 | ],
18 | "typeRoots": [
19 | "./node_modules/@types/"
20 | ]
21 | },
22 | "exclude": [
23 | "node_modules"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/src/ngx-tabset/rollup.config.js:
--------------------------------------------------------------------------------
1 | import resolve from 'rollup-plugin-node-resolve';
2 | import sourcemaps from 'rollup-plugin-sourcemaps';
3 |
4 | const globals = {
5 | '@angular/core': 'ng.core',
6 | '@angular/common': 'ng.common',
7 | '@angular/platform-browser': 'ng.platformBrowser'
8 | };
9 |
10 | export default {
11 | external: Object.keys(globals),
12 | plugins: [resolve(), sourcemaps()],
13 | onwarn: () => { return },
14 | output: {
15 | format: 'umd',
16 | name: 'ng.ngxTabset',
17 | globals: globals,
18 | sourcemap: true,
19 | exports: 'named'
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/ngx-tabset/rollup.es.config.js:
--------------------------------------------------------------------------------
1 | import sourcemaps from 'rollup-plugin-sourcemaps';
2 | import license from 'rollup-plugin-license';
3 |
4 | const path = require('path');
5 |
6 | export default {
7 | output: {
8 | format: 'es',
9 | sourcemap: true
10 | },
11 | plugins: [
12 | sourcemaps(),
13 | license({
14 | sourceMap: true,
15 |
16 | banner: {
17 | file: path.join(__dirname, 'license-banner.txt'),
18 | encoding: 'utf-8',
19 | }
20 | })
21 | ],
22 | onwarn: () => { return }
23 | }
24 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## ngx-tabset Changelog
2 |
3 |
4 | ### 1.2.0
5 | * Add `Angular Animations` with options to tabs
6 | * Update building process:
7 | - TSLint
8 | - Stop building for errors on Rollup conversion
9 | - Use local import for ShellJS
10 | * Add sourcemap files for testing to _karma.conf_ & use ES2015 syntax in _spec.bundle_
11 | * Update Codelyzer rules
12 | * Update packages
13 |
14 |
15 | ### 1.1.0
16 | * Add fixes and add more complete readme
17 |
18 |
19 | ### 1.0.2
20 | * Re-add container div inside tabset.
21 |
22 |
23 | ### 1.0.1
24 | * Add fixes to enhance tabs
25 |
--------------------------------------------------------------------------------
/src/ngx-tabset/tests/components/ngx-tabset-component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 |
3 | import { TabsetComponent, TabComponent } from './../../index';
4 |
5 | describe('TabsetComponent', () => {
6 |
7 | beforeEach(async(() => {
8 | TestBed.configureTestingModule({
9 | declarations: [
10 | TabsetComponent,
11 | TabComponent
12 | ]
13 | }).compileComponents();
14 | }));
15 |
16 | it('should create a tabset', async(() => {
17 | const fixture = TestBed.createComponent(TabsetComponent);
18 | const app = fixture.debugElement.componentInstance;
19 | expect(app).toBeTruthy();
20 | }));
21 |
22 | });
23 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-align: center;
3 | }
4 |
5 | .container {
6 | display: flex;
7 | align-items: flex-start;
8 | justify-content: center;
9 | padding: 20px;
10 |
11 | section {
12 | flex: 1;
13 | width: 100%;
14 | background: white;
15 | border-radius: 3px;
16 | height: auto;
17 | min-height: 200px;
18 | margin: 0 20px;
19 |
20 | .section-container {
21 | margin: 20px;
22 | }
23 | }
24 | }
25 |
26 | .footer {
27 | font-weight: 400;
28 | line-height: 2;
29 | text-align: center;
30 | font-size: 12px;
31 | }
32 |
33 | @media screen and (max-width: 991px) {
34 | .container {
35 | flex-direction: column;
36 |
37 | section {
38 | margin: 20px 0;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | $ cat .gitignore
2 | **/node_modules
3 | **/dist
4 | **/documentation
5 |
6 | *.log
7 | *.tgz
8 |
9 | # See http://help.github.com/ignore-files/ for more about ignoring files.
10 |
11 | # compiled output
12 | /tmp
13 | /out-tsc
14 |
15 | # IDEs and editors
16 | /.idea
17 | .project
18 | .classpath
19 | .c9/
20 | *.launch
21 | .settings/
22 | *.sublime-workspace
23 |
24 | # IDE - VSCode
25 | .vscode/*
26 | !.vscode/settings.json
27 | !.vscode/tasks.json
28 | !.vscode/launch.json
29 | !.vscode/extensions.json
30 |
31 | # misc
32 | /.sass-cache
33 | /connect.lock
34 | **/coverage
35 | /libpeerconnection.log
36 | **/npm-debug.log
37 | testem.log
38 | **/typings
39 |
40 | # e2e
41 | /e2e/*.js
42 | /e2e/*.map
43 |
44 | # System Files
45 | .DS_Store
46 | Thumbs.db
47 |
--------------------------------------------------------------------------------
/src/app/my-component/my-component.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { MyComponentComponent } from './my-component.component';
4 |
5 | describe('MyComponentComponent', () => {
6 | let component: MyComponentComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ MyComponentComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(MyComponentComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/my-other-component/my-other-component.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { MyOtherComponentComponent } from './my-other-component.component';
4 |
5 | describe('MyOtherComponentComponent', () => {
6 | let component: MyOtherComponentComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ MyOtherComponentComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(MyOtherComponentComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/ngx-tabset/src/components/tab.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, ContentChild, TemplateRef } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'ngx-tab',
5 | template: `
6 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | `
17 | })
18 | export class TabComponent {
19 | @Input() public tabTitle: string;
20 | @Input() public tabSubTitle: string;
21 | @Input() public active = false;
22 | @Input() public disabled = false;
23 | @Input() public bypassDOM = false;
24 | @Input() public customPaneClass: string = '';
25 | @ContentChild(TemplateRef) template: TemplateRef;
26 | }
27 |
--------------------------------------------------------------------------------
/src/ngx-tabset/spec.bundle.js:
--------------------------------------------------------------------------------
1 | import 'core-js';
2 | import 'zone.js/dist/zone';
3 | import 'zone.js/dist/long-stack-trace-zone';
4 | import 'zone.js/dist/proxy.js';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/jasmine-patch';
7 | import 'zone.js/dist/async-test';
8 | import 'zone.js/dist/fake-async-test';
9 |
10 | import { getTestBed } from '@angular/core/testing';
11 | import {
12 | BrowserDynamicTestingModule,
13 | platformBrowserDynamicTesting
14 | } from '@angular/platform-browser-dynamic/testing';
15 |
16 | import 'rxjs';
17 |
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting()
21 | );
22 |
23 | const testContext = require.context('./tests', true, /\.spec\.ts/);
24 |
25 | function requireAll(requireContext) {
26 | return requireContext.keys().map(requireContext);
27 | }
28 |
29 | const modules = requireAll(testContext);
30 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | dist: trusty
3 | addons:
4 | chrome: stable
5 | language: node_js
6 | node_js:
7 | - 11.10.0
8 | before_install:
9 | - echo "$TRAVIS_BRANCH"
10 | - echo "$TRAVIS_PULL_REQUEST"
11 | - npm i npm -g
12 | - npm install -g greenkeeper-lockfile@1
13 | - cd src/ngx-tabset
14 | install:
15 | - npm install
16 | script:
17 | - npm run test
18 | - npm run build
19 | before_script:
20 | - export DISPLAY=:99.0
21 | - sh -e /etc/init.d/xvfb start
22 | - sleep 3
23 | - greenkeeper-lockfile-update
24 | after_script:
25 | - greenkeeper-lockfile-upload
26 | after_success:
27 | - codecov
28 | - rm -r node_modules
29 | - cd ../..
30 | - if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" == false ]; then npm run ghpages; fi
31 | notifications:
32 | email: false
33 | deploy:
34 | provider: pages
35 | skip_cleanup: true
36 | local_dir: dist
37 | github_token: $PUSH_TOKEN
38 | on:
39 | branch: master
40 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
3 | import { NgModule } from '@angular/core';
4 |
5 | import { AppRoutingModule } from './app-routing.module';
6 | import { AppComponent } from './app.component';
7 | import { TabsModule } from '../ngx-tabset';
8 | import { CommonModule } from '@angular/common';
9 | import { MyComponentComponent } from './my-component/my-component.component';
10 | import { MyOtherComponentComponent } from './my-other-component/my-other-component.component';
11 |
12 | @NgModule({
13 | declarations: [
14 | AppComponent,
15 | MyComponentComponent,
16 | MyOtherComponentComponent
17 | ],
18 | imports: [
19 | BrowserModule,
20 | BrowserAnimationsModule,
21 | CommonModule,
22 | AppRoutingModule,
23 | TabsModule.forRoot()
24 | ],
25 | providers: [],
26 | bootstrap: [AppComponent]
27 | })
28 | export class AppModule { }
29 |
--------------------------------------------------------------------------------
/src/ngx-tabset/tsconfig-build.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "rootDir": ".",
4 | "baseUrl": ".",
5 | "paths": {
6 | "@angular/*": [
7 | "node_modules/@angular/*"
8 | ]
9 | },
10 | "outDir": "dist",
11 | "declaration": true,
12 | "strict": true,
13 | "moduleResolution": "node",
14 | "module": "es2015",
15 | "target": "es2015",
16 | "lib": [
17 | "es2015",
18 | "dom"
19 | ],
20 | "skipLibCheck": true,
21 | "types": [],
22 | "experimentalDecorators": true,
23 | "emitDecoratorMetadata": true,
24 | "sourceMap": true,
25 | "inlineSources": true
26 | },
27 | "files": [
28 | "index.ts",
29 | "node_modules/zone.js/dist/zone.js.d.ts"
30 | ],
31 | "angularCompilerOptions": {
32 | "skipTemplateCodegen": true,
33 | "annotateForClosureCompiler": true,
34 | "strictMetadataEmit": true,
35 | "flatModuleOutFile": "ngx-tabset.js",
36 | "flatModuleId": "ngx-tabset"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/ngx-tabset/src/modules/ngx-tabset.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule, ModuleWithProviders } from '@angular/core';
3 |
4 | import { TabsetComponent } from '../components/tab-set';
5 | import { TabComponent } from '../components/tab';
6 |
7 | @NgModule({
8 | declarations: [
9 | TabComponent,
10 | TabsetComponent,
11 | ],
12 | exports: [
13 | TabComponent,
14 | TabsetComponent,
15 | ],
16 | imports: [CommonModule]
17 | })
18 | export class TabsModule {
19 |
20 | /**
21 | * Use in AppModule: new instance of NgxTabset.
22 | */
23 | public static forRoot(): ModuleWithProviders {
24 | return {
25 | ngModule: TabsModule,
26 | providers: []
27 | };
28 | }
29 |
30 | /**
31 | * Use in features modules with lazy loading: new instance of NgxTabset.
32 | */
33 | public static forChild(): ModuleWithProviders {
34 | return {
35 | ngModule: TabsModule,
36 | providers: []
37 | };
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/ngx-tabset/src/ngx-tabset.scss:
--------------------------------------------------------------------------------
1 | $active-tab-color: red !default;
2 | $default-tab-border: solid 1px transparent !default;
3 | $tab-border-transition-duration: 200ms !default;
4 | $tab-border-transition-timing-function: ease-out !default;
5 | $nav-tab-padding: 10px !default;
6 | $disabled-tab-opacity: .5 !default;
7 | $tab-cursor: pointer !default;
8 |
9 | .nav-tabset:not(.disable-style) {
10 | list-style: none;
11 | padding: 0;
12 | display: flex;
13 | align-items: center;
14 |
15 | .nav-tab {
16 | padding: $nav-tab-padding;
17 | border-bottom: $default-tab-border;
18 | transition: border $tab-border-transition-duration $tab-border-transition-timing-function;
19 |
20 | > .tab-subtitle {
21 | display: block;
22 | font-size: .7rem;
23 | }
24 |
25 | &.active {
26 | border-color: $active-tab-color;
27 | }
28 |
29 | &.disabled {
30 | opacity: $disabled-tab-opacity;
31 | }
32 |
33 | &:not(.active):not(.disabled) {
34 | cursor: $tab-cursor;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: ['html', 'lcovonly'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false
30 | });
31 | };
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 |
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async(() => {
7 | TestBed.configureTestingModule({
8 | declarations: [
9 | AppComponent
10 | ],
11 | }).compileComponents();
12 | }));
13 |
14 | it('should create the app', async(() => {
15 | const fixture = TestBed.createComponent(AppComponent);
16 | const app = fixture.debugElement.componentInstance;
17 | expect(app).toBeTruthy();
18 | }));
19 |
20 | it(`should have as title 'app'`, async(() => {
21 | const fixture = TestBed.createComponent(AppComponent);
22 | const app = fixture.debugElement.componentInstance;
23 | expect(app.title).toEqual('app');
24 | }));
25 |
26 | it('should render title in a h1 tag', async(() => {
27 | const fixture = TestBed.createComponent(AppComponent);
28 | fixture.detectChanges();
29 | const compiled = fixture.debugElement.nativeElement;
30 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
31 | }));
32 | });
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Roberto Simonetti
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/ngx-tabset/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "stylelint-config-standard",
3 | "ignoreFiles": [
4 | "src/assets/**/*",
5 | "dist/assets/**/*"
6 | ],
7 | "rules": {
8 | "at-rule-no-unknown": [
9 | true,
10 | {
11 | "ignoreAtRules": [
12 | "extend",
13 | "at-root",
14 | "debug",
15 | "warn",
16 | "error",
17 | "if",
18 | "else",
19 | "for",
20 | "each",
21 | "while",
22 | "mixin",
23 | "include",
24 | "content",
25 | "return",
26 | "function"
27 | ]
28 | }
29 | ],
30 | "number-leading-zero": "never",
31 | "at-rule-empty-line-before": [
32 | "always",
33 | {
34 | "ignore": [
35 | "inside-block",
36 | "blockless-after-same-name-blockless",
37 | "blockless-after-blockless"
38 | ]
39 | }
40 | ],
41 | "no-eol-whitespace": [
42 | true,
43 | {
44 | "ignore": [
45 | "empty-lines"
46 | ]
47 | }
48 | ],
49 | "selector-type-no-unknown": [
50 | true,
51 | {
52 | "ignoreTypes": [
53 | "ngx-tabset",
54 | "ngx-tab"
55 | ]
56 | }
57 | ],
58 | "no-descending-specificity": null
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngx-tabset-demo",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "test": "ng test",
9 | "lint": "ng lint",
10 | "e2e": "ng e2e",
11 | "ghpages": "yarn && ng build --prod --aot --no-progress --baseHref='/ngx-tabset/'"
12 | },
13 | "private": true,
14 | "dependencies": {
15 | "@angular/animations": "~7.0.0",
16 | "@angular/common": "~7.0.0",
17 | "@angular/compiler": "~7.0.0",
18 | "@angular/core": "~7.0.0",
19 | "@angular/forms": "~7.0.0",
20 | "@angular/http": "~7.0.0",
21 | "@angular/platform-browser": "~7.0.0",
22 | "@angular/platform-browser-dynamic": "~7.0.0",
23 | "@angular/router": "~7.0.0",
24 | "core-js": "^2.5.4",
25 | "rxjs": "~6.3.3",
26 | "zone.js": "~0.8.26"
27 | },
28 | "devDependencies": {
29 | "@angular-devkit/build-angular": "^0.10.0",
30 | "@angular/cli": "~7.0.1",
31 | "@angular/compiler-cli": "~7.0.0",
32 | "@angular/language-service": "~7.0.0",
33 | "@types/jasmine": "~2.8.8",
34 | "@types/jasminewd2": "~2.0.3",
35 | "@types/node": "~8.9.4",
36 | "codelyzer": "~4.5.0",
37 | "jasmine-core": "~2.99.1",
38 | "jasmine-spec-reporter": "~4.2.1",
39 | "karma": "~3.0.0",
40 | "karma-chrome-launcher": "~2.2.0",
41 | "karma-coverage-istanbul-reporter": "~2.0.1",
42 | "karma-jasmine": "~1.1.2",
43 | "karma-jasmine-html-reporter": "^0.2.2",
44 | "node-sass": "^4.13.0",
45 | "protractor": "~5.4.0",
46 | "ts-node": "~7.0.0",
47 | "tslint": "~5.11.0",
48 | "typescript": "~3.1.1"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | **I'm submitting a ...** (check one with "x")
6 | ```
7 | [ ] bug report => search github for a similar issue or PR before submitting
8 | [ ] feature request
9 | [ ] support request => Please do not submit support request here, post on Stackoverflow or Gitter
10 | ```
11 |
12 | **Current behavior**
13 |
14 |
15 | **Expected behavior**
16 |
17 |
18 | **Reproduction of the problem**
19 |
20 |
21 | **What is the motivation / use case for changing the behavior?**
22 |
23 |
24 | **Please tell us about your environment:**
25 |
26 |
27 | * **Tabset version:** 2.0.x
28 |
29 |
30 | * **Angular version:** 8.0.x
31 |
32 |
33 | * **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
34 |
35 |
36 | * **Language:** [all | TypeScript X.X | ES6/7 | ES5]
--------------------------------------------------------------------------------
/src/ngx-tabset/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint:recommended",
3 | "rulesDirectory": [
4 | "node_modules/codelyzer"
5 | ],
6 | "rules": {
7 | "no-trailing-whitespace": false,
8 | "max-line-length": [
9 | true,
10 | 10000
11 | ],
12 | "angular-whitespace": [
13 | true,
14 | "check-interpolation",
15 | "check-pipe"
16 | ],
17 | "banana-in-box": true,
18 | "templates-no-negated-async": true,
19 | "directive-selector": [
20 | true,
21 | "attribute",
22 | [
23 | "dir-prefix1",
24 | "dir-prefix2"
25 | ],
26 | "camelCase"
27 | ],
28 | "component-selector": [
29 | true,
30 | "element",
31 | [
32 | "ngx-tabset",
33 | "ngx-tab"
34 | ],
35 | "kebab-case"
36 | ],
37 | "use-input-property-decorator": true,
38 | "use-output-property-decorator": true,
39 | "use-host-property-decorator": true,
40 | "no-attribute-parameter-decorator": true,
41 | "no-input-rename": true,
42 | "no-output-rename": true,
43 | "no-forward-ref": true,
44 | "use-view-encapsulation": false,
45 | "use-life-cycle-interface": true,
46 | "use-pipe-transform-interface": true,
47 | "pipe-naming": [
48 | true,
49 | "camelCase",
50 | "Pipe"
51 | ],
52 | "component-class-suffix": [
53 | true,
54 | "Component"
55 | ],
56 | "directive-class-suffix": [
57 | true,
58 | "Directive"
59 | ],
60 | "ordered-imports": [
61 | false
62 | ],
63 | "quotemark": [
64 | true,
65 | "single"
66 | ],
67 | "trailing-comma": [
68 | false
69 | ],
70 | "variable-name": false
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/ngx-tabset/src/components/tab-set.ts:
--------------------------------------------------------------------------------
1 | import { Component, ContentChildren, QueryList, AfterContentInit, Input, Output, EventEmitter } from '@angular/core';
2 |
3 | import { TabComponent } from './tab';
4 |
5 | @Component({
6 | selector: 'ngx-tabset',
7 | template: `
8 |
11 | -
16 | {{ tab.tabTitle }}
17 | {{ tab.tabSubTitle }}
18 |
19 |
20 |
22 |
23 |
24 | `
25 | })
26 | export class TabsetComponent implements AfterContentInit {
27 |
28 | @ContentChildren(TabComponent) public tabs: QueryList;
29 |
30 | @Input() public disableStyle = false;
31 | @Input() public customNavClass: string = '';
32 | @Input() public customTabsClass: string = '';
33 |
34 | @Output() public onSelect = new EventEmitter();
35 |
36 | // contentChildren are set
37 | public ngAfterContentInit() {
38 | // get all active tabs
39 | const activeTabs = this.tabs.filter((tab: TabComponent) => tab.active);
40 |
41 | // if there is no active tab set, activate the first
42 | if (activeTabs.length === 0) {
43 | this.selectTab(this.tabs.first);
44 | }
45 | }
46 |
47 | public selectTab(tabToSelect: TabComponent): void {
48 | if (tabToSelect.disabled === true || tabToSelect.active === true) {
49 | return;
50 | }
51 |
52 | // deactivate all tabs
53 | this.tabs.toArray().forEach((tab: TabComponent) => tab.active = false);
54 |
55 | // activate the tab the user has clicked on.
56 | tabToSelect.active = true;
57 | this.onSelect.emit(this.tabs.toArray().indexOf(tabToSelect));
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 | ngx-tabset
2 |
3 |
4 |
5 |
6 |
Tabs with default style
7 |
8 |
9 |
10 | Its all about me.
11 |
12 |
13 |
14 | This is content of the contacts tab
15 |
16 |
17 |
18 | Content of the Map Tab
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
Tabs with selected tab and onSelect event
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Watch your console, events are logged!
39 |
40 |
41 |
42 |
43 | Content of the Map Tab
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
Tabset without style
54 |
55 |
56 |
57 | Its all about me.
58 |
59 |
60 | This is content of the contacts tab
61 |
62 |
63 | Content of the Map Tab
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/ngx-tabset/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngx-tabset",
3 | "version": "2.2.0",
4 | "description": "A very simple library to let you create some tabs",
5 | "main": "./bundles/ngx-tabset.umd.js",
6 | "module": "./esm5/ngx-tabset.js",
7 | "es2015": "./esm2015/ngx-tabset.js",
8 | "scripts": {
9 | "build": "node build.js",
10 | "test": "karma start",
11 | "pack-lib": "npm pack ./dist",
12 | "publish-lib": "npm publish ./dist",
13 | "compodoc": "compodoc -p tsconfig.json",
14 | "compodoc-serve": "compodoc -s"
15 | },
16 | "typings": "./ngx-tabset.d.ts",
17 | "author": {
18 | "name": "Maxime Lafarie",
19 | "email": "maxime.lafarie@gmail.com"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "https://maximelafarie.com/ngx-tabset.git"
24 | },
25 | "bugs": {
26 | "url": "https://maximelafarie.com/ngx-tabset/issues"
27 | },
28 | "homepage": "https://maximelafarie.com/ngx-tabset",
29 | "keywords": [
30 | "ngx-tabset",
31 | "javascript",
32 | "typescript",
33 | "tabs",
34 | "tabset",
35 | "tab",
36 | "angular",
37 | "angular2",
38 | "angular4",
39 | "angular5",
40 | "angular6",
41 | "angular7",
42 | "ngx"
43 | ],
44 | "license": "MIT",
45 | "peerDependencies": {
46 | "@angular/common": ">=2.0.0",
47 | "@angular/core": ">=2.0.0",
48 | "@angular/platform-browser": ">=2.0.0"
49 | },
50 | "devDependencies": {
51 | "@angular/animations": "5.0.0",
52 | "@angular/common": "5.0.0",
53 | "@angular/compiler": "5.0.0",
54 | "@angular/compiler-cli": "5.0.0",
55 | "@angular/core": "5.0.0",
56 | "@angular/platform-browser": "5.0.0",
57 | "@angular/platform-browser-dynamic": "5.0.0",
58 | "@angular/platform-server": "5.0.0",
59 | "@compodoc/compodoc": "1.0.3",
60 | "@types/jasmine": "2.6.2",
61 | "@types/node": "8.0.47",
62 | "chalk": "2.3.0",
63 | "codelyzer": "4.0.0",
64 | "core-js": "2.5.1",
65 | "istanbul-instrumenter-loader": "3.0.0",
66 | "jasmine-core": "2.8.0",
67 | "karma": "1.7.1",
68 | "karma-chrome-launcher": "2.2.0",
69 | "karma-coverage-istanbul-reporter": "1.3.0",
70 | "karma-jasmine": "1.1.0",
71 | "karma-sourcemap-loader": "0.3.7",
72 | "karma-spec-reporter": "0.0.31",
73 | "karma-webpack": "2.0.5",
74 | "node-sass": "^4.8.3",
75 | "reflect-metadata": "0.1.10",
76 | "rollup": "0.50.0",
77 | "rollup-plugin-license": "0.5.0",
78 | "rollup-plugin-node-resolve": "3.0.0",
79 | "rollup-plugin-sourcemaps": "0.4.2",
80 | "rxjs": "5.5.2",
81 | "shelljs": "0.7.8",
82 | "source-map-loader": "0.2.3",
83 | "stylelint": "^9.1.3",
84 | "stylelint-config-standard": "^18.2.0",
85 | "ts-loader": "3.1.1",
86 | "tslint": "5.8.0",
87 | "typescript": "2.4.2",
88 | "uglify-js": "3.1.6",
89 | "webpack": "3.8.1",
90 | "zone.js": "0.8.18"
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs/Rx"
22 | ],
23 | "import-spacing": true,
24 | "indent": [
25 | true,
26 | "spaces"
27 | ],
28 | "interface-over-type-literal": true,
29 | "label-position": true,
30 | "max-line-length": [
31 | true,
32 | 140
33 | ],
34 | "member-access": false,
35 | "member-ordering": [
36 | true,
37 | {
38 | "order": [
39 | "static-field",
40 | "instance-field",
41 | "static-method",
42 | "instance-method"
43 | ]
44 | }
45 | ],
46 | "no-arg": true,
47 | "no-bitwise": true,
48 | "no-console": [
49 | true,
50 | "debug",
51 | "info",
52 | "time",
53 | "timeEnd",
54 | "trace"
55 | ],
56 | "no-construct": true,
57 | "no-debugger": true,
58 | "no-duplicate-super": true,
59 | "no-empty": false,
60 | "no-empty-interface": true,
61 | "no-eval": true,
62 | "no-inferrable-types": [
63 | true,
64 | "ignore-params"
65 | ],
66 | "no-misused-new": true,
67 | "no-non-null-assertion": true,
68 | "no-redundant-jsdoc": true,
69 | "no-shadowed-variable": true,
70 | "no-string-literal": false,
71 | "no-string-throw": true,
72 | "no-switch-case-fall-through": true,
73 | "no-trailing-whitespace": true,
74 | "no-unnecessary-initializer": true,
75 | "no-unused-expression": true,
76 | "no-use-before-declare": true,
77 | "no-var-keyword": true,
78 | "object-literal-sort-keys": false,
79 | "one-line": [
80 | true,
81 | "check-open-brace",
82 | "check-catch",
83 | "check-else",
84 | "check-whitespace"
85 | ],
86 | "prefer-const": true,
87 | "quotemark": [
88 | true,
89 | "single"
90 | ],
91 | "radix": true,
92 | "semicolon": [
93 | true,
94 | "always"
95 | ],
96 | "triple-equals": [
97 | true,
98 | "allow-null-check"
99 | ],
100 | "typedef-whitespace": [
101 | true,
102 | {
103 | "call-signature": "nospace",
104 | "index-signature": "nospace",
105 | "parameter": "nospace",
106 | "property-declaration": "nospace",
107 | "variable-declaration": "nospace"
108 | }
109 | ],
110 | "unified-signatures": true,
111 | "variable-name": false,
112 | "whitespace": [
113 | true,
114 | "check-branch",
115 | "check-decl",
116 | "check-operator",
117 | "check-separator",
118 | "check-type"
119 | ],
120 | "no-output-on-prefix": true,
121 | "use-input-property-decorator": true,
122 | "use-output-property-decorator": true,
123 | "use-host-property-decorator": true,
124 | "no-input-rename": true,
125 | "no-output-rename": true,
126 | "use-life-cycle-interface": true,
127 | "use-pipe-transform-interface": true,
128 | "component-class-suffix": true,
129 | "directive-class-suffix": true
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /**
38 | * If the application will be indexed by Google Search, the following is required.
39 | * Googlebot uses a renderer based on Chrome 41.
40 | * https://developers.google.com/search/docs/guides/rendering
41 | **/
42 | // import 'core-js/es6/array';
43 |
44 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
45 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
46 |
47 | /** IE10 and IE11 requires the following for the Reflect API. */
48 | // import 'core-js/es6/reflect';
49 |
50 | /**
51 | * Web Animations `@angular/platform-browser/animations`
52 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
53 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
54 | **/
55 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
56 |
57 | /**
58 | * By default, zone.js will patch all possible macroTask and DomEvents
59 | * user can disable parts of macroTask/DomEvents patch by setting following flags
60 | */
61 |
62 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
63 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
64 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
65 |
66 | /*
67 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
68 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
69 | */
70 | // (window as any).__Zone_enable_cross_context_check = true;
71 |
72 | /***************************************************************************************************
73 | * Zone JS is required by default for Angular itself.
74 | */
75 | import 'zone.js/dist/zone'; // Included with Angular CLI.
76 |
77 |
78 | /***************************************************************************************************
79 | * APPLICATION IMPORTS
80 | */
81 |
--------------------------------------------------------------------------------
/src/ngx-tabset/build.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const shell = require('shelljs');
4 | const chalk = require('chalk');
5 |
6 | const PACKAGE = `ngx-tabset`;
7 | const NPM_DIR = `dist`;
8 | const ESM2015_DIR = `${NPM_DIR}/esm2015`;
9 | const ESM5_DIR = `${NPM_DIR}/esm5`;
10 | const BUNDLES_DIR = `${NPM_DIR}/bundles`;
11 | const OUT_DIR_ESM5 = `${NPM_DIR}/package/esm5`;
12 |
13 | shell.echo(`Start building...`);
14 |
15 | shell.rm(`-Rf`, `${NPM_DIR}/*`);
16 | shell.mkdir(`-p`, `./${ESM2015_DIR}`);
17 | shell.mkdir(`-p`, `./${ESM5_DIR}`);
18 | shell.mkdir(`-p`, `./${BUNDLES_DIR}`);
19 | shell.echo(shell.pwd());
20 |
21 | /* TSLint with Codelyzer */
22 | // https://github.com/palantir/tslint/blob/master/src/configs/recommended.ts
23 | // https://github.com/mgechev/codelyzer
24 | shell.echo(`Start TSLint`);
25 | shell.exec(`tslint -c tslint.json -t stylish src/**/*.ts`);
26 | shell.echo(chalk.green(`TSLint completed`));
27 |
28 | /* SCSSLint */
29 | shell.echo(`Start SCSSLint`);
30 | if (shell.exec(`stylelint src/*.scss`).code !== 0) {
31 | shell.echo(chalk.red(`Error: SCSS linting revealed some errors`));
32 | shell.echo(chalk.red(`Please check the above errors and fix them before build`));
33 | shell.exit(1);
34 | }
35 | shell.echo(chalk.green(`SCSSLint completed`));
36 |
37 | /* AoT compilation */
38 | shell.echo(`Start AoT compilation`);
39 | if (shell.exec(`ngc -p tsconfig-build.json`).code !== 0) {
40 | shell.echo(chalk.red(`Error: AoT compilation failed`));
41 | shell.exit(1);
42 | }
43 | shell.echo(chalk.green(`AoT compilation completed`));
44 |
45 | /* BUNDLING PACKAGE */
46 | shell.echo(`Start bundling`);
47 | shell.echo(`Rollup package`);
48 | if (shell.exec(`rollup -c rollup.es.config.js -i ${NPM_DIR}/${PACKAGE}.js -o ${ESM2015_DIR}/${PACKAGE}.js`).code !== 0) {
49 | shell.echo(chalk.red(`Error: Rollup package failed`));
50 | shell.exit(1);
51 | }
52 |
53 | shell.echo(`Produce ESM5 version`);
54 | shell.exec(`ngc -p tsconfig-build.json --target es5 -d false --outDir ${OUT_DIR_ESM5} --importHelpers true --sourceMap`);
55 | if (shell.exec(`rollup -c rollup.es.config.js -i ${OUT_DIR_ESM5}/${PACKAGE}.js -o ${ESM5_DIR}/${PACKAGE}.js`).code !== 0) {
56 | shell.echo(chalk.red(`Error: ESM5 version failed`));
57 | shell.exit(1);
58 | }
59 |
60 | shell.echo(`Run Rollup conversion on package`);
61 | if (shell.exec(`rollup -c rollup.config.js -i ${ESM5_DIR}/${PACKAGE}.js -o ${BUNDLES_DIR}/${PACKAGE}.umd.js`).code !== 0) {
62 | shell.echo(chalk.red(`Error: Rollup conversion failed`));
63 | shell.exit(1);
64 | }
65 |
66 | shell.echo(`Minifying`);
67 | shell.cd(`${BUNDLES_DIR}`);
68 | shell.exec(`uglifyjs ${PACKAGE}.umd.js -c --comments -o ${PACKAGE}.umd.min.js --source-map "filename='${PACKAGE}.umd.min.js.map', includeSources"`);
69 | shell.cd(`..`);
70 | shell.cd(`..`);
71 |
72 | shell.echo(`Copying and minifying styles`);
73 | shell.cp('-R', 'src/ngx-tabset.scss', `${NPM_DIR}/`);
74 | shell.cd(`${NPM_DIR}`);
75 | if (shell.exec(`node-sass ngx-tabset.scss ngx-tabset.css`).code !== 0) {
76 | shell.echo(chalk.red(`Error: SCSS compilation failed`));
77 | shell.exit(1);
78 | }
79 | shell.cd(`..`);
80 |
81 | shell.echo(chalk.green(`Bundling completed`));
82 |
83 | shell.rm(`-Rf`, `${NPM_DIR}/package`);
84 | shell.rm(`-Rf`, `${NPM_DIR}/node_modules`);
85 | shell.rm(`-Rf`, `${NPM_DIR}/*.js`);
86 | shell.rm(`-Rf`, `${NPM_DIR}/*.js.map`);
87 | shell.rm(`-Rf`, `${NPM_DIR}/src/**/*.js`);
88 | shell.rm(`-Rf`, `${NPM_DIR}/src/**/*.js.map`);
89 |
90 | shell.cp(`-Rf`, [`package.json`, `../../LICENSE`, `../../README.md`], `${NPM_DIR}`);
91 |
92 | shell.echo(chalk.green(`End building`));
93 |
--------------------------------------------------------------------------------
/src/ngx-tabset/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for Unit testing
2 |
3 | const path = require('path');
4 |
5 | module.exports = function (config) {
6 |
7 | const configuration = {
8 |
9 | // base path that will be used to resolve all patterns (eg. files, exclude)
10 | basePath: '',
11 |
12 | // frameworks to use
13 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
14 | frameworks: ['jasmine'],
15 |
16 | plugins: [
17 | require('karma-jasmine'),
18 | require('karma-chrome-launcher'),
19 | require('karma-webpack'),
20 | require('karma-sourcemap-loader'),
21 | require('karma-spec-reporter'),
22 | require('karma-coverage-istanbul-reporter'),
23 | require("istanbul-instrumenter-loader")
24 | ],
25 |
26 | // list of files / patterns to load in the browser
27 | files: [
28 | { pattern: 'spec.bundle.js', watched: false }
29 | ],
30 |
31 | // list of files to exclude
32 | exclude: [],
33 |
34 | // preprocess matching files before serving them to the browser
35 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
36 | preprocessors: {
37 | 'spec.bundle.js': ['webpack', 'sourcemap']
38 | },
39 |
40 | // webpack
41 | webpack: {
42 | resolve: {
43 | extensions: ['.ts', '.js']
44 | },
45 | module: {
46 | rules: [
47 | {
48 | test: /\.ts/,
49 | use: [
50 | { loader: 'ts-loader' },
51 | { loader: 'source-map-loader' }
52 | ],
53 | exclude: /node_modules/
54 | },
55 | {
56 | enforce: 'post',
57 | test: /\.ts/,
58 | use: [
59 | {
60 | loader: 'istanbul-instrumenter-loader',
61 | options: { esModules: true }
62 | }
63 | ],
64 | exclude: [
65 | /\.spec.ts/,
66 | /node_modules/
67 | ]
68 | }
69 | ],
70 | exprContextCritical: false
71 | },
72 | devtool: 'inline-source-map',
73 | performance: { hints: false }
74 | },
75 |
76 | webpackServer: {
77 | noInfo: true
78 | },
79 |
80 |
81 | // test results reporter to use
82 | // possible values: 'dots', 'progress'
83 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
84 | reporters: ['spec', 'coverage-istanbul'],
85 |
86 | coverageIstanbulReporter: {
87 | reports: ['html', 'lcovonly'],
88 | dir: path.join(__dirname, 'coverage'),
89 | fixWebpackSourcePaths: true
90 | },
91 |
92 |
93 | // web server port
94 | port: 9876,
95 |
96 |
97 | // enable / disable colors in the output (reporters and logs)
98 | colors: true,
99 |
100 |
101 | // level of logging
102 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
103 | logLevel: config.LOG_INFO,
104 |
105 |
106 | // enable / disable watching file and executing tests whenever any file changes
107 | autoWatch: true,
108 |
109 |
110 | // start these browsers
111 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
112 | browsers: ['Chrome'],
113 |
114 |
115 | // Continuous Integration mode
116 | // if true, Karma captures browsers, runs the tests and exits
117 | singleRun: true
118 |
119 | };
120 |
121 | config.set(configuration);
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "ngx-tabset-demo": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "prefix": "app",
11 | "schematics": {
12 | "@schematics/angular:component": {
13 | "styleext": "scss"
14 | }
15 | },
16 | "architect": {
17 | "build": {
18 | "builder": "@angular-devkit/build-angular:browser",
19 | "options": {
20 | "outputPath": "dist",
21 | "index": "src/index.html",
22 | "main": "src/main.ts",
23 | "polyfills": "src/polyfills.ts",
24 | "tsConfig": "src/tsconfig.app.json",
25 | "assets": [
26 | "src/favicon.ico",
27 | "src/assets"
28 | ],
29 | "styles": [
30 | "src/styles.scss"
31 | ],
32 | "scripts": []
33 | },
34 | "configurations": {
35 | "production": {
36 | "fileReplacements": [
37 | {
38 | "replace": "src/environments/environment.ts",
39 | "with": "src/environments/environment.prod.ts"
40 | }
41 | ],
42 | "optimization": true,
43 | "outputHashing": "all",
44 | "sourceMap": false,
45 | "extractCss": true,
46 | "namedChunks": false,
47 | "aot": true,
48 | "extractLicenses": true,
49 | "vendorChunk": false,
50 | "buildOptimizer": true,
51 | "budgets": [
52 | {
53 | "type": "initial",
54 | "maximumWarning": "2mb",
55 | "maximumError": "5mb"
56 | }
57 | ]
58 | }
59 | }
60 | },
61 | "serve": {
62 | "builder": "@angular-devkit/build-angular:dev-server",
63 | "options": {
64 | "browserTarget": "ngx-tabset-demo:build"
65 | },
66 | "configurations": {
67 | "production": {
68 | "browserTarget": "ngx-tabset-demo:build:production"
69 | }
70 | }
71 | },
72 | "extract-i18n": {
73 | "builder": "@angular-devkit/build-angular:extract-i18n",
74 | "options": {
75 | "browserTarget": "ngx-tabset-demo:build"
76 | }
77 | },
78 | "test": {
79 | "builder": "@angular-devkit/build-angular:karma",
80 | "options": {
81 | "main": "src/test.ts",
82 | "polyfills": "src/polyfills.ts",
83 | "tsConfig": "src/tsconfig.spec.json",
84 | "karmaConfig": "src/karma.conf.js",
85 | "styles": [
86 | "src/styles.scss"
87 | ],
88 | "scripts": [],
89 | "assets": [
90 | "src/favicon.ico",
91 | "src/assets"
92 | ]
93 | }
94 | },
95 | "lint": {
96 | "builder": "@angular-devkit/build-angular:tslint",
97 | "options": {
98 | "tsConfig": [
99 | "src/tsconfig.app.json",
100 | "src/tsconfig.spec.json"
101 | ],
102 | "exclude": [
103 | "**/node_modules/**"
104 | ]
105 | }
106 | }
107 | }
108 | },
109 | "ngx-tabset-demo-e2e": {
110 | "root": "e2e/",
111 | "projectType": "application",
112 | "prefix": "",
113 | "architect": {
114 | "e2e": {
115 | "builder": "@angular-devkit/build-angular:protractor",
116 | "options": {
117 | "protractorConfig": "e2e/protractor.conf.js",
118 | "devServerTarget": "ngx-tabset-demo:serve"
119 | },
120 | "configurations": {
121 | "production": {
122 | "devServerTarget": "ngx-tabset-demo:serve:production"
123 | }
124 | }
125 | },
126 | "lint": {
127 | "builder": "@angular-devkit/build-angular:tslint",
128 | "options": {
129 | "tsConfig": "e2e/tsconfig.e2e.json",
130 | "exclude": [
131 | "**/node_modules/**"
132 | ]
133 | }
134 | }
135 | }
136 | }
137 | },
138 | "defaultProject": "ngx-tabset-demo"
139 | }
140 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://greenkeeper.io/)
4 | [](https://travis-ci.org/maximelafarie/ngx-tabset) [](https://badge.fury.io/js/ngx-tabset) [](https://npmjs.org/ngx-tabset) [](https://codecov.io/gh/maximelafarie/ngx-tabset)
5 |
6 | `ngx-tabset` is a very simple library to let you create some tabs. It uses flex-box and is compatible with Angular >=2.0.0.
7 |
8 | ## Demo
9 | https://maximelafarie.com/ngx-tabset/
10 |
11 | **This library doesn't use any framework (no CSS library, no JQuery...)**
12 |
13 | ## Setup
14 | To use `ngx-tabset` in your project install it via [npm](https://www.npmjs.com/package/ngx-tabset) / [yarn](https://yarnpkg.com/fr/package/ngx-tabset):
15 | ```
16 | npm i ngx-tabset --save
17 | ```
18 | or
19 | ```
20 | yarn add ngx-tabset
21 | ```
22 |
23 | If you are using system.js you may want to add this into your config:
24 |
25 | ```json
26 | System.config({
27 | "map": {
28 | "ngx-tabset": "node_modules/ngx-tabset/bundles/ngx-tabset.umd.js"
29 | }
30 | });
31 | ```
32 |
33 | ## Usage
34 |
35 | Import `TabsModule` in your app module and start using it in any component:
36 | ```typescript
37 | import {CommonModule} from '@angular/common';
38 | import {BrowserModule} from '@angular/platform-browser';
39 | import {NgModule} from '@angular/core';
40 |
41 | import {AppComponent} from './app.component';
42 | import {TabsModule} from 'ngx-tabset';
43 |
44 | @NgModule({
45 | declarations: [
46 | AppComponent
47 | ],
48 | imports: [
49 | BrowserModule,
50 | CommonModule,
51 | TabsModule.forRoot()
52 | ],
53 | providers: [],
54 | bootstrap: [AppComponent]
55 | })
56 | export class AppModule {
57 | }
58 | ```
59 |
60 | And import `ngx-tabset.scss` or `ngx-tabset.css` in a global style file (e.g. `styles.scss` or `styles.css` in classic Angular projects or any other scss/css file it imports):
61 | Example with **styles.scss**:
62 | ```
63 | /* You can add global styles to this file, and also import other style files */
64 | @import "~ngx-tabset/ngx-tabset";
65 | @import "app/app.component";
66 | ...
67 | ```
68 | [Demo example here](https://maximelafarie.com/ngx-tabset/blob/master/src/styles.scss)
69 |
70 | ### Basic example
71 | ```html
72 |
73 |
74 | Its all about me.
75 |
76 |
77 |
78 | This is content of the contacts tab
79 |
80 |
81 |
82 | Content of the Map Tab
83 |
84 |
85 | ```
86 |
87 | ### Content projection example
88 | ```html
89 |
90 |
91 |
92 |
93 |
94 |
95 | ...
96 |
97 | ```
98 |
99 | * `` is a container for all tabs
100 | * `[disableStyle]="true|false"` Disables/enables the built-in style. It allows you to style the entire tab yourself
101 | * `(onSelect)="doSomethingOnTabSelect($event)"` Callback to be called when tab is being selected. It returns the index of the selected tab into tabset collection.
102 | * `` is a single tab item
103 | * `tabTitle` The tab title
104 | * `tabSubTitle` The tab subtitle
105 | * `[disabled]="true|false` Indicates if current tab is enabled or disabled
106 | * `[active]="true|false"` Specifies which tab should be active on init. By default the first tab will be active.
107 |
108 |
109 | ## Style
110 | ⚠️ **For `ngx-tabset` >= 2.0.0 only!**
111 | `ngx-tabset` provides built-in [SCSS variables](https://sass-lang.com/guide#topic-2) that you can override easily like it (assuming you imported `ngx-tabset.scss` as explained above):
112 | ```
113 | /* You can add global styles to this file, and also import other style files */
114 | /* NgxTabset variables override */
115 | $active-tab-color: rgba(0, 0, 0, .7);
116 | $nav-tab-padding: 20px;
117 |
118 | @import "~ngx-tabset/ngx-tabset";
119 | ...
120 | ```
121 |
122 | ### Available SCSS variables
123 | The below documentation will use the following pattern:
124 | > `parameter/option name` (type) | default value | _description_
125 |
126 | - `$active-tab-color` (hex / rgb / rgba) | `red` ― _Modifies the border color of the active tab_
127 |
128 | - `$default-tab-border` ([border](https://developer.mozilla.org/fr/docs/Web/CSS/border)) | `solid 1px transparent` ― _Modifies tab's default border style_
129 |
130 | - `$nav-tab-padding` (px / % / ...) | `10px` ― _Defines the nav tabs padding_
131 |
132 | - `$disabled-tab-opacity` (0 - 1) | `.5` ― _The nav tab opacity of disabled tabs_
133 |
134 | - `$tab-cursor` ([cursor](https://developer.mozilla.org/fr/docs/Web/CSS/cursor)) | `pointer` ― _Defines the cursor type for tabs that aren't disabled or active._
135 |
136 | - `$tab-border-transition-duration` (duration) | `200ms` ― _The animation duration. You can use any duration unit you want_
137 |
138 | - `$tab-border-transition-timing-function` (transition-timing-function Property) | `ease-in-out` ― _Specifies the speed curve of the transition effect ([available speed curves here](https://www.w3schools.com/cssref/css3_pr_transition-timing-function.asp))_
139 |
140 |
141 | ## Customization options
142 | `ngx-tabset` comes with several options in order to facilitate integration (with CSS frameworks, custom style, etc.).
143 |
144 | The below documentation will use the following pattern:
145 | > `parameter/option name` (type) | default value | required? ― _description_
146 |
147 | - `disableStyle` (boolean) | `false` ― _Enable / disable default tabset style. E.g.: it's useful if you want to keep the provided style on some tabs and disable it on others_
148 |
149 | - `bypassDOM` (boolean) | `false` ― _Option to allow the tab to trigger lifecycle events to the wrapped content, e.g. for wrapped components. You need to surround tab content with `...` in order to make it work. Please check the [above example](https://maximelafarie.com/ngx-tabset#more-complete-example) for full implementation_
150 |
151 | - `customNavClass` (string) | `''` ― _All the additionnal classes you want to add to the tabset **header / nav**. You can add several classes by giving a string with space-separated classnames_
152 |
153 | - `customTabsClass` (string) | `''` ― _All the additionnal classes you want to add to the tabset **container**. You can add several classes by giving a string with space-separated classnames_
154 |
155 | - `customPaneClass` (string) | `''` ― _All the additionnal classes you want to add to **each** tab pane. You can add several classes by giving a string with space-separated classnames_
156 |
157 | ## Issues
158 | If you wish to submit an issue, please use the available template to facilitate reading and comprehension of all issues encountered. You can find this template in `./github/issue_template.md`.
--------------------------------------------------------------------------------