set button css by attrs

This commit is contained in:
Adam Bradley
2015-12-06 20:44:56 -06:00
parent 4025a01fe0
commit 3d3eae1e4f
8 changed files with 273 additions and 87 deletions

View File

@@ -1,25 +0,0 @@
@import "../../globals.core";
// Button Sizes
// --------------------------------------------------
$button-large-font-size: 2rem !default;
$button-large-height: 2.8em !default;
$button-large-padding: 1.0em !default;
$button-small-font-size: 1.3rem !default;
$button-small-height: 2.1em !default;
$button-small-padding: 0.9em !default;
.button-large {
padding: 0 $button-large-padding;
min-height: $button-large-height;
font-size: $button-large-font-size;
}
.button-small {
padding: 0 $button-small-padding;
min-height: $button-small-height;
font-size: $button-small-font-size;
}

View File

@@ -1,16 +1,12 @@
import {Directive, ElementRef, Renderer, Attribute} from 'angular2/angular2';
import {Directive, ElementRef, Renderer, Attribute, Optional} from 'angular2/angular2';
import {Config} from '../../config/config';
import {Toolbar} from '../toolbar/toolbar';
/**
* @name Button
* @module ionic
* @property [primary] - sets button color to default primary
* @property [secondary] - sets button color to default secondary
* @property [danger] - sets button color to default danger
* @property [light] - sets button color to default light
* @property [dark] - sets button color to default dark
* @property [outline] - for an unfilled outline button
* @property [clear] - for a transparent button that only shows text and icons
* @property [round] - for a button with rounded corners
@@ -36,10 +32,14 @@ export class Button {
constructor(
config: Config,
elementRef: ElementRef,
renderer: Renderer,
@Attribute('type') type: string
private elementRef: ElementRef,
private renderer: Renderer
) {
this._role = 'button'; // bar-button
this._size = null; // large
this._style = null; // outline
this._display = null; // block
this._colors = []; // primary
let element = elementRef.nativeElement;
@@ -49,16 +49,11 @@ export class Button {
if (element.hasAttribute('ion-item')) {
// no need to put on these classes for an ion-item
this._role = null;
return;
}
renderer.setElementClass(elementRef, 'button', true);
this.attrToClass(renderer, elementRef);
if (type) {
renderer.setElementAttribute(elementRef, type, '');
}
this._readAttrs(element);
// figure out if and where the icon lives in the button
let childNodes = element.childNodes;
@@ -98,47 +93,65 @@ export class Button {
}
attrToClass(renderer, elementRef) {
// looping through native dom attributes, eww
// https://github.com/angular/angular/issues/1818
afterContentInit() {
this._assignCss(true);
}
let element = elementRef.nativeElement;
let attrs = element.attributes;
let btnAttrs = [];
BUTTON_SIZE_ATTRS.forEach(buttonAttr => {
if (element.hasAttribute(buttonAttr)) {
renderer.setElementClass(elementRef, 'button-' + buttonAttr, true);
}
});
BUTTON_TYPE_ATTRS.forEach(buttonAttr => {
if (element.hasAttribute(buttonAttr)) {
renderer.setElementClass(elementRef, 'button-' + buttonAttr, true);
btnAttrs.push(buttonAttr);
}
});
setRole(val) {
this._role = val;
}
_readAttrs(element) {
let elementAttrs = element.attributes;
let attrName;
for (let i = 0, l = attrs.length; i < l; i++) {
attrName = attrs[i].name;
if (attrs[i].value === '' && COMMON_HTML_ATTR.test(attrName) !== true && BUTTON_TYPE_ATTRS.indexOf(attrName) < 0 && BUTTON_SIZE_ATTRS.indexOf(attrName) < 0) {
if (btnAttrs.length) {
btnAttrs.forEach(buttonAttr => {
renderer.setElementClass(elementRef, 'button-' + buttonAttr + '-' + attrName, true);
});
} else {
renderer.setElementClass(elementRef, 'button-' + attrName, true);
}
for (let i = 0, l = elementAttrs.length; i < l; i++) {
if (elementAttrs[i].value !== '') continue;
attrName = elementAttrs[i].name;
if (BUTTON_STYLE_ATTRS.indexOf(attrName) > -1) {
this._style = attrName;
} else if (BUTTON_DISPLAY_ATTRS.indexOf(attrName) > -1) {
this._display = attrName;
} else if (BUTTON_SIZE_ATTRS.indexOf(attrName) > -1) {
this._size = attrName;
} else if (!(IGNORE_ATTRS.test(attrName))) {
this._colors.push(attrName);
}
}
}
_assignCss(assignCssClass) {
let setElementClass = this.renderer.setElementClass;
let elementRef = this.elementRef;
let role = this._role;
if (role) {
setElementClass(elementRef, role, assignCssClass); // button
if (this._style) setElementClass(elementRef, role + '-' + this._style, assignCssClass); // button-clear
if (this._display) setElementClass(elementRef, role + '-' + this._display, assignCssClass); // button-full
if (this._size) setElementClass(elementRef, role + '-' + this._size, assignCssClass); // button-small
this._colors.forEach(color => {
setElementClass(elementRef, role + '-' + color, assignCssClass); // button-secondary
});
}
}
static setRoles(contentButtonChildren, role) {
let buttons = contentButtonChildren.toArray();
buttons.forEach(button => {
button.setRole(role);
});
}
}
const COMMON_HTML_ATTR = /id|class|type|href|ng-/;
const BUTTON_TYPE_ATTRS = ['block', 'full', 'round', 'clear', 'outline'];
const BUTTON_SIZE_ATTRS = ['large', 'small'];
const BUTTON_STYLE_ATTRS = ['round', 'clear', 'outline', 'fab'];
const BUTTON_DISPLAY_ATTRS = ['block', 'full'];
const IGNORE_ATTRS = /_ng/;
const TEXT = 1;
const ICON = 2;

View File

@@ -14,6 +14,13 @@ $button-ios-text-color: inverse($button-ios-color) !default;
$button-ios-hover-opacity: 0.8 !default;
$button-ios-border-radius: 4px !default;
$button-ios-large-font-size: 2rem !default;
$button-ios-large-height: 2.8em !default;
$button-ios-large-padding: 1.0em !default;
$button-ios-small-font-size: 1.3rem !default;
$button-ios-small-height: 2.1em !default;
$button-ios-small-padding: 0.9em !default;
// iOS Default Button
// --------------------------------------------------
@@ -62,6 +69,22 @@ $button-ios-border-radius: 4px !default;
}
// iOS Button Sizes
// --------------------------------------------------
.button-large {
padding: 0 $button-ios-large-padding;
min-height: $button-ios-large-height;
font-size: $button-ios-large-font-size;
}
.button-small {
padding: 0 $button-ios-small-padding;
min-height: $button-ios-small-height;
font-size: $button-ios-small-font-size;
}
// iOS Block Button
// --------------------------------------------------
@@ -160,4 +183,3 @@ $button-ios-border-radius: 4px !default;
@import "../button-fab";
@import "../button-icon";
@import "../button-size";

View File

@@ -26,6 +26,13 @@ $button-md-color-activated: color-shade($button-md-color) !defaul
$button-md-text-color: inverse($button-md-color) !default;
$button-md-hover-opacity: 0.8 !default;
$button-md-large-font-size: 2rem !default;
$button-md-large-height: 2.8em !default;
$button-md-large-padding: 1.0em !default;
$button-md-small-font-size: 1.3rem !default;
$button-md-small-height: 2.1em !default;
$button-md-small-padding: 0.9em !default;
// Material Design Default Button
// --------------------------------------------------
@@ -87,15 +94,15 @@ $button-md-hover-opacity: 0.8 !default;
// --------------------------------------------------
.button-large {
padding: 0 $button-large-padding;
min-height: $button-large-height;
font-size: $button-large-font-size;
padding: 0 $button-md-large-padding;
min-height: $button-md-large-height;
font-size: $button-md-large-font-size;
}
.button-small {
padding: 0 $button-small-padding;
min-height: $button-small-height;
font-size: $button-small-font-size;
padding: 0 $button-md-small-padding;
min-height: $button-md-small-height;
font-size: $button-md-small-font-size;
}
@@ -254,4 +261,3 @@ $button-md-hover-opacity: 0.8 !default;
@import "../button-fab";
@import "../button-icon";
@import "../button-size";

View File

@@ -0,0 +1,160 @@
import {Button, Config} from 'ionic/ionic';
export function run() {
function mockButton(attrs, config) {
config = config || new Config();
let elementRef = {
nativeElement: document.createElement('button')
};
if (attrs) {
for (let i = 0; i < attrs.length; i++) {
elementRef.nativeElement.setAttribute(attrs[i], '');
}
}
let renderer = {
setElementClass: function(elementRef, className, shouldAdd) {
elementRef.nativeElement.classList[shouldAdd ? 'add' : 'remove'](className);
}
};
return new Button(config, elementRef, renderer);
}
function hasClass(button, className) {
return button.elementRef.nativeElement.classList.contains(className);
}
it('should set a different button role', () => {
let b = mockButton(['outline', 'small', 'full', 'primary']);
b.setRole('bar-button')
b._assignCss(true);
expect(hasClass(b, 'bar-button-outline')).toEqual(true);
expect(hasClass(b, 'bar-button-small')).toEqual(true);
expect(hasClass(b, 'bar-button-full')).toEqual(true);
expect(hasClass(b, 'bar-button-primary')).toEqual(true);
expect(hasClass(b, 'button-outline')).toEqual(false);
expect(hasClass(b, 'button-small')).toEqual(false);
expect(hasClass(b, 'button-full')).toEqual(false);
expect(hasClass(b, 'button-primary')).toEqual(false);
});
it('should remove button color attributes and add different role', () => {
let b = mockButton(['outline', 'small', 'full', 'primary']);
b._assignCss(true);
expect(hasClass(b, 'button-outline')).toEqual(true);
expect(hasClass(b, 'button-small')).toEqual(true);
expect(hasClass(b, 'button-full')).toEqual(true);
expect(hasClass(b, 'button-primary')).toEqual(true);
b._assignCss(false);
expect(hasClass(b, 'button-outline')).toEqual(false);
expect(hasClass(b, 'button-small')).toEqual(false);
expect(hasClass(b, 'button-full')).toEqual(false);
expect(hasClass(b, 'button-primary')).toEqual(false);
});
it('should read button color attributes', () => {
let b = mockButton(['primary']);
b._assignCss(true);
expect(hasClass(b, 'button-primary')).toEqual(true);
b = mockButton(['primary', 'secondary']);
b._assignCss(true);
expect(hasClass(b, 'button-primary')).toEqual(true);
expect(hasClass(b, 'button-secondary')).toEqual(true);
b = mockButton(['outline', 'small', 'full', 'primary']);
b._assignCss(true);
expect(hasClass(b, 'button-outline')).toEqual(true);
expect(hasClass(b, 'button-small')).toEqual(true);
expect(hasClass(b, 'button-full')).toEqual(true);
expect(hasClass(b, 'button-primary')).toEqual(true);
});
it('should read button style attributes', () => {
let b = mockButton(['round']);
b._assignCss(true);
expect(hasClass(b, 'button-round')).toEqual(true);
b = mockButton(['clear']);
b._assignCss(true);
expect(hasClass(b, 'button-clear')).toEqual(true);
b = mockButton(['outline']);
b._assignCss(true);
expect(hasClass(b, 'button-outline')).toEqual(true);
b = mockButton(['fab']);
b._assignCss(true);
expect(hasClass(b, 'button-fab')).toEqual(true);
b = mockButton(['clear', 'outline', 'small', 'full']);
b._assignCss(true);
expect(hasClass(b, 'button-clear')).toEqual(false);
expect(hasClass(b, 'button-outline')).toEqual(true);
expect(hasClass(b, 'button-small')).toEqual(true);
expect(hasClass(b, 'button-full')).toEqual(true);
});
it('should read button display attributes', () => {
let b = mockButton(['block']);
b._assignCss(true);
expect(hasClass(b, 'button-block')).toEqual(true);
b = mockButton(['full']);
b._assignCss(true);
expect(hasClass(b, 'button-full')).toEqual(true);
b = mockButton(['block', 'full']);
b._assignCss(true);
expect(hasClass(b, 'button-block')).toEqual(false);
expect(hasClass(b, 'button-full')).toEqual(true);
});
it('should read button size attributes', () => {
let b = mockButton(['small']);
b._assignCss(true);
expect(hasClass(b, 'button-small')).toEqual(true);
b = mockButton(['large']);
b._assignCss(true);
expect(hasClass(b, 'button-large')).toEqual(true);
b = mockButton(['large', 'small']);
b._assignCss(true);
expect(hasClass(b, 'button-large')).toEqual(false);
expect(hasClass(b, 'button-small')).toEqual(true);
});
it('should not add button css class for ion-item attribute', () => {
let b = mockButton(['ion-item']);
b._assignCss(true);
expect(hasClass(b, 'button')).toEqual(false);
});
it('should add button css class', () => {
let b = mockButton();
b._assignCss(true);
expect(hasClass(b, 'button')).toEqual(true);
});
it('should add disable-hover css class', () => {
let config = new Config({
hoverCSS: false
});
let b = mockButton(null, config);
expect(hasClass(b, 'disable-hover')).toEqual(true);
});
it('should set defaults', () => {
let b = mockButton();
expect(b._role).toEqual('button');
expect(b._size).toEqual(null);
expect(b._colors.length).toEqual(0);
expect(b._style).toEqual(null);
expect(b._display).toEqual(null);
});
}

View File

@@ -1,9 +1,10 @@
import {Component, Directive, Host, ElementRef, Optional, forwardRef, Inject} from 'angular2/angular2';
import {Component, Directive, Host, ElementRef, Optional, forwardRef, Inject, ContentChildren, ContentChild, QueryList} from 'angular2/angular2';
import {Ion} from '../ion';
import {Config} from '../../config/config';
import {MenuToggle} from '../menu/menu-toggle';
import {Navbar} from '../navbar/navbar';
import {Button} from '../button/button';
/**
@@ -63,8 +64,8 @@ export class ToolbarBase extends Ion {
/**
* @name Toolbar
* @description
* The toolbar is generic bar that sits above content.
* Unlike an `ionNavbar`, `ionToolbar` can be used for a subheader as well.
* The toolbar is generic bar that sits above or below content.
* Unlike an `Navbar`, `Toolbar` can be used for a subheader as well.
* @usage
* ```html
* <ion-toolbar>
@@ -89,6 +90,7 @@ export class ToolbarBase extends Ion {
}
})
export class Toolbar extends ToolbarBase {
constructor(
elementRef: ElementRef,
config: Config
@@ -101,14 +103,14 @@ export class Toolbar extends ToolbarBase {
/**
* @name ToolbarTitle
* @description
* `ion-title` is a component that sets the title of the `ionToolbar` or `ionNavbar`
* `ion-title` is a component that sets the title of the `Toolbar` or `Navbar`
* @usage
* ```html
* <ion-navbar *navbar>
* <ion-title>Tab 1</ion-title>
* </ion-navbar>
*
*<!-- or if you wanted to crate a subheader title-->
*<!-- or if you wanted to create a subheader title-->
* <ion-navbar *navbar>
* <ion-title>Tab 1</ion-title>
* </ion-navbar>
@@ -159,4 +161,9 @@ export class ToolbarItem extends Ion {
toolbar && toolbar.addItemRef(elementRef);
navbar && navbar.addItemRef(elementRef);
}
@ContentChildren(Button)
set _buttons(buttons) {
Button.setRoles(buttons, 'bar-button');
}
}

View File

@@ -301,7 +301,10 @@ export class Platform {
* @returns {boolean} TODO
*/
testNavigatorPlatform(navigatorPlatformExpression) {
return navigatorPlatformExpression.test(this._bPlt || '');
if (navigatorPlatformExpression && this._bPlt) {
let rgx = new RegExp(navigatorPlatformExpression, 'i');
return rgx.test(this._bPlt);
}
}
/**

View File

@@ -52,7 +52,7 @@ Platform.register({
// however, under-powered devices shouldn't use ripple
// if this a linux device, and is using Android Chrome v36 (Android 5.0)
// or above then use ripple, otherwise do not use a ripple effect
if (p.testNavigatorPlatform('/linux/i')) {
if (p.testNavigatorPlatform(/linux/i)) {
let chromeVersion = p.matchUserAgentVersion(/Chrome\/(\d+).(\d+)?/);
if (chromeVersion) {
// linux android device using modern android chrome browser gets ripple