feat(button) combine boolean fill/size/expand props to string (#13348)

* chore(utils) extend getElementClassObject to accept string array
* feat(button) combine boolean fill/size/span props to string
* feat(button) rename span prop to expand
* docs(button) remove default from listed size options
* docs(button) clarify default button fill styles
This commit is contained in:
Cam Wiegert
2017-11-28 10:40:59 -06:00
committed by GitHub
parent 285a99a6a0
commit 444b77add0
6 changed files with 118 additions and 135 deletions

View File

@ -362,16 +362,11 @@ declare global {
itemButton?: boolean, itemButton?: boolean,
href?: string, href?: string,
buttonType?: string, buttonType?: string,
large?: boolean, size?: 'small' | 'large',
small?: boolean,
default?: boolean,
disabled?: boolean, disabled?: boolean,
outline?: boolean, fill?: 'clear' | 'outline' | 'solid' | 'default',
clear?: boolean,
solid?: boolean,
round?: boolean, round?: boolean,
block?: boolean, expand?: 'full' | 'block',
full?: boolean,
strong?: boolean, strong?: boolean,
color?: string, color?: string,
mode?: 'ios' | 'md' mode?: 'ios' | 'md'

View File

@ -1,7 +1,56 @@
import { Component, CssClassMap, Element, Prop } from '@stencil/core'; import { Component, Element, Prop } from '@stencil/core';
import { getElementClassObject } from '../../utils/theme'; import { getElementClassObject } from '../../utils/theme';
/**
* @name Button
* @module ionic
* @description
* Buttons are simple components in Ionic. They can consist of text and icons
* and be enhanced by a wide range of attributes.
*
* @usage
*
* ```html
*
* <!-- Colors -->
* <ion-button>Default</ion-button>
* <ion-button color="secondary">Secondary</ion-button>
* <ion-button color="danger">Danger</ion-button>
* <ion-button color="light">Light</ion-button>
* <ion-button color="dark">Dark</ion-button>
*
* <!-- Shapes -->
* <ion-button expand="full">Full Button</ion-button>
* <ion-button expand="block">Block Button</ion-button>
* <ion-button round>Round Button</ion-button>
*
* <!-- Outline -->
* <ion-button expand="full" fill="outline">Outline + Full</ion-button>
* <ion-button expand="block" fill="outline">Outline + Block</ion-button>
* <ion-button round fill="outline">Outline + Round</ion-button>
*
* <!-- Icons -->
* <ion-button>
* <ion-icon slot="start" name="star"></ion-icon>
* Left Icon
* </ion-button>
*
* <ion-button>
* Right Icon
* <ion-icon slot="end" name="star"></ion-icon>
* </ion-button>
*
* <ion-button>
* <ion-icon slot="icon-only" name="star"></ion-icon>
* </ion-button>
*
* <!-- Sizes -->
* <ion-button size="large">Large</ion-button>
* <ion-button>Default</ion-button>
* <ion-button size="small">Small</ion-button>
* ```
*
*/
@Component({ @Component({
tag: 'ion-button', tag: 'ion-button',
styleUrls: { styleUrls: {
@ -27,22 +76,10 @@ export class Button {
@Prop() buttonType: string = 'button'; @Prop() buttonType: string = 'button';
/** /**
* @input {boolean} If true, activates the large button size. * @input {string} The button size.
* Type: size * Possible values are: `"small"`, `"large"`.
*/ */
@Prop() large: boolean = false; @Prop() size: 'small' | 'large';
/**
* @input {boolean} If true, activates the small button size.
* Type: size
*/
@Prop() small: boolean = false;
/**
* @input {boolean} If true, activates the default button size. Normally the default, useful for buttons in an item.
* Type: size
*/
@Prop() default: boolean = false;
/** /**
* @input {boolean} If true, sets the button into a disabled state. * @input {boolean} If true, sets the button into a disabled state.
@ -50,22 +87,11 @@ export class Button {
@Prop() disabled: boolean = false; @Prop() disabled: boolean = false;
/** /**
* @input {boolean} If true, activates a transparent button style with a border. * @input {string} Set to `"clear"` for a transparent button, to `"outline"` for a transparent
* Type: style * button with a border, or to `"solid"`. The default style is `"solid"` except inside of
* `ion-navbar`, where the default is `"clear"`.
*/ */
@Prop() outline: boolean = false; @Prop() fill: 'clear' | 'outline' | 'solid' | 'default' = 'default';
/**
* @input {boolean} If true, activates a transparent button style without a border.
* Type: style
*/
@Prop() clear: boolean = false;
/**
* @input {boolean} If true, activates a solid button style. Normally the default, useful for buttons in a toolbar.
* Type: style
*/
@Prop() solid: boolean = false;
/** /**
* @input {boolean} If true, activates a button with rounded corners. * @input {boolean} If true, activates a button with rounded corners.
@ -74,17 +100,10 @@ export class Button {
@Prop() round: boolean = false; @Prop() round: boolean = false;
/** /**
* @input {boolean} If true, activates a button style that fills the available width. * @input {string} Set to `"block"` for a full-width button or to `"full"` for a full-width button
* Type: display * without left and right borders.
*/ */
@Prop() block: boolean = false; @Prop() expand: 'full' | 'block';
/**
* @input {boolean} If true, activates a button style that fills the available width without
* a left and right border.
* Type: display
*/
@Prop() full: boolean = false;
/** /**
* @input {boolean} If true, activates a button with a heavier font weight. * @input {boolean} If true, activates a button with a heavier font weight.
@ -106,45 +125,36 @@ export class Button {
*/ */
@Prop() mode: 'ios' | 'md'; @Prop() mode: 'ios' | 'md';
render() { protected render() {
const buttonType = this.buttonType;
const mode = this.mode;
const size = const {
(this.large ? 'large' : null) || buttonType,
(this.small ? 'small' : null) || itemButton,
(this.default ? 'default' : null); color,
expand,
fill,
mode,
round,
size,
strong
} = this;
const shape = (this.round ? 'round' : null); const elementClasses: string[] = []
const display =
(this.block ? 'block' : null) ||
(this.full ? 'full' : null);
const decorator = (this.strong ? 'strong' : null);
const hostClasses = getElementClassObject(this.el.classList);
const elementClasses: CssClassMap = []
.concat( .concat(
getButtonClassList(buttonType, mode), getButtonClassList(buttonType, mode),
getClassList(buttonType, shape, mode), getClassList(buttonType, expand, mode),
getClassList(buttonType, display, mode),
getClassList(buttonType, size, mode), getClassList(buttonType, size, mode),
getClassList(buttonType, decorator, mode), getClassList(buttonType, round ? 'round' : null, mode),
getStyleClassList(mode, this.color, buttonType, this.outline, this.clear, this.solid), getClassList(buttonType, strong ? 'strong' : null, mode),
getItemClassList(this.itemButton, size) getColorClassList(buttonType, color, fill, mode),
) getItemClassList(itemButton, size)
.reduce((prevValue, cssClass) => { );
prevValue[cssClass] = true;
return prevValue;
}, {});
const TagType = this.href ? 'a' : 'button'; const TagType = this.href ? 'a' : 'button';
const buttonClasses = { const buttonClasses = {
...hostClasses, ...getElementClassObject(this.el.classList),
...elementClasses ...getElementClassObject(elementClasses)
}; };
return ( return (
@ -191,55 +201,33 @@ function getClassList(buttonType: string, type: string, mode: string): string[]
]; ];
} }
/** function getColorClassList(buttonType: string, color: string, fill: string, mode: string): string[] {
* Get the classes for the color let className = buttonType;
*/
function getColorClassList(color: string, buttonType: string, style: string, mode: string): string[] {
style = (buttonType !== 'bar-button' && style === 'solid') ? 'default' : style;
let className = if (buttonType !== 'bar-button' && fill === 'solid') {
buttonType + fill = 'default';
((style && style !== 'default') ? }
'-' + style.toLowerCase() :
''); if (fill && fill !== 'default') {
className += `-${fill.toLowerCase()}`;
}
// special case for a default bar button // special case for a default bar button
// if the bar button is default it should get the style // if the bar button is default it should get the fill
// but if a color is passed the style shouldn't be added // but if a color is passed the fill shouldn't be added
if (buttonType === 'bar-button' && style === 'default') { if (buttonType === 'bar-button' && fill === 'default') {
className = buttonType; className = buttonType;
if (!color) { if (!color) {
className += '-' + style.toLowerCase(); className += '-' + fill.toLowerCase();
} }
} }
return [`${className}-${mode}`].concat( return [`${className}-${mode}`].concat(
style !== 'default' ? `${className}` : [], fill !== 'default' ? `${className}` : [],
color ? `${className}-${mode}-${color}` : [] color ? `${className}-${mode}-${color}` : []
); );
} }
/**
* Get the classes for the style
* e.g. outline, clear, solid
*/
function getStyleClassList(mode: string, color: string, buttonType: string, outline: boolean, clear: boolean, solid: boolean): string[] {
let classList = [].concat(
outline ? getColorClassList(color, buttonType, 'outline', mode) : [],
clear ? getColorClassList(color, buttonType, 'clear', mode) : [],
solid ? getColorClassList(color, buttonType, 'solid', mode) : []
);
if (classList.length === 0) {
classList = getColorClassList(color, buttonType, 'default', mode);
}
return classList;
}
/**
* Get the item classes for the button
*/
function getItemClassList(itemButton: boolean, size: string) { function getItemClassList(itemButton: boolean, size: string) {
return itemButton && !size ? ['item-button'] : []; return itemButton && !size ? ['item-button'] : [];
} }

View File

@ -43,7 +43,7 @@
<ion-toolbar color="primary"> <ion-toolbar color="primary">
<ion-buttons slot="mode-start"> <ion-buttons slot="mode-start">
<ion-button class="closeButton" clear> <ion-button class="closeButton" fill="clear">
Close Close
</ion-button> </ion-button>
<ion-button> <ion-button>
@ -94,17 +94,17 @@
<ion-toolbar> <ion-toolbar>
<ion-buttons slot="mode-start"> <ion-buttons slot="mode-start">
<ion-button solid> <ion-button fill="solid">
<ion-icon slot="icon-only" name="contact"></ion-icon> <ion-icon slot="icon-only" name="contact"></ion-icon>
</ion-button> </ion-button>
<ion-button solid> <ion-button fill="solid">
<ion-icon name="contact" slot="start"></ion-icon> <ion-icon name="contact" slot="start"></ion-icon>
Solid Solid
</ion-button> </ion-button>
</ion-buttons> </ion-buttons>
<ion-title>Solid</ion-title> <ion-title>Solid</ion-title>
<ion-buttons slot="mode-end"> <ion-buttons slot="mode-end">
<ion-button solid color="secondary"> <ion-button fill="solid" color="secondary">
Help Help
<ion-icon name="help-circle" slot="end"></ion-icon> <ion-icon name="help-circle" slot="end"></ion-icon>
</ion-button> </ion-button>
@ -113,17 +113,17 @@
<ion-toolbar> <ion-toolbar>
<ion-buttons slot="mode-start"> <ion-buttons slot="mode-start">
<ion-button solid class="activated"> <ion-button fill="solid" class="activated">
<ion-icon slot="icon-only" name="contact"></ion-icon> <ion-icon slot="icon-only" name="contact"></ion-icon>
</ion-button> </ion-button>
<ion-button solid class="activated"> <ion-button fill="solid" class="activated">
<ion-icon name="contact" slot="start"></ion-icon> <ion-icon name="contact" slot="start"></ion-icon>
Solid Solid
</ion-button> </ion-button>
</ion-buttons> </ion-buttons>
<ion-title>Solid Activated</ion-title> <ion-title>Solid Activated</ion-title>
<ion-buttons slot="mode-end"> <ion-buttons slot="mode-end">
<ion-button solid color="secondary" class="activated"> <ion-button fill="solid" color="secondary" class="activated">
Help Help
<ion-icon name="help-circle" slot="end"></ion-icon> <ion-icon name="help-circle" slot="end"></ion-icon>
</ion-button> </ion-button>
@ -132,16 +132,16 @@
<ion-toolbar> <ion-toolbar>
<ion-buttons slot="mode-start"> <ion-buttons slot="mode-start">
<ion-button outline> <ion-button fill="outline">
<ion-icon slot="icon-only" name="contact"></ion-icon> <ion-icon slot="icon-only" name="contact"></ion-icon>
</ion-button> </ion-button>
<ion-button outline> <ion-button fill="outline">
<ion-icon name="star" slot="start"></ion-icon> <ion-icon name="star" slot="start"></ion-icon>
Star Star
</ion-button> </ion-button>
</ion-buttons> </ion-buttons>
<ion-buttons slot="mode-end"> <ion-buttons slot="mode-end">
<ion-button color="secondary" outline> <ion-button color="secondary" fill="outline">
<ion-icon slot="icon-only" name="contact"></ion-icon> <ion-icon slot="icon-only" name="contact"></ion-icon>
</ion-button> </ion-button>
</ion-buttons> </ion-buttons>
@ -150,16 +150,16 @@
<ion-toolbar> <ion-toolbar>
<ion-buttons slot="mode-start"> <ion-buttons slot="mode-start">
<ion-button outline class="activated"> <ion-button fill="outline" class="activated">
<ion-icon slot="icon-only" name="contact"></ion-icon> <ion-icon slot="icon-only" name="contact"></ion-icon>
</ion-button> </ion-button>
<ion-button outline class="activated"> <ion-button fill="outline" class="activated">
<ion-icon name="star" slot="start"></ion-icon> <ion-icon name="star" slot="start"></ion-icon>
Star Star
</ion-button> </ion-button>
</ion-buttons> </ion-buttons>
<ion-buttons slot="mode-end"> <ion-buttons slot="mode-end">
<ion-button color="secondary" outline class="activated"> <ion-button color="secondary" fill="outline" class="activated">
<ion-icon slot="icon-only" name="contact"></ion-icon> <ion-icon slot="icon-only" name="contact"></ion-icon>
</ion-button> </ion-button>
</ion-buttons> </ion-buttons>

View File

@ -344,7 +344,7 @@ export class Searchbar {
<ion-button <ion-button
onClick={this.cancelSearchbar.bind(this)} onClick={this.cancelSearchbar.bind(this)}
onMouseDown={this.cancelSearchbar.bind(this)} onMouseDown={this.cancelSearchbar.bind(this)}
clear fill="clear"
color='dark' color='dark'
class='searchbar-md-cancel'> class='searchbar-md-cancel'>
<ion-icon name='md-arrow-back'></ion-icon> <ion-icon name='md-arrow-back'></ion-icon>
@ -362,7 +362,7 @@ export class Searchbar {
autoCorrect={this.autocorrect} autoCorrect={this.autocorrect}
spellCheck={this.spellcheck}/> spellCheck={this.spellcheck}/>
<ion-button <ion-button
clear fill="clear"
class='searchbar-clear-icon' class='searchbar-clear-icon'
onClick={this.clearInput.bind(this)} onClick={this.clearInput.bind(this)}
onMouseDown={this.clearInput.bind(this)}> onMouseDown={this.clearInput.bind(this)}>
@ -370,7 +370,7 @@ export class Searchbar {
</div>, </div>,
<ion-button <ion-button
tabindex={this.activated ? 1 : -1} tabindex={this.activated ? 1 : -1}
clear fill="clear"
onClick={this.cancelSearchbar.bind(this)} onClick={this.cancelSearchbar.bind(this)}
onMouseDown={this.cancelSearchbar.bind(this)} onMouseDown={this.cancelSearchbar.bind(this)}
class='searchbar-ios-cancel'> class='searchbar-ios-cancel'>

View File

@ -197,7 +197,7 @@ export class Toast {
? <div class='toast-message'>{this.message}</div> ? <div class='toast-message'>{this.message}</div>
: null} : null}
{this.showCloseButton {this.showCloseButton
? <ion-button clear color='light' class='toast-button' onClick={() => this.dismiss()}> ? <ion-button fill="clear" color='light' class='toast-button' onClick={() => this.dismiss()}>
{this.closeButtonText || 'Close'} {this.closeButtonText || 'Close'}
</ion-button> </ion-button>
: null} : null}

View File

@ -26,11 +26,11 @@ export function createThemedClasses(mode: string, color: string, classes: string
/** /**
* Get the classes from a class list and return them as an object * Get the classes from a class list and return them as an object
*/ */
export function getElementClassObject(classList: DOMTokenList): CssClassMap { export function getElementClassObject(classList: DOMTokenList | string[]): CssClassMap {
let classObj: CssClassMap = {}; let classObj: CssClassMap = {};
for (var i = 0; i < classList.length; i++) { for (var i = 0; i < classList.length; i++) {
classObj[classList.item(i)] = true; classObj[classList[i]] = true;
} }
return classObj; return classObj;