Semantic-UI的React实现(二):CSS类构造模块

更简单的类名标签

Semantic-UI使用了更简单的类名声明。用过Bootstrap的同学都会被其复杂的类名标签折磨过,例如一个简单的按键样式,不论颜色或是大小,都需要btn-前缀声明:

<button type="button" class="btn btn-primary btn-lg active">Primary button</button> 

在Semantic-UI中,类名更接近自然表述:

<button class="ui blue large active button">Blue Button</button> 

语义化的样式声明

样式名并不是对某种组件进行的类型声明,可以通用到其他组件中。例如对于Label(标签)组件,也可用与button相同的CSS类声明其样式:

<div class="ui blue large active label">Blue Label</div> 

这样的好处是显而易见的,CSS类名语义化,刚方便使用和学习。

类名构造模块的实现

从以上细节可以看出,每个组件的类声明均可由公用模块生成,每个组件仅仅声明本模块可使用的Props即可。以Button举例如下:

import PropHelper from './PropHelper'; import UiElement from './UiElement'; ...  let PROP_TYPES = ['primary', 'size', 'color', 'basic', 'active', ...];  class Button extends UiElement {          static propTypes = {         ...PropHelper.createPropTypes(PROP_TYPES)     };          render() {                  let style = this.createElementStyle(this.props, PROP_TYPES, 'button');                  return (             <div id={this.props.id} className={style} tabIndex='0'>                 {this.props.children}             </div>         );     }          ... }  

Button类声明了其可以使用的class类名,通过共通处理生成style即可。生成style的共同处理,由PropsHelper类负责完成。

PropsHelper

PropsHelper类主要的职责有两个:

生成各组件所需的class类集合 生成各组件的props属性检查定义

PropsHelper作为工具类,相关处理过程中并无状态参与,方法应该声明为静态方法(static)。

props属性检查

Semantci-UI中的所有class类属性的集合是可枚举的,这些属性直接在PropsHelper中定义即可:

const BOOL_PROPS = ['ui', 'active', 'disabled', 'circular', ...]; const STRING_PROPS = ['icon', 'appendClass', ...], const NUMBER_PROPS = ['column', 'wide', ...], const COLLECTION_PROPS = ['color', 'size', 'position', ...];  

对于每个组件的属性检查定义,可以遍历传入的属性,并根据名字找到该属性的PropTypes定义。

class PropsHelper {          ...          /**      * 生成属性检查      */     static createPropTypes(propTypes) {                  let result = {};         propTypes.forEach(function(typeName, index) {                  if (BOOL_PROPS.indexOf(typeName) >= 0) {               result[typeName] = React.PropTypes.bool;             }             else if (STRING_PROPS.indexOf(typeName) >= 0) {               result[typeName] = React.PropTypes.string;             }             else if (NUMBER_PROPS.indexOf(typeName) >= 0) {               result[typeName] = React.PropTypes.number;             }             else if (COLLECTION_PROPS.indexOf(typeName) >= 0) {               result[typeName] = React.PropTypes.oneOf(PROPS_VALUES[typeName]);             }         });                  return result;     } }  

class类集合组装

与createPropTypes同样的思路,将传入的组件props遍历一遍,找到各自prop属性的类型定义,根据类型定义编辑和组装该组件的class类集合。

class PropsHelper {      ...          /**      * 根据属性生成引用的class      */     static createStyle(props, types) {      let style = '';     for (let i = 0; i < types.length; i++) {       let type = types[i];       if (props.hasOwnProperty(type)) {         style += this.formatStyleValue(props[type], type);       }     }      return style;   }      /**    * 格式化属性对应的class    */   static formatStyleValue(value, type) {      // 如果是数字型属性     if (NUMBER_PROPS.indexOf(type) >= 0) {       return ' ' + this.getNumberStr(value) + ' ' + type;     }     else if (COLLECTION_PROPS.indexOf(type) >= 0) {       if (type == 'size') return ' ' + value;       return ' ' + value + ' ' + type;     }     else if (BOOL_PROPS.indexOf(type) >= 0) {        if (!value) return '';        if (type == 'imaged') return ' image';       if (type == 'iconed') return ' icon';       if (type == 'long') return ' long scrolling';       if (type == 'equalWidth') return '';       return ' ' + type;     }     else if (STRING_PROPS.indexOf(type) >= 0) {       return ' ' + value;     }     else {       return '';     }   } }  

这样实现以后,各组件在各自属性的定义和class类声明的处理时获得了两方面的益处:

属性统一管理,不用再各自声明 代码复用性和耦合性更佳(特别是在复杂组件的使用中)