refactor(tabbar): removes ion-tab-button

This commit is contained in:
Manu Mtz.-Almeida
2018-08-12 22:50:52 +02:00
parent 0dc70f7f6d
commit 0842939273
16 changed files with 157 additions and 328 deletions

View File

@ -46,7 +46,6 @@ exports.config.outputTargets = [
'ion-router-outlet',
'ion-anchor',
'ion-tabbar',
'ion-tab-button',
// auxiliar
'ion-picker-column',

View File

@ -2103,7 +2103,7 @@ declare global {
* The text to display on the ok button. Default: `OK`.
*/
'okText': string;
'open': (ev?: UIEvent | undefined) => Promise<HTMLIonPopoverElement> | Promise<HTMLIonActionSheetElement> | Promise<HTMLIonAlertElement>;
'open': (ev?: UIEvent | undefined) => Promise<HTMLIonActionSheetElement> | Promise<HTMLIonAlertElement> | Promise<HTMLIonPopoverElement>;
/**
* The text to display when the select is empty.
*/
@ -2266,21 +2266,6 @@ declare global {
'when': string | boolean;
}
interface IonTabButton {
'badge': string;
'badgeColor': string;
'color': Color;
'disabled': boolean;
'href': string;
'icon': string;
'label': string;
'mode': Mode;
/**
* If true, the tab button will be selected. Defaults to `false`.
*/
'selected': boolean;
}
interface IonTab {
/**
* If true, sets the tab as the active tab.
@ -2315,7 +2300,7 @@ declare global {
*/
'getTabId': () => string | null;
/**
* The URL which will be used as the `href` within this tab's `<ion-tab-button>` anchor.
* The URL which will be used as the `href` within this tab's button anchor.
*/
'href': string;
/**
@ -3384,14 +3369,6 @@ declare global {
};
interface HTMLIonTabButtonElement extends StencilComponents.IonTabButton, HTMLStencilElement {}
var HTMLIonTabButtonElement: {
prototype: HTMLIonTabButtonElement;
new (): HTMLIonTabButtonElement;
};
interface HTMLIonTabElement extends StencilComponents.IonTab, HTMLStencilElement {}
var HTMLIonTabElement: {
@ -3579,7 +3556,6 @@ declare global {
'ion-slides': JSXElements.IonSlidesAttributes;
'ion-spinner': JSXElements.IonSpinnerAttributes;
'ion-split-pane': JSXElements.IonSplitPaneAttributes;
'ion-tab-button': JSXElements.IonTabButtonAttributes;
'ion-tab': JSXElements.IonTabAttributes;
'ion-tabbar': JSXElements.IonTabbarAttributes;
'ion-tabs': JSXElements.IonTabsAttributes;
@ -5826,21 +5802,6 @@ declare global {
'when'?: string | boolean;
}
export interface IonTabButtonAttributes extends HTMLAttributes {
'badge'?: string;
'badgeColor'?: string;
'color'?: Color;
'disabled'?: boolean;
'href'?: string;
'icon'?: string;
'label'?: string;
'mode'?: Mode;
/**
* If true, the tab button will be selected. Defaults to `false`.
*/
'selected'?: boolean;
}
export interface IonTabAttributes extends HTMLAttributes {
/**
* If true, sets the tab as the active tab.
@ -5871,7 +5832,7 @@ declare global {
*/
'disabled'?: boolean;
/**
* The URL which will be used as the `href` within this tab's `<ion-tab-button>` anchor.
* The URL which will be used as the `href` within this tab's button anchor.
*/
'href'?: string;
/**
@ -6364,7 +6325,6 @@ declare global {
'ion-slides': HTMLIonSlidesElement
'ion-spinner': HTMLIonSpinnerElement
'ion-split-pane': HTMLIonSplitPaneElement
'ion-tab-button': HTMLIonTabButtonElement
'ion-tab': HTMLIonTabElement
'ion-tabbar': HTMLIonTabbarElement
'ion-tabs': HTMLIonTabsElement
@ -6468,7 +6428,6 @@ declare global {
'ion-slides': HTMLIonSlidesElement;
'ion-spinner': HTMLIonSpinnerElement;
'ion-split-pane': HTMLIonSplitPaneElement;
'ion-tab-button': HTMLIonTabButtonElement;
'ion-tab': HTMLIonTabElement;
'ion-tabbar': HTMLIonTabbarElement;
'ion-tabs': HTMLIonTabsElement;

View File

@ -1,26 +0,0 @@
# ion-tab-button
TabButton is an internal component for tabs. Please see the [Tab docs](../tab) for more details.
<!-- Auto Generated Below -->
## Properties
| Property | Attribute | Description | Type |
| ------------ | ------------- | -------------------------------------------------------------- | --------- |
| `badgeColor` | `badge-color` | | `string` |
| `badge` | `badge` | | `string` |
| `color` | `color` | | `Color` |
| `disabled` | `disabled` | | `boolean` |
| `href` | `href` | | `string` |
| `icon` | `icon` | | `string` |
| `label` | `label` | | `string` |
| `mode` | `mode` | | `Mode` |
| `selected` | `selected` | If true, the tab button will be selected. Defaults to `false`. | `boolean` |
----------------------------------------------
*Built with [StencilJS](https://stenciljs.com/)*

View File

@ -1,82 +0,0 @@
import { Component, Element, Prop, State } from '@stencil/core';
import { Color, Mode } from '../../interface';
import { createColorClasses } from '../../utils/theme';
@Component({
tag: 'ion-tab-button',
styleUrls: {
ios: 'tab-button.ios.scss',
md: 'tab-button.md.scss'
},
shadow: true
})
export class TabButton {
@Element() el!: HTMLElement;
@Prop() mode!: Mode;
@Prop() color?: Color;
@State() keyFocus = false;
/**
* If true, the tab button will be selected. Defaults to `false`.
*/
@Prop() selected = false;
@Prop() label?: string;
@Prop() icon?: string;
@Prop() badge?: string;
@Prop() disabled?: boolean;
@Prop() badgeColor?: string;
@Prop() href?: string;
private onKeyUp() {
this.keyFocus = true;
}
private onBlur() {
this.keyFocus = false;
}
hostData() {
const selected = this.selected;
const hasLabel = !!this.label;
const hasIcon = !!this.icon;
const hasLabelOnly = (hasLabel && !hasIcon);
const hasIconOnly = (hasIcon && !hasLabel);
const hasBadge = !!this.badge;
return {
'role': 'tab',
'aria-selected': selected ? 'true' : null,
class: {
...createColorClasses(this.color),
'tab-selected': selected,
'has-label': hasLabel,
'has-icon': hasIcon,
'has-label-only': hasLabelOnly,
'has-icon-only': hasIconOnly,
'has-badge': hasBadge,
'tab-button-disabled': this.disabled,
'focused': this.keyFocus
}
};
}
render() {
const { icon, label, href, badge, badgeColor, mode } = this;
return [
<a
href={href || '#'}
class="tab-button-native"
onKeyUp={this.onKeyUp.bind(this)}
onBlur={this.onBlur.bind(this)}>
{ icon && <ion-icon class="tab-button-icon" icon={icon} lazy={false}></ion-icon> }
{ label && <span class="tab-button-text">{label}</span> }
{ badge && <ion-badge class="tab-badge" color={badgeColor}>{badge}</ion-badge> }
{ mode === 'md' && <ion-ripple-effect tapClick={true}></ion-ripple-effect> }
</a>
];
}
}

View File

@ -30,7 +30,7 @@ export class Tab {
@Prop() label?: string;
/**
* The URL which will be used as the `href` within this tab's `<ion-tab-button>` anchor.
* The URL which will be used as the `href` within this tab's button anchor.
*/
@Prop() href?: string;

View File

@ -1,27 +1,20 @@
@import "./tab-button";
@import "./tab-button.ios.vars";
:host {
--color: #{$tab-button-ios-text-color};
--color-selected: #{$tabbar-ios-text-color-active};
--background-focused: #{$tabbar-ios-background-color-focused};
.tab-btn {
@include padding($tab-button-ios-padding-top, $tab-button-ios-padding-end, $tab-button-ios-padding-bottom, $tab-button-ios-padding-start);
max-width: $tab-button-ios-max-width;
font-size: $tab-button-ios-font-size;
}
.tab-button-native {
@include padding($tab-button-ios-padding-top, $tab-button-ios-padding-end, $tab-button-ios-padding-bottom, $tab-button-ios-padding-start);
}
.tab-button-text {
.tab-btn-text {
@include margin(0, null, 1px, null);
min-height: $tab-button-ios-font-size + 1;
}
:host(.has-label-only) .tab-button-text {
.tab-btn-has-label-only .tab-btn-text {
@include margin(2px, 0);
font-size: $tab-button-ios-font-size + 2;
@ -30,12 +23,12 @@
line-height: 1.1;
}
.tab-button-icon {
.tab-btn-icon {
@include margin(4px, null, null, null);
font-size: $tab-button-ios-icon-size;
}
.tab-button-icon::before {
.tab-btn-icon::before {
vertical-align: top;
}

View File

@ -4,27 +4,18 @@
// Material Design Tab Button
// --------------------------------------------------
:host {
--color: #{$tab-button-md-text-color};
--color-selected: #{$tabbar-md-text-color-active};
--background-focused: #{$tabbar-md-background-color-focused};
--icon-transform-selected: #{$tab-button-md-icon-transform-active};
.tab-btn {
@include padding($tab-button-md-padding-top, $tab-button-md-padding-end, $tab-button-md-padding-bottom, $tab-button-md-padding-start);
max-width: 168px;
font-weight: $tab-button-md-font-weight;
}
.tab-button-native {
@include padding($tab-button-md-padding-top, $tab-button-md-padding-end, $tab-button-md-padding-bottom, $tab-button-md-padding-start);
display: flex;
}
// Material Design Tab Button Text
// --------------------------------------------------
.tab-button-text {
.tab-btn-text {
@include margin($tab-button-md-text-margin-top, $tab-button-md-text-margin-end, $tab-button-md-text-margin-bottom, $tab-button-md-text-margin-start);
@include transform-origin(center, bottom);
@ -37,7 +28,7 @@
text-transform: $tab-button-md-text-capitalization;
}
:host(.tab-selected) .tab-button-text {
.tab-btn-selected .tab-btn-text {
--label-transform: #{$tab-button-md-text-transform-active};
transition: $tab-button-md-text-transition;
@ -46,7 +37,7 @@
// Material Design Tab Button Icon
// --------------------------------------------------
.tab-button-icon {
.tab-btn-icon {
@include transform-origin(center, center);
width: $tab-button-md-icon-size;

View File

@ -1,55 +1,15 @@
@import "../../themes/ionic.globals";
:host {
@include border-radius(0);
@include margin(0);
display: block;
position: relative;
flex: 1;
height: 100%;
border: 0;
background: var(--background);
color: var(--color);
text-align: center;
text-decoration: none;
overflow: hidden;
user-select: none;
z-index: 0;
box-sizing: border-box;
box-sizing: border-box;
}
:host(.focused) {
background: var(--background-focused);
}
:host(:hover),
:host(.tab-selected) {
color: var(--color-selected);
}
:host(.tab-hidden) {
/* stylelint-disable-next-line declaration-no-important */
display: none !important;
}
a {
text-decoration: none;
}
.tab-button-native {
.tab-btn {
@include text-inherit();
@include margin(0);
@include padding(0);
display: flex;
position: relative;
flex: 1;
flex-direction: var(--flex-direction, column);
align-items: center;
justify-content: var(--justify-content, flex-start);
@ -59,27 +19,38 @@ a {
border: 0;
outline: none;
background: transparent;
color: inherit;
text-decoration: none;
cursor: pointer;
box-sizing: border-box;
&:active,
&:focus {
outline: none;
}
-webkit-user-drag: none;
}
:host(.tab-button-disabled) {
.tab-btn:focus-visible {
background: var(--background-focused);
}
.tab-btn:hover,
.tab-btn-selected {
color: var(--color-selected);
}
.tab-btn-hidden {
/* stylelint-disable-next-line declaration-no-important */
display: none !important;
}
.tab-btn-disabled {
pointer-events: none;
}
:host(.tab-button-disabled) .tab-button-native {
opacity: .4;
}
.tab-button-text {
.tab-btn-text {
@include margin(var(--label-margin-top), null, var(--label-margin-bottom), null);
display: var(--label-display, block);
@ -89,7 +60,7 @@ a {
line-height: var(--label-line-height);
}
.tab-button-icon {
.tab-btn-icon {
@include margin(var(--icon-margin-top), null, var(--icon-margin-bottom), null);
display: var(--icon-display, block);
@ -100,8 +71,8 @@ a {
font-size: var(--icon-font-size);
}
.tab-button-text,
.tab-button-icon {
.tab-btn-text,
.tab-btn-icon {
align-self: center;
min-width: 26px;
@ -115,12 +86,12 @@ a {
box-sizing: border-box;
}
:host(.has-label-only) .tab-button-text {
.tab-btn-has-label-only .tab-btn-text {
white-space: normal;
}
:host(.has-icon-only) .tab-button-native,
:host(.has-label-only) .tab-button-native {
.tab-btn-has-icon-only,
.tab-btn-has-label-only {
--justify-content: center;
}
@ -128,7 +99,7 @@ a {
// Tab Badges
// --------------------------------------------------
.tab-badge {
.tab-btn-badge {
@include position(6%, 4%, null, null); // 4% fallback
@include position(null, var(--badge-end, calc(50% - 30px)), null, null);
@include padding(1px, 6px);
@ -144,14 +115,14 @@ a {
line-height: 16px;
}
:host(.has-label-only) .tab-badge {
.tab-btn-has-label-only .tab-btn-badge {
--badge-end: #{calc(50% - 50px)};
}
:host(.has-icon-only) .tab-badge {
.tab-btn-has-icon-only .tab-btn-badge {
--badge-end: #{calc(50% - 30px)};
}
:host(.tab-selected) .tab-button-icon {
.tab-btn-selected .tab-btn-icon {
transform: var(--icon-transform-selected);
}

View File

@ -1,5 +1,6 @@
@import "./tabbar";
@import "./tabbar.ios.vars";
@import "./tab-button.ios";
// iOS Tabs
// --------------------------------------------------
@ -8,7 +9,9 @@
// default color / background
--background: #{$tabbar-ios-background-color};
--background-rgb: var(--ion-tabbar-translucent-background-color-rgb, 248, 248, 248);
--color: #{$tabbar-ios-text-color-active};
--color: #{$tab-button-ios-text-color};
--color-selected: #{$tabbar-ios-text-color-active};
--background-focused: #{$tabbar-ios-background-color-focused};
justify-content: center;
@ -36,17 +39,17 @@
// iOS Tabbar Layout
// --------------------------------------------------
:host(.layout-icon-end) ion-tab-button,
:host(.layout-icon-start) ion-tab-button,
:host(.layout-icon-hide) ion-tab-button {
:host(.layout-icon-end) .tab-btn,
:host(.layout-icon-start) .tab-btn,
:host(.layout-icon-hide) .tab-btn {
--label-margin-top: 2px;
--label-margin-bottom: 2px;
--label-font-size: 14px;
--label-line-height: 1.1;
}
:host(.layout-icon-end) ion-tab-button,
:host(.layout-icon-start) ion-tab-button {
:host(.layout-icon-end) .tab-btn,
:host(.layout-icon-start) .tab-btn {
--icon-margin-top: 2px;
--icon-margin-bottom: 1px;
--icon-min-width: 24px;
@ -54,6 +57,6 @@
--icon-font-size: 24px;
}
:host(.layout-label-hide) ion-tab-button {
:host(.layout-label-hide) .tab-btn {
--icon-margin: 0;
}

View File

@ -1,10 +1,14 @@
@import "./tabbar";
@import "./tabbar.md.vars";
@import "./tab-button.md";
:host {
// default color / background
--color: #{$tab-button-md-text-color};
--color-selected: #{$tabbar-md-text-color-active};
--background: #{$tabbar-md-background-color};
--color: #{$tabbar-md-text-color-active};
--background-focused: #{$tabbar-md-background-color-focused};
--icon-transform-selected: #{$tab-button-md-icon-transform-active};
height: $tabbar-md-height;
@ -16,20 +20,20 @@
// Material Design Tabbar Layout
// --------------------------------------------------
:host(.layout-icon-top) ion-tab-button {
:host(.layout-icon-top) .tab-btn {
--label-margin-bottom: -2px;
}
:host(.layout-icon-end) ion-tab-button {
:host(.layout-icon-end) .tab-btn {
--icon-transform-selected: #{$tab-button-md-icon-right-transform-active};
}
:host(.layout-icon-bottom) ion-tab-button {
:host(.layout-icon-bottom) .tab-btn {
--label-margin-top: -2px;
--label-transform: transform-origin(center, top);
--icon-transform-selected: #{$tab-button-md-icon-bottom-transform-active};
}
:host(.layout-icon-start) ion-tab-button {
:host(.layout-icon-start) .tab-btn {
--icon-transform-selected: #{$tab-button-md-icon-left-transform-active};
}

View File

@ -1,5 +1,4 @@
@import "../../themes/ionic.globals.md";
@import "../tab-button/tab-button.md.vars";
// Material Design Tabs
// --------------------------------------------------

View File

@ -13,11 +13,12 @@
background: var(--background);
color: var(--color);
user-select: none;
z-index: $z-index-toolbar;
}
:host(.ion-color),
:host(.ion-color) ion-tab-button {
:host(.ion-color) {
--background: #{current-color(base)};
--color: #{current-color(contrast, .7)};
--color-selected: #{current-color(contrast)};
@ -37,7 +38,6 @@
}
// Tab Highlight
// --------------------------------------------------
@ -75,40 +75,40 @@
// Tab Layout
// --------------------------------------------------
:host(.layout-icon-start) ion-tab-button {
:host(.layout-icon-start) .tab-btn {
--flex-direction: row;
}
:host(.layout-icon-end) ion-tab-button {
:host(.layout-icon-end) .tab-btn {
--flex-direction: row-reverse;
}
:host(.layout-icon-bottom) ion-tab-button {
:host(.layout-icon-bottom) .tab-btn {
--flex-direction: column-reverse;
}
:host(.layout-icon-start) ion-tab-button,
:host(.layout-icon-end) ion-tab-button,
:host(.layout-icon-hide) ion-tab-button,
:host(.layout-label-hide) ion-tab-button {
:host(.layout-icon-start) .tab-btn,
:host(.layout-icon-end) .tab-btn,
:host(.layout-icon-hide) .tab-btn,
:host(.layout-label-hide) .tab-btn {
--justify-content: center;
}
:host(.layout-icon-hide) ion-tab-button {
:host(.layout-icon-hide) .tab-btn {
--icon-display: none;
}
:host(.layout-label-hide) ion-tab-button {
:host(.layout-label-hide) .tab-btn {
--label-display: none;
}
:host(.layout-icon-top) ion-tab-button,
:host(.layout-icon-bottom) ion-tab-button {
:host(.layout-icon-top) .tab-btn,
:host(.layout-icon-bottom) .tab-btn {
--badge-end: #{calc(50% - 30px)};
}
:host(.layout-icon-hide) ion-tab-button,
:host(.layout-icon-start) ion-tab-button,
:host(.layout-icon-end) ion-tab-button {
:host(.layout-icon-hide) .tab-btn,
:host(.layout-icon-start) .tab-btn,
:host(.layout-icon-end) .tab-btn {
--badge-end: #{calc(50% - 50px)};
}

View File

@ -1,4 +1,4 @@
import { Component, Element, Event, EventEmitter, Listen, Prop, QueueApi, State, Watch } from '@stencil/core';
import { Component, Element, Event, EventEmitter, Listen, Prop, QueueApi, State } from '@stencil/core';
import { Color, Mode, TabbarLayout, TabbarPlacement } from '../../interface';
import { createColorClasses } from '../../utils/theme';
@ -41,10 +41,10 @@ export class Tabbar {
/** The tabs to render */
@Prop() tabs: HTMLIonTabElement[] = [];
@Watch('selectedTab')
selectedTabChanged() {
this.updateHighlight();
}
// @Watch('selectedTab')
// selectedTabChanged() {
// this.updateHighlight();
// }
/**
* If true, show the tab highlight bar under the selected tab.
@ -71,32 +71,32 @@ export class Tabbar {
}
}
@Listen('window:resize')
onResize() {
this.updateHighlight();
}
// @Listen('window:resize')
// onResize() {
// this.updateHighlight();
// }
componentDidLoad() {
this.updateHighlight();
}
// componentDidLoad() {
// this.updateHighlight();
// }
private getSelectedButton(): HTMLIonTabButtonElement | undefined {
return Array.from(this.el.querySelectorAll('ion-tab-button'))
.find(btn => btn.selected);
}
// private getSelectedButton(): HTMLIonTabButtonElement | undefined {
// return Array.from(this.el.querySelectorAll('ion-tab-button'))
// .find(btn => btn.selected);
// }
private updateHighlight() {
if (!this.highlight) {
return;
}
this.queue.read(() => {
const btn = this.getSelectedButton();
const highlight = this.el.querySelector('div.tabbar-highlight') as HTMLElement;
if (btn && highlight) {
highlight.style.transform = `translate3d(${btn.offsetLeft}px,0,0) scaleX(${btn.offsetWidth})`;
}
});
}
// private updateHighlight() {
// if (!this.highlight) {
// return;
// }
// this.queue.read(() => {
// const btn = this.getSelectedButton();
// const highlight = this.el.querySelector('div.tabbar-highlight') as HTMLElement;
// if (btn && highlight) {
// highlight.style.transform = `translate3d(${btn.offsetLeft}px,0,0) scaleX(${btn.offsetWidth})`;
// }
// });
// }
hostData() {
const { color, translucent, layout, placement, keyboardVisible } = this;
@ -115,32 +115,50 @@ export class Tabbar {
render() {
const selectedTab = this.selectedTab;
return [
this.tabs.map(tab => (
<ion-tab-button
id={tab.btnId}
label={tab.label}
icon={tab.icon}
badge={tab.badge}
disabled={tab.disabled}
badgeColor={tab.badgeColor}
href={tab.href}
selected={selectedTab === tab}
mode={this.mode}
color={this.color}
aria-hidden={ !tab.show ? 'true' : null }
class={{ 'tab-hidden': !tab.show }}
onClick={ev => {
if (!tab.disabled) {
this.tabs.map(tab => renderTabButton(tab, tab === selectedTab, this.mode, () => {
this.ionTabbarClick.emit(tab);
}
ev.stopPropagation();
ev.preventDefault();
}}
/>
)),
})),
this.highlight && <div class="animated tabbar-highlight" />
];
}
}
function renderTabButton(tab: HTMLIonTabElement, selected: boolean, mode: string, onClick: () => void) {
const { icon, label, disabled, badge, badgeColor, href } = tab;
const hasLabel = !!label;
const hasIcon = !!icon;
const hasLabelOnly = (hasLabel && !hasIcon);
const hasIconOnly = (hasIcon && !hasLabel);
const hasBadge = !!badge;
return (
<a
role="tab"
aria-selected={selected ? 'true' : null}
href={href || '#'}
ion-activable
class={{
'tab-btn': true,
'tab-btn-selected': selected,
'tab-btn-has-label': hasLabel,
'tab-btn-has-icon': hasIcon,
'tab-btn-has-label-only': hasLabelOnly,
'tab-btn-has-icon-only': hasIconOnly,
'tab-btn-has-badge': hasBadge,
'tab-btn-disabled': disabled,
'tab-btn-hidden': !tab.show
}}
onClick={ev => {
if (!tab.disabled) {
onClick();
}
ev.stopPropagation();
ev.preventDefault();
}}>
{ icon && <ion-icon class="tab-btn-icon" icon={icon} lazy={false}></ion-icon> }
{ label && <span class="tab-btn-text">{label}</span> }
{ badge && <ion-badge class="tab-btn-badge" color={badgeColor}>{badge}</ion-badge> }
{ mode === 'md' && <ion-ripple-effect tapClick={true}></ion-ripple-effect> }
</a>
);
}

View File

@ -41,7 +41,7 @@ exports.config = {
{ components: ['ion-slides', 'ion-slide'] },
{ components: ['ion-spinner'] },
{ components: ['ion-split-pane'] },
{ components: ['ion-tabs', 'ion-tab', 'ion-tabbar', 'ion-tab-button'] },
{ components: ['ion-tabs', 'ion-tab', 'ion-tabbar'] },
{ components: ['ion-text'] },
{ components: ['ion-toast', 'ion-toast-controller'] },
{ components: ['ion-toggle'] },