Front end of the Slack clone application.

tab.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. import { ChangeDetectorRef, Component, ComponentFactoryResolver, ElementRef, ErrorHandler, EventEmitter, Input, NgZone, Optional, Output, Renderer, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
  2. import { App } from '../app/app';
  3. import { Config } from '../../config/config';
  4. import { DeepLinker } from '../../navigation/deep-linker';
  5. import { DomController } from '../../platform/dom-controller';
  6. import { GestureController } from '../../gestures/gesture-controller';
  7. import { isTrueProperty } from '../../util/util';
  8. import { NavControllerBase } from '../../navigation/nav-controller-base';
  9. import { Platform } from '../../platform/platform';
  10. import { Tabs } from './tabs';
  11. import { TransitionController } from '../../transitions/transition-controller';
  12. /**
  13. * @name Tab
  14. * @description
  15. * The Tab component, written `<ion-tab>`, is styled based on the mode and should
  16. * be used in conjunction with the [Tabs](../Tabs/) component.
  17. *
  18. * Each `ion-tab` is a declarative component for a [NavController](../../../navigation/NavController/).
  19. * Basically, each tab is a `NavController`. For more information on using
  20. * navigation controllers take a look at the [NavController API Docs](../../../navigation/NavController/).
  21. *
  22. * See the [Tabs API Docs](../Tabs/) for more details on configuring Tabs.
  23. *
  24. * @usage
  25. *
  26. * To add a basic tab, you can use the following markup where the `root` property
  27. * is the page you want to load for that tab, `tabTitle` is the optional text to
  28. * display on the tab, and `tabIcon` is the optional [icon](../../icon/Icon/).
  29. *
  30. * ```html
  31. * <ion-tabs>
  32. * <ion-tab [root]="chatRoot" tabTitle="Chat" tabIcon="chat"></ion-tab>
  33. * </ion-tabs>
  34. * ```
  35. *
  36. * Then, in your class you can set `chatRoot` to an imported class:
  37. *
  38. * ```ts
  39. * import { ChatPage } from '../chat/chat';
  40. *
  41. * export class Tabs {
  42. * // here we'll set the property of chatRoot to
  43. * // the imported class of ChatPage
  44. * chatRoot = ChatPage;
  45. *
  46. * constructor() {
  47. *
  48. * }
  49. * }
  50. * ```
  51. *
  52. * You can also pass some parameters to the root page of the tab through
  53. * `rootParams`. Below we pass `chatParams` to the Chat tab:
  54. *
  55. * ```html
  56. * <ion-tabs>
  57. * <ion-tab [root]="chatRoot" [rootParams]="chatParams" tabTitle="Chat" tabIcon="chat"></ion-tab>
  58. * </ion-tabs>
  59. * ```
  60. *
  61. * ```ts
  62. * export class Tabs {
  63. * chatRoot = ChatPage;
  64. *
  65. * // set some user information on chatParams
  66. * chatParams = {
  67. * user1: 'admin',
  68. * user2: 'ionic'
  69. * };
  70. *
  71. * constructor() {
  72. *
  73. * }
  74. * }
  75. * ```
  76. *
  77. * And in `ChatPage` you can get the data from `NavParams`:
  78. *
  79. * ```ts
  80. * export class ChatPage {
  81. * constructor(navParams: NavParams) {
  82. * console.log('Passed params', navParams.data);
  83. * }
  84. * }
  85. * ```
  86. *
  87. * Sometimes you may want to call a method instead of navigating to a new
  88. * page. You can use the `(ionSelect)` event to call a method on your class when
  89. * the tab is selected. Below is an example of presenting a modal from one of
  90. * the tabs.
  91. *
  92. * ```html
  93. * <ion-tabs>
  94. * <ion-tab (ionSelect)="chat()" tabTitle="Show Modal"></ion-tab>
  95. * </ion-tabs>pop
  96. * ```
  97. *
  98. * ```ts
  99. * export class Tabs {
  100. * constructor(public modalCtrl: ModalController) {
  101. *
  102. * }
  103. *
  104. * chat() {
  105. * let modal = this.modalCtrl.create(ChatPage);
  106. * modal.present();
  107. * }
  108. * }
  109. * ```
  110. *
  111. *
  112. * @demo /docs/demos/src/tabs/
  113. * @see {@link /docs/components#tabs Tabs Component Docs}
  114. * @see {@link ../../tabs/Tabs Tabs API Docs}
  115. * @see {@link ../../nav/Nav Nav API Docs}
  116. * @see {@link ../../nav/NavController NavController API Docs}
  117. */
  118. export class Tab extends NavControllerBase {
  119. constructor(parent, app, config, plt, elementRef, zone, renderer, cfr, _cd, gestureCtrl, transCtrl, linker, _dom, errHandler) {
  120. // A Tab is a NavController for its child pages
  121. super(parent, app, config, plt, elementRef, zone, renderer, cfr, gestureCtrl, transCtrl, linker, _dom, errHandler);
  122. this._cd = _cd;
  123. this.linker = linker;
  124. this._dom = _dom;
  125. /**
  126. * @hidden
  127. */
  128. this._isEnabled = true;
  129. /**
  130. * @hidden
  131. */
  132. this._isShown = true;
  133. /**
  134. * @output {Tab} Emitted when the current tab is selected.
  135. */
  136. this.ionSelect = new EventEmitter();
  137. this.id = parent.add(this);
  138. this._tabsHideOnSubPages = config.getBoolean('tabsHideOnSubPages');
  139. this._tabId = 'tabpanel-' + this.id;
  140. this._btnId = 'tab-' + this.id;
  141. }
  142. /**
  143. * @input {boolean} If true, enable the tab. If false,
  144. * the user cannot interact with this element.
  145. * Default: `true`.
  146. */
  147. get enabled() {
  148. return this._isEnabled;
  149. }
  150. set enabled(val) {
  151. this._isEnabled = isTrueProperty(val);
  152. }
  153. /**
  154. * @input {boolean} If true, the tab button is visible within the
  155. * tabbar. Default: `true`.
  156. */
  157. get show() {
  158. return this._isShown;
  159. }
  160. set show(val) {
  161. this._isShown = isTrueProperty(val);
  162. }
  163. /**
  164. * @input {boolean} If true, hide the tabs on child pages.
  165. */
  166. get tabsHideOnSubPages() {
  167. return this._tabsHideOnSubPages;
  168. }
  169. set tabsHideOnSubPages(val) {
  170. this._tabsHideOnSubPages = isTrueProperty(val);
  171. }
  172. /**
  173. * @hidden
  174. */
  175. set _vp(val) {
  176. this.setViewport(val);
  177. }
  178. /**
  179. * @hidden
  180. */
  181. ngOnInit() {
  182. this.tabBadgeStyle = this.tabBadgeStyle ? this.tabBadgeStyle : 'default';
  183. }
  184. /**
  185. * @hidden
  186. */
  187. load(opts) {
  188. const segment = this._segment;
  189. if (segment || (!this._loaded && this.root)) {
  190. this.setElementClass('show-tab', true);
  191. // okay, first thing we need to do if check if the view already exists
  192. const nameToUse = segment && segment.name ? segment.name : this.root;
  193. const dataToUse = segment ? segment.data : this.rootParams;
  194. const numViews = this.length() - 1;
  195. for (let i = numViews; i >= 0; i--) {
  196. const viewController = this.getByIndex(i);
  197. if (viewController && (viewController.id === nameToUse || viewController.component === nameToUse)) {
  198. if (i === numViews) {
  199. // this is the last view in the stack and it's the same
  200. // as the segment so there's no change needed
  201. return Promise.resolve();
  202. }
  203. else {
  204. // it's not the exact view as the end
  205. // let's have this nav go back to this exact view
  206. return this.popTo(viewController, {
  207. animate: false,
  208. updateUrl: false,
  209. });
  210. }
  211. }
  212. }
  213. let promise = null;
  214. if (segment && segment.defaultHistory && segment.defaultHistory.length && this._views.length === 0) {
  215. promise = this.linker.initViews(segment).then((views) => {
  216. return this.setPages(views, opts);
  217. });
  218. }
  219. else {
  220. promise = this.push(nameToUse, dataToUse, opts);
  221. }
  222. return promise.then(() => {
  223. this._segment = null;
  224. this._loaded = true;
  225. });
  226. }
  227. else {
  228. // if this is not the Tab's initial load then we need
  229. // to refresh the tabbar and content dimensions to be sure
  230. // they're lined up correctly
  231. this._dom.read(() => {
  232. this.resize();
  233. });
  234. return Promise.resolve();
  235. }
  236. }
  237. /**
  238. * @hidden
  239. */
  240. resize() {
  241. const active = this.getActive();
  242. if (!active) {
  243. return;
  244. }
  245. const content = active.getIONContent();
  246. content && content.resize();
  247. }
  248. /**
  249. * @hidden
  250. */
  251. _viewAttachToDOM(viewCtrl, componentRef, viewport) {
  252. const isTabSubPage = (this._tabsHideOnSubPages && viewCtrl.index > 0);
  253. if (isTabSubPage) {
  254. viewport = this.parent.portal;
  255. }
  256. super._viewAttachToDOM(viewCtrl, componentRef, viewport);
  257. if (isTabSubPage) {
  258. // add the .tab-subpage css class to tabs pages that should act like subpages
  259. const pageEleRef = viewCtrl.pageRef();
  260. if (pageEleRef) {
  261. this._renderer.setElementClass(pageEleRef.nativeElement, 'tab-subpage', true);
  262. }
  263. }
  264. }
  265. /**
  266. * @hidden
  267. */
  268. setSelected(isSelected) {
  269. this.isSelected = isSelected;
  270. this.setElementClass('show-tab', isSelected);
  271. this.setElementAttribute('aria-hidden', (!isSelected).toString());
  272. if (isSelected) {
  273. // this is the selected tab, detect changes
  274. this._cd.reattach();
  275. }
  276. else {
  277. // this tab is not selected, do not detect changes
  278. this._cd.detach();
  279. }
  280. }
  281. /**
  282. * @hidden
  283. */
  284. get index() {
  285. return this.parent.getIndex(this);
  286. }
  287. /**
  288. * @hidden
  289. */
  290. updateHref(component, data) {
  291. if (this.btn && this.linker) {
  292. let href = this.linker.createUrl(this.parent, component, data) || '#';
  293. this.btn.updateHref(href);
  294. }
  295. }
  296. /**
  297. * @hidden
  298. */
  299. ngOnDestroy() {
  300. this.destroy();
  301. }
  302. /**
  303. * @hidden
  304. */
  305. getType() {
  306. return 'tab';
  307. }
  308. goToRoot(opts) {
  309. return this.setRoot(this.root, this.rootParams, opts, null);
  310. }
  311. }
  312. Tab.decorators = [
  313. { type: Component, args: [{
  314. selector: 'ion-tab',
  315. template: '<div #viewport></div><div class="nav-decor"></div>',
  316. host: {
  317. '[attr.id]': '_tabId',
  318. '[attr.aria-labelledby]': '_btnId',
  319. 'role': 'tabpanel'
  320. },
  321. encapsulation: ViewEncapsulation.None,
  322. },] },
  323. ];
  324. /** @nocollapse */
  325. Tab.ctorParameters = () => [
  326. { type: Tabs, },
  327. { type: App, },
  328. { type: Config, },
  329. { type: Platform, },
  330. { type: ElementRef, },
  331. { type: NgZone, },
  332. { type: Renderer, },
  333. { type: ComponentFactoryResolver, },
  334. { type: ChangeDetectorRef, },
  335. { type: GestureController, },
  336. { type: TransitionController, },
  337. { type: DeepLinker, decorators: [{ type: Optional },] },
  338. { type: DomController, },
  339. { type: ErrorHandler, },
  340. ];
  341. Tab.propDecorators = {
  342. 'root': [{ type: Input },],
  343. 'rootParams': [{ type: Input },],
  344. 'tabUrlPath': [{ type: Input },],
  345. 'tabTitle': [{ type: Input },],
  346. 'tabIcon': [{ type: Input },],
  347. 'tabBadge': [{ type: Input },],
  348. 'tabBadgeStyle': [{ type: Input },],
  349. 'enabled': [{ type: Input },],
  350. 'show': [{ type: Input },],
  351. 'tabsHideOnSubPages': [{ type: Input },],
  352. 'ionSelect': [{ type: Output },],
  353. '_vp': [{ type: ViewChild, args: ['viewport', { read: ViewContainerRef },] },],
  354. };
  355. //# sourceMappingURL=tab.js.map