ngx-bootstrap-tabs.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. import { Directive, ViewContainerRef, Input, Injectable, Component, Renderer2, ElementRef, HostBinding, EventEmitter, Output, TemplateRef, NgModule } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. /**
  4. * @fileoverview added by tsickle
  5. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  6. */
  7. class NgTranscludeDirective {
  8. /**
  9. * @param {?} viewRef
  10. */
  11. constructor(viewRef) {
  12. this.viewRef = viewRef;
  13. }
  14. /**
  15. * @param {?} templateRef
  16. * @return {?}
  17. */
  18. set ngTransclude(templateRef) {
  19. this._ngTransclude = templateRef;
  20. if (templateRef) {
  21. this.viewRef.createEmbeddedView(templateRef);
  22. }
  23. }
  24. /* tslint:disable-next-line:no-any */
  25. /**
  26. * @return {?}
  27. */
  28. get ngTransclude() {
  29. return this._ngTransclude;
  30. }
  31. }
  32. NgTranscludeDirective.decorators = [
  33. { type: Directive, args: [{
  34. selector: '[ngTransclude]'
  35. },] }
  36. ];
  37. /** @nocollapse */
  38. NgTranscludeDirective.ctorParameters = () => [
  39. { type: ViewContainerRef }
  40. ];
  41. NgTranscludeDirective.propDecorators = {
  42. ngTransclude: [{ type: Input }]
  43. };
  44. /**
  45. * @fileoverview added by tsickle
  46. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  47. */
  48. class TabsetConfig {
  49. constructor() {
  50. /**
  51. * provides default navigation context class: 'tabs' or 'pills'
  52. */
  53. this.type = 'tabs';
  54. }
  55. }
  56. TabsetConfig.decorators = [
  57. { type: Injectable }
  58. ];
  59. /**
  60. * @fileoverview added by tsickle
  61. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  62. */
  63. // todo: add active event to tab
  64. // todo: fix? mixing static and dynamic tabs position tabs in order of creation
  65. class TabsetComponent {
  66. /**
  67. * @param {?} config
  68. * @param {?} renderer
  69. * @param {?} elementRef
  70. */
  71. constructor(config, renderer, elementRef) {
  72. this.renderer = renderer;
  73. this.elementRef = elementRef;
  74. this.clazz = true;
  75. this.tabs = [];
  76. this.classMap = {};
  77. Object.assign(this, config);
  78. }
  79. /**
  80. * if true tabs will be placed vertically
  81. * @return {?}
  82. */
  83. get vertical() {
  84. return this._vertical;
  85. }
  86. /**
  87. * @param {?} value
  88. * @return {?}
  89. */
  90. set vertical(value) {
  91. this._vertical = value;
  92. this.setClassMap();
  93. }
  94. /**
  95. * if true tabs fill the container and have a consistent width
  96. * @return {?}
  97. */
  98. get justified() {
  99. return this._justified;
  100. }
  101. /**
  102. * @param {?} value
  103. * @return {?}
  104. */
  105. set justified(value) {
  106. this._justified = value;
  107. this.setClassMap();
  108. }
  109. /**
  110. * navigation context class: 'tabs' or 'pills'
  111. * @return {?}
  112. */
  113. get type() {
  114. return this._type;
  115. }
  116. /**
  117. * @param {?} value
  118. * @return {?}
  119. */
  120. set type(value) {
  121. this._type = value;
  122. this.setClassMap();
  123. }
  124. /**
  125. * @return {?}
  126. */
  127. ngOnDestroy() {
  128. this.isDestroyed = true;
  129. }
  130. /**
  131. * @param {?} tab
  132. * @return {?}
  133. */
  134. addTab(tab) {
  135. this.tabs.push(tab);
  136. tab.active = this.tabs.length === 1 && typeof tab.active === 'undefined';
  137. }
  138. /**
  139. * @param {?} tab
  140. * @param {?=} options
  141. * @return {?}
  142. */
  143. removeTab(tab, options = { reselect: true, emit: true }) {
  144. /** @type {?} */
  145. const index = this.tabs.indexOf(tab);
  146. if (index === -1 || this.isDestroyed) {
  147. return;
  148. }
  149. // Select a new tab if the tab to be removed is selected and not destroyed
  150. if (options.reselect && tab.active && this.hasAvailableTabs(index)) {
  151. /** @type {?} */
  152. const newActiveIndex = this.getClosestTabIndex(index);
  153. this.tabs[newActiveIndex].active = true;
  154. }
  155. if (options.emit) {
  156. tab.removed.emit(tab);
  157. }
  158. this.tabs.splice(index, 1);
  159. if (tab.elementRef.nativeElement.parentNode) {
  160. this.renderer.removeChild(tab.elementRef.nativeElement.parentNode, tab.elementRef.nativeElement);
  161. }
  162. }
  163. /* tslint:disable-next-line: cyclomatic-complexity */
  164. /**
  165. * @param {?} event
  166. * @param {?} index
  167. * @return {?}
  168. */
  169. keyNavActions(event, index) {
  170. /** @type {?} */
  171. const list = Array.from(this.elementRef.nativeElement.querySelectorAll('.nav-link'));
  172. // const activeElList = list.filter((el: HTMLElement) => !el.classList.contains('disabled'));
  173. // tslint:disable-next-line:deprecation
  174. if (event.keyCode === 13 || event.key === 'Enter' || event.keyCode === 32 || event.key === 'Space') {
  175. event.preventDefault();
  176. /** @type {?} */
  177. const currentTab = list[(index) % list.length];
  178. currentTab.click();
  179. return;
  180. }
  181. // tslint:disable-next-line:deprecation
  182. if (event.keyCode === 39 || event.key === 'RightArrow') {
  183. /** @type {?} */
  184. let nextTab;
  185. /** @type {?} */
  186. let shift = 1;
  187. do {
  188. nextTab = list[(index + shift) % list.length];
  189. shift++;
  190. } while (nextTab.classList.contains('disabled'));
  191. nextTab.focus();
  192. return;
  193. }
  194. // tslint:disable-next-line:deprecation
  195. if (event.keyCode === 37 || event.key === 'LeftArrow') {
  196. /** @type {?} */
  197. let previousTab;
  198. /** @type {?} */
  199. let shift = 1;
  200. /** @type {?} */
  201. let i = index;
  202. do {
  203. if ((i - shift) < 0) {
  204. i = list.length - 1;
  205. previousTab = list[i];
  206. shift = 0;
  207. }
  208. else {
  209. previousTab = list[i - shift];
  210. }
  211. shift++;
  212. } while (previousTab.classList.contains('disabled'));
  213. previousTab.focus();
  214. return;
  215. }
  216. // tslint:disable-next-line:deprecation
  217. if (event.keyCode === 36 || event.key === 'Home') {
  218. event.preventDefault();
  219. /** @type {?} */
  220. let firstTab;
  221. /** @type {?} */
  222. let shift = 0;
  223. do {
  224. firstTab = list[shift % list.length];
  225. shift++;
  226. } while (firstTab.classList.contains('disabled'));
  227. firstTab.focus();
  228. return;
  229. }
  230. // tslint:disable-next-line:deprecation
  231. if (event.keyCode === 35 || event.key === 'End') {
  232. event.preventDefault();
  233. /** @type {?} */
  234. let lastTab;
  235. /** @type {?} */
  236. let shift = 1;
  237. /** @type {?} */
  238. let i = index;
  239. do {
  240. if ((i - shift) < 0) {
  241. i = list.length - 1;
  242. lastTab = list[i];
  243. shift = 0;
  244. }
  245. else {
  246. lastTab = list[i - shift];
  247. }
  248. shift++;
  249. } while (lastTab.classList.contains('disabled'));
  250. lastTab.focus();
  251. return;
  252. }
  253. // tslint:disable-next-line:deprecation
  254. if (event.keyCode === 46 || event.key === 'Delete') {
  255. if (this.tabs[index].removable) {
  256. this.removeTab(this.tabs[index]);
  257. if (list[index + 1]) {
  258. list[(index + 1) % list.length].focus();
  259. return;
  260. }
  261. if (list[list.length - 1]) {
  262. list[0].focus();
  263. }
  264. }
  265. }
  266. }
  267. /**
  268. * @protected
  269. * @param {?} index
  270. * @return {?}
  271. */
  272. getClosestTabIndex(index) {
  273. /** @type {?} */
  274. const tabsLength = this.tabs.length;
  275. if (!tabsLength) {
  276. return -1;
  277. }
  278. for (let step = 1; step <= tabsLength; step += 1) {
  279. /** @type {?} */
  280. const prevIndex = index - step;
  281. /** @type {?} */
  282. const nextIndex = index + step;
  283. if (this.tabs[prevIndex] && !this.tabs[prevIndex].disabled) {
  284. return prevIndex;
  285. }
  286. if (this.tabs[nextIndex] && !this.tabs[nextIndex].disabled) {
  287. return nextIndex;
  288. }
  289. }
  290. return -1;
  291. }
  292. /**
  293. * @protected
  294. * @param {?} index
  295. * @return {?}
  296. */
  297. hasAvailableTabs(index) {
  298. /** @type {?} */
  299. const tabsLength = this.tabs.length;
  300. if (!tabsLength) {
  301. return false;
  302. }
  303. for (let i = 0; i < tabsLength; i += 1) {
  304. if (!this.tabs[i].disabled && i !== index) {
  305. return true;
  306. }
  307. }
  308. return false;
  309. }
  310. /**
  311. * @protected
  312. * @return {?}
  313. */
  314. setClassMap() {
  315. this.classMap = {
  316. 'nav-stacked': this.vertical,
  317. 'flex-column': this.vertical,
  318. 'nav-justified': this.justified,
  319. [`nav-${this.type}`]: true
  320. };
  321. }
  322. }
  323. TabsetComponent.decorators = [
  324. { type: Component, args: [{
  325. selector: 'tabset',
  326. template: "<ul class=\"nav\" [ngClass]=\"classMap\" (click)=\"$event.preventDefault()\">\n <li *ngFor=\"let tabz of tabs; let i = index\" [ngClass]=\"['nav-item', tabz.customClass || '']\"\n [class.active]=\"tabz.active\" [class.disabled]=\"tabz.disabled\" (keydown)=\"keyNavActions($event, i)\">\n <a href=\"javascript:void(0);\" class=\"nav-link\"\n [attr.id]=\"tabz.id ? tabz.id + '-link' : ''\"\n [class.active]=\"tabz.active\" [class.disabled]=\"tabz.disabled\"\n (click)=\"tabz.active = true\">\n <span [ngTransclude]=\"tabz.headingRef\">{{ tabz.heading }}</span>\n <span *ngIf=\"tabz.removable\" (click)=\"$event.preventDefault(); removeTab(tabz);\" class=\"bs-remove-tab\"> &#10060;</span>\n </a>\n </li>\n</ul>\n<div class=\"tab-content\">\n <ng-content></ng-content>\n</div>\n",
  327. styles: [":host .nav-tabs .nav-item.disabled a.disabled{cursor:default}"]
  328. }] }
  329. ];
  330. /** @nocollapse */
  331. TabsetComponent.ctorParameters = () => [
  332. { type: TabsetConfig },
  333. { type: Renderer2 },
  334. { type: ElementRef }
  335. ];
  336. TabsetComponent.propDecorators = {
  337. vertical: [{ type: Input }],
  338. justified: [{ type: Input }],
  339. type: [{ type: Input }],
  340. clazz: [{ type: HostBinding, args: ['class.tab-container',] }]
  341. };
  342. /**
  343. * @fileoverview added by tsickle
  344. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  345. */
  346. class TabDirective {
  347. /**
  348. * @param {?} tabset
  349. * @param {?} elementRef
  350. * @param {?} renderer
  351. */
  352. constructor(tabset, elementRef, renderer) {
  353. this.elementRef = elementRef;
  354. this.renderer = renderer;
  355. /**
  356. * fired when tab became active, $event:Tab equals to selected instance of Tab component
  357. */
  358. this.selectTab = new EventEmitter();
  359. /**
  360. * fired when tab became inactive, $event:Tab equals to deselected instance of Tab component
  361. */
  362. this.deselect = new EventEmitter();
  363. /**
  364. * fired before tab will be removed, $event:Tab equals to instance of removed tab
  365. */
  366. this.removed = new EventEmitter();
  367. this.addClass = true;
  368. this.tabset = tabset;
  369. this.tabset.addTab(this);
  370. }
  371. /**
  372. * if set, will be added to the tab's class attribute. Multiple classes are supported.
  373. * @return {?}
  374. */
  375. get customClass() {
  376. return this._customClass;
  377. }
  378. /**
  379. * @param {?} customClass
  380. * @return {?}
  381. */
  382. set customClass(customClass) {
  383. if (this.customClass) {
  384. this.customClass.split(' ').forEach((/**
  385. * @param {?} cssClass
  386. * @return {?}
  387. */
  388. (cssClass) => {
  389. this.renderer.removeClass(this.elementRef.nativeElement, cssClass);
  390. }));
  391. }
  392. this._customClass = customClass ? customClass.trim() : null;
  393. if (this.customClass) {
  394. this.customClass.split(' ').forEach((/**
  395. * @param {?} cssClass
  396. * @return {?}
  397. */
  398. (cssClass) => {
  399. this.renderer.addClass(this.elementRef.nativeElement, cssClass);
  400. }));
  401. }
  402. }
  403. /**
  404. * tab active state toggle
  405. * @return {?}
  406. */
  407. get active() {
  408. return this._active;
  409. }
  410. /**
  411. * @param {?} active
  412. * @return {?}
  413. */
  414. set active(active) {
  415. if (this._active === active) {
  416. return;
  417. }
  418. if ((this.disabled && active) || !active) {
  419. if (this._active && !active) {
  420. this.deselect.emit(this);
  421. this._active = active;
  422. }
  423. return;
  424. }
  425. this._active = active;
  426. this.selectTab.emit(this);
  427. this.tabset.tabs.forEach((/**
  428. * @param {?} tab
  429. * @return {?}
  430. */
  431. (tab) => {
  432. if (tab !== this) {
  433. tab.active = false;
  434. }
  435. }));
  436. }
  437. /**
  438. * @return {?}
  439. */
  440. ngOnInit() {
  441. this.removable = this.removable;
  442. }
  443. /**
  444. * @return {?}
  445. */
  446. ngOnDestroy() {
  447. this.tabset.removeTab(this, { reselect: false, emit: false });
  448. }
  449. }
  450. TabDirective.decorators = [
  451. { type: Directive, args: [{ selector: 'tab, [tab]' },] }
  452. ];
  453. /** @nocollapse */
  454. TabDirective.ctorParameters = () => [
  455. { type: TabsetComponent },
  456. { type: ElementRef },
  457. { type: Renderer2 }
  458. ];
  459. TabDirective.propDecorators = {
  460. heading: [{ type: Input }],
  461. id: [{ type: HostBinding, args: ['attr.id',] }, { type: Input }],
  462. disabled: [{ type: Input }],
  463. removable: [{ type: Input }],
  464. customClass: [{ type: Input }],
  465. active: [{ type: HostBinding, args: ['class.active',] }, { type: Input }],
  466. selectTab: [{ type: Output }],
  467. deselect: [{ type: Output }],
  468. removed: [{ type: Output }],
  469. addClass: [{ type: HostBinding, args: ['class.tab-pane',] }]
  470. };
  471. /**
  472. * @fileoverview added by tsickle
  473. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  474. */
  475. /**
  476. * Should be used to mark <ng-template> element as a template for tab heading
  477. */
  478. class TabHeadingDirective {
  479. /* tslint:disable-next-line:no-any */
  480. /**
  481. * @param {?} templateRef
  482. * @param {?} tab
  483. */
  484. constructor(templateRef, tab) {
  485. tab.headingRef = templateRef;
  486. }
  487. }
  488. TabHeadingDirective.decorators = [
  489. { type: Directive, args: [{ selector: '[tabHeading]' },] }
  490. ];
  491. /** @nocollapse */
  492. TabHeadingDirective.ctorParameters = () => [
  493. { type: TemplateRef },
  494. { type: TabDirective }
  495. ];
  496. /**
  497. * @fileoverview added by tsickle
  498. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  499. */
  500. class TabsModule {
  501. /**
  502. * @return {?}
  503. */
  504. static forRoot() {
  505. return {
  506. ngModule: TabsModule,
  507. providers: [TabsetConfig]
  508. };
  509. }
  510. }
  511. TabsModule.decorators = [
  512. { type: NgModule, args: [{
  513. imports: [CommonModule],
  514. declarations: [
  515. NgTranscludeDirective,
  516. TabDirective,
  517. TabsetComponent,
  518. TabHeadingDirective
  519. ],
  520. exports: [
  521. TabDirective,
  522. TabsetComponent,
  523. TabHeadingDirective,
  524. NgTranscludeDirective
  525. ]
  526. },] }
  527. ];
  528. export { NgTranscludeDirective, TabDirective, TabHeadingDirective, TabsModule, TabsetComponent, TabsetConfig };
  529. //# sourceMappingURL=ngx-bootstrap-tabs.js.map