Git Product home page Git Product logo

esui's Introduction

Enterprise Simple UI library

文档

cd {esui}
sudo gem install jsduck
jsduck —-config=jsduck/config.json
open doc/api/index.html

esui's People

Contributors

akio0o avatar badplum avatar bjlxj2008 avatar bobshen avatar chestnutchen avatar chriswong avatar curarchy avatar dddbear avatar erik168 avatar firede avatar justineo avatar kitemao avatar leeight avatar leowang721 avatar osdream avatar otakustay avatar southwhale avatar srhb18 avatar strwind avatar uraincay avatar wurongyao avatar yanghuabei avatar yankun01 avatar zfkun avatar zxhfighter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

esui's Issues

HTML的data-ui属性中值的trim问题

以前有讨论过,我的意见是data-ui属性中,类似style属性写法的值,key和value都应该去掉前面的空格。

因为很多情况下会这么写:

<div data-ui="type: Panel; id: panel;"></div>

在冒号和分号后会习惯性地加一个空格,这也使得代码更清晰易读,因此应该在解析的时候去掉这空格。

对于 原本就故意想加空格 这样的需求,是否可以要求其车用data-ui-*属性完成?

[评审]UI标准 - Dialog

ECOM UI组件标准 - Dialog

继承层级

Dialog
    - Control

功能描述

对话框控件包括三个部分:标题、主体、腿部。

对话框控件内置两种模式:alert、confirm。可通过静态方法调用。

prototype.type

"Dialog"

控件主元素

控件主元素必须为div。

使用html搭建dialog时支持将title、content、foot内容预写入主元素下。并使用data-role属性指定节点角色。如:

    <div data-ui="type:Dialog;width:400;mask:true;id:staticDg">
        <h1 data-role="title">我来自静态html</h1>
        <div data-role="content">
          <p>第一行</p>
          <span data-ui="type:Button;id:springBtn;skin:spring">显示文字</span>
        </div>
    </div>

属性

{boolean=} autoPosition

是否自动定位居中。默认值为false。

{boolean} closeButton

是否具有关闭按钮。默认值为true。

{string} content

内容区域的显示内容。默认为主元素内容区域的内容。支持html。

{boolean} mask

可以通过boolean指定对话框是否具有遮挡层。

{string} title

标题的显示文字。支持html。

{number} width

对话框的宽度,单位为px。默认值为600。

{number} height

对话框的高度,单位为px。默认值为175。

{boolean} needFoot

对话框是否需要足部内容。有些对话框不需要使用foot来装载负责确认或取消操作的按钮,此时可以将此值置为false。

{string} foot

对话框的足部显示内容。默认值包含两个按钮:确定和取消。可以自定义符合自己要求的内容。

构造器静态方法

{void} alert ( {Object} args )

显示警告对话框。

args参数各项成员:

  • {string} title: 标题文字
  • {string} type: 对话框类型。其决定了显示的icon。
  • {string} content: 主体内容。
  • {number} width: 弹出框宽度。
  • {Function} onok: 点击确定按钮的行为。若返回值不为false则关闭对话框。

{string} confirm ( {Object} args )

显示询问对话框。

args参数各项成员:

  • {string} title: 标题文字
  • {string} type: 对话框类型。其决定了显示的icon。
  • {string} content: 主体内容。
  • {number} width: 弹出框宽度。
  • {Function} onok: 点击确定按钮的行为。若返回值不为false则关闭对话框。
  • {Function} oncancel: 点击取消按钮的行为。若返回值不为false则关闭对话框。

公共实例方法

{ui.Panel} getBody()

获取对话框主体的控件对象。

{ui.Panel} getFoot()

获取对话框腿部的控件对象。

{ui.Panel} getHead()

获取对话框头部的控件对象。

{void} show()

显示对话框。

{void} hide()

隐藏对话框。

{void} setTitle( {string} title )

设置对话框的标题。

参数:

  • {string} title: 对话框主体的标题,支持html

{void} setContent( {string} content )

设置对话框主体的内容。

参数:

  • {string} content: 对话框主体的内容,支持html

{void} setFoot( {string} content )

设置腿部内容。

参数:

  • {string} content: 对话框腿部的内容,支持html

{void} setWidth( {number} width )

设置对话框的宽度,单位为px。

参数:

  • {number} width: 对话框的宽度

{void} setHeight( {number} height )

设置对话框的高度,单位为px。

参数:

  • {number} height: 对话框的高度

事件

show

当对话框被显示时触发。

事件对象成员:

  • {string}type: 事件名

hide

当对话框被隐藏时触发。

事件对象成员:

  • {string}type: 事件名

[评审]UI标准 - Calendar

ECOM UI组件标准 - Calendar

继承层级

Calendar
    - InputControl
        - Control

功能描述

日历控件包括一个日期显示框和一个弹出层形式的单月日历。

prototype.type

"Calendar"

控件主元素

控件主元素必须为div。

数据格式说明

选中的日期,为Date类型的Javascript对象。

允许选择日期区间“range”的数据格式,以一个Object表示。该Object拥有name为begin和end的成员,分别为Date类型。

{
    begin: new Date(1983, 8, 3),
    end: new Date(2011, 10, 4)
}

属性

{string} dateFormat

日期显示的格式化方式。默认'yyyy-MM-dd'。

{string} paramDateFormat

日期参数的格式化方式。默认'yyyy-MM-dd'。

{Object} range

可选中的日期区间。可选,默认

{
    begin: new Date(1983, 8, 3),
    end: new Date(2046, 10, 4)
}

{Date} rawValue

当前选中的日期的原始数据。

公共实例方法

{Date} getRawValue()

获取当前选中的日期。

{void} setRange( {Object} range )

设置允许选中的日期区间。

参数:

  • {Object|string} range: 允许选中的日期区间

支持字符串型,‘,’分割起始时间和结束时间。如:1983-09-03,2046-11-04。

{void} setRawValue( {Date} value )

通过日期格式,设置当前选中的日期。

参数:

  • {Date} value: 当前选中的日期

事件

change

当选择的日期发生改变时触发。

事件对象成员:

  • {string}type: 事件名
  • {Date}rawValue: 选择的值,日期格式

[评审]UI标准 - MonthView

ECOM UI组件标准 - MonthView

继承层级

MonthView 
    - Control

功能描述

单月日历控件包括日期选择、月份选择、年份选择、单月浏览。

prototype.type

"MonthView"

控件主元素

控件主元素必须为div。

数据格式说明

选中的日期,为Date类型的Javascript对象。

允许选择日期区间“range”的数据格式,以一个Object表示。该Object拥有name为begin和end的成员,分别为Date类型。

{
    begin: new Date(1983, 8, 3),
    end: new Date(2046, 10, 4)
}

使用data-ui定义range时,支持字符串型,‘,’分割起始时间和结束时间。如:1983-09-03,2046-11-04。

属性

{string} dateFormat

日期显示的格式化方式。默认'yyyy-MM-dd'。

{Object} range

可选中的日期区间。可选,默认

{
    begin: new Date(1983, 8, 3),
    end: new Date(2046, 10, 4)
}

{Date} rawValue

当前选中的日期的原始数据。

如果rawValue超出range范围,则该日期有可能不会展示在日历上。

公共实例方法

{Date} getRawValue()

获取当前选中的日期。

{void} setRange( {Object} range )

设置允许选中的日期区间。

参数:

  • {Object} range: 允许选中的日期区间

{void} setRawValue( {Date} value )

通过日期格式,设置当前选中的日期。

参数:

  • {Date} value: 当前选中的日期

事件

change

当选择的日期发生改变时触发。

事件对象成员:

  • {string}type: 事件名

changeYear

当选择的年份发生改变时触发。

事件对象成员:

  • {string}type: 事件名

changeMonth

当选择的月份发生改变时触发。

事件对象成员:

  • {string}type: 事件名

关于控件的生成

在从HTML生成控件main.init时,建议在生成控件后,为元素加上一个data-ui-id属性,这个属性的值就是控件的id值。

分为以下3个情况:

  • 如果本身就有这个属性,则控件的id一定是这个属性的值,因此再写回去没问题
  • 如果本身有data-ui属性,里面有id的值,则再写回去也没问题
  • 如果没声明id,则不会有data-ui-id这个属性,控件自动生成一个id,后加上这个属性也没问题

因此认为这么做没有副作用。

这么做的好处是:

  • 调试的时候可以迅速从DOM树看到对应的控件的id,从而用ViewContext.prototype.get去拿到并调试
  • getControlFromElement(虽然不希望这方法存在)可以更方便地找到对应的控件

@errorrik 评估一下,OK的话我去加上

增加一个DOM事件模块

在控件的开发中,免不了要往main元素上加事件,也免不了要在dispose()的时候把这些事件去掉。

但是这里存在几个问题:

  1. 往往注册上去的事件执行时,我们希望在事件的处理函数内部可以访问到控件的实例(如通过this或者me),这就必然导致事件的处理函数是一个在闭包内的函数(为了固定this或能引用me),也进一步导致,在取消这些事件的时候,容易找不回那个处理函数(因为在闭包里)不好取消。
  2. 在控件的整个生命周期和交互过程中,会不限量地注册各种事件,要在dispose()的时候一一去掉,就要记住注册了哪些事件,用哪些处理函数,这些逻辑全部用代码来写而不抽象出来早晚会混乱。

当然一个非常简单的方法,就是参考现在的controlHelper模块,用onxxx属性来挂事件,到时候只要onxxx = null;就能去掉,但这不仅仅受到 一个事件只能注册一个函数 的限制,同时也只解决了问题1,没解决问题2。看看controlHelper中的initMouseBehaviordispose中的处理,并不是十分潇洒漂亮的形式,这也仅仅是限定地处理了鼠标相关的,有更多事件的话会更难管理。

因此,建议在控件上(事实上是controlHelper模块)中添加一个专门负责main对象上的事件处理,通过addDOMEventremoveDOMEventclearDOMEvents等方法来管理DOM事件,保证注册上就能去掉,而且在dispose()的时候可以去干净。

如果大家认为确实可以有这样一个模块的话,我会负责实现之。

完善TextBox

  1. createMain接受参数options,从参数中拿mode等值
  2. 去掉render方法,使用initStructure来注册focusblur等事件和其它逻辑
  3. 去掉setRawValue,交由repaint处理
  4. textarea模式下不应该把enter事件丢出去
  5. 事件注册统一用helper.addDOMEvent,这样inputHandler也不用挂上面了,dispose方法可以去掉

我们还是谁写的谁负责的原则,请 @errorrik 辛苦一下

[评审]UI标准 - Crumb

Crumb控件标准

Crumb控件用作面包屑导航,用于展现一系列有递进层次的链接。

继承关系

- Control
    - Crumb

主元素

Crumb控件的主元素可以是<div>或任何流式元素,默认使用<nav>

属性

{Array.} path

指定导航的路径,数组中的每一项称为 节点数据项 ,一个节点数据项可以包含以下属性:

  • {string} text:显示的文字
  • {string=} href:跳转的链接地址,可以没有该属性,如果没有该属性,则对应的节点是一个文本而非链接。

{string} separator

指定每两个节点之间的分割符,可以是一段HTML,控件不负责对其进行HTML转义。默认值为&gt;

扩展点

{string} textNodeTemplate

用来指定控件中文本节点生成时的HTML模板,其中占位符包含:

  • ${text}:显示的文本。

占位符的替换均在经过HTML编码后进行。

{string} linkNodeTemplate

用来指定控件中链接节点生成时的HTML模板,其中占位符包含:

  • ${text}:显示的文本。
  • ${href}:链接地址。

占位符的替换均在经过HTML编码后进行。

{function(Object): string} getNodeHTML

该方法用于生成控件中每一项的HTML串,默认实现是基于textNodeTemplatelinkNodeTemplate属性的字符串格式化操作。

重写该方法后,textNodeTemplatelinkNodeTemplate的功能将失效。

控件DOM结构

控件的标准DOM结构如下:

<nav class="ui-crumb">
    <ol>
    <!-- for ${path} as ${node} -->
        <li class="ui-crumb-node">
        <!-- if ${node.href} -->
            <a href="${item.href}">${item.text}</a>
        <!-- /if -->
        <!-- else -->
            <span>${item.text}</span>
        <!-- /else -->
        </li>
        <li class="ui-crumb-separator">${separator}</li>
    <!-- /for -->
    </ol>
</nav>

与状态相关的class如下:

  • ui-crumb-node-first:第一个节点。
  • ui-crumb-node-last:最后一个节点。

Tip控件未完成部分

参考:http://fe.baidu.com/doc/ecom/std/ui/v1.1/tip.text

  1. 增加hideDelay属性,控制消失的延迟时间,注意计时器不要混乱
  2. 增加mode属性,控制交互的事件
  3. 增加showhide事件

另,建议增加:

  1. 增加showDelay属性,当mode值为over时生效,不要鼠标路过就显示出来,默认值为100ms
  2. 增加自动摆放模式,当arrow为1或true等非字符串值,或者无arrow时,自动根据空间判断放置的位置和箭头(可二期再做)
  3. 增加从已有的DOM中抽取titlecontent属性,避免在js中还要写HTML或者需要在模板中分离对应的内容的尴尬(可二期再做)

@badplum 关注,尽快实现

[评审]UI标准 - Table

Table控件标准

Table控件形如HTML中的<table>元素,主要用于二维数据的展现。

继承关系

- Control
    - Table

主元素

Table控件的主元素是一个<div>元素。

属性

{int} width

表格体的宽度,单位为px。默认自适应内容宽度。
如果设置该值,则将按照该宽度绘制Table,而不再根据父容器大小自适应。

{int} bodyMaxHeight

表格体的最大高度,单位为px。

{boolean} breakLine

表格内容是否允许断行。默认false。

{Array.} datasource

控件的数据源,数据源中的每一个对象称为 数据项,对应到表格每行的显示数据 。

{boolean} disabled

控件的不可用状态。默认false。处于不可用状态时,表格拖拽、排序、行选中等功能都将禁用。

{boolean} columnResizable

表格是否允许拖拽改变列宽。默认true。

{boolean} followHead

在滚动条纵向滚动时,是否表头跟随。默认false。

{string} noDataHtml

没有数据时,表格体中显示的html内容。默认null。

{boolean} noHead

表格是否不显示标题。默认false。

{string} select

设置表格的选择方式。single(单选)|multi(多选)。默认null。

{string} selectMode

设置表格的选择模式。line|box。默认box。如果设置了值为line,则点击行时触发选择。

{boolean} sortable

表格是否允许排序。默认false,

{boolean} subrow

表格是否允许子行。默认false,

{boolean} subrowMutex

表格子行是否互斥,也就是打开一子行其他子行是否需自动关闭。默认false,

{Array.} fields

表格的列配置。

初始化的参数中,fields是一个数组,作为表格显示的列设置。其中每一列的可选属性如下:

{string} align

列内容的排序方式。left | center | right。 默认left。

{boolean} breakLine

列是否允许断行。默认应用控件breakLine属性。

{string|Function} content

每一行该列需要显示的内容。为Function时应用返回值。

Function的接口形式为 fn(item, index),item为每行对应数据项,index对应datasource数组序号。

{boolean} resizable

该列是否允许拖拽改变列宽。默认true。

{string} field

该列的字段名。主要用于可排序表格中的可排序字段。

{number} minWidth

该列的最小宽度。表格分配列宽时分配宽度不会小于设置的minWidth。

{boolean} stable

是否固定列。表格分配列宽时不会影响到固定列。默认false。

{boolean} sortable

是否依据该列进行数据排序,默认false。

{string|Function} title

该列标题需要显示的内容。

{string|Function} tip

该列标题需要显示的提示信息,当鼠标移动到表头的tip控件上时显示。

{number} width

设置该列的宽度。未设置stable时,会自动根据其他列设置的width,按比例分配,增减调整宽度。

公共实例方法

{Object} getSubrow(index)

获取表格子行的元素。

参数:

  • {int} index: 行序列值

{void} adjustWidth()

自适应表格宽度。
若之前已设置Table的width属性,该方法将不起效果。

事件

click

当控件主元素被点击时触发。

事件对象成员:

  • {string}type: 事件名

select

  • 当表格项被选中时触发。

事件对象成员:

  • {string}type: 事件名
  • {Array|number}selectedIndex: 单选模式时为选中项的索引,多选模式时为选中项索引的数组。

sort

当用户点击表格排序时触发。

事件对象成员:

  • {string}type: 事件名
  • {Object}field: 当前排序列,对应初始化参数fields设置中的当前排序项
  • {string}order: 排序方式,asc|desc

subrowopen

当用户点击展开子行按钮时触发

事件对象成员:

  • {string}type: 事件名
  • {int}index: 当前行序列,对应datasource中的当前行序列
  • {Object}item: 当前行数据

subrowclose

当用户点击展开子行按钮时触发

事件对象成员:

  • {string}type: 事件名
  • {int}index: 当前行序列,对应datasource中的当前行序列
  • {Object}item: 当前行数据

控件DOM结构

控件主元素的固定结构如下:

<div>
    <div> <!-- head -->
        <table>
            <tr><th>标题</th></tr>
            <tr><th>标题</th></tr>
            ......
        </table>
    </div>
    <div> <!-- body -->
        <div> <!-- rows -->
            <table>
                <tr><td>数据</td></tr>
                <tr><td>数据</td></tr>
                ......
            </table>
        </div>
        <div>
            <table>
                <tr><td>数据</td></tr>
                <tr><td>数据</td></tr>
                ......
            </table>
        </div>
        ......
    </div>
    <div> <!-- foot -->
        <table>
            <tr><th>信息</th></tr>
        </table>
    </div>
</div>

controlHelper需要几个和class相关的methods

  • {Array} getPartClasses( {Control} control, {string=}part )
  • {Array} getStateClasses( {Control} control, {string}state )
  • {void} addPartClasses( {Control} control, {string=} part, {HTMLElement=} element )
  • {void} removePartClasses( {Control} control, {string=} part, {HTMLElement=} element )
  • {void} addStateClasses( {Control} control, {string} state )
  • {void} removeStateClasses( {Control} control, {string} state )

关于控件生命周期各阶段的各方法的可用性

参考以下代码:

var label = new Label();
label.main; // null
label.setText('Hello World');
label.appendTo(document.body);

需要确定几个问题:

  1. appendTo导致生成main元素前,setText能不能调用(调用时是否允许异常)
  2. 如果允许调用setText,则在渲染后,这个Label的文字要不要显示为 Hello World (即是否允许在main出现前的相关属性调用被丢弃)

[评审]UI标准 - Select

Select控件标准

Select控件形如HTML中的<select>元素,是一个下拉列表,可以选择其中一项。

继承关系

- Control
    - InputControl
        - Select

主元素

Select控件的主元素可以是一个<div>或者其它类型的流式元素,也可以是一个<select>元素。

当使用<select>元素作为主元素时,控件会删除该元素,并在相同的位置放置一个<div>元素作为替换。

当满足以下2个条件时:

  • 使用<select>元素作为主元素时
  • 实例化控件时未给定datasource属性

Select控件会从主元素的<option>子元素中提取数据作为自身的datasource属性。

属性

{number} width

指定控件的宽。

{number} height

指定控件的高。

{Array.} datasource

控件的数据源,数据源中的每一个对象称为 数据项 ,一个数据项需包含:

- `{string} text`:该项的文本,与`name`二选一。
- `{string} name`:该项的文本,与`name`二选一。
- `{*} value`:该项的值,通常是字符串类型,否则在显示时将被转为字符串。

如果nametext均存在,则 name为优先

{string} emptyText

该属性指定当控件未选择任何一项时显示的文本。如果此属性不为空,则该文本将在以下情况出现:

  • 实例化控件时没给valuerawValueselectedIndex属性,导致未选中任何项。
  • 实例化或更新时,给定的valuerawValueselectedIndex不在datasource范围内,如给了一个datasource不包含的value,或者一个超出datasource.lengthselectedIndex值。
  • 修改了datasource,导致原来选中的项不在新的datasource中。

需要注意的是,如果修改datasource后,原来选定的rawValue还在新的datasource的范围内,则不会显示emptyText,而是继续选中该项。此时如果需要回到未选任何一项的状态,可以通过select.set('selectedIndex', -1);来完成。

{number} selectedIndex

选中的项在datasource中的下标,可以通过设置该属性来选中指定的项。如果该值小于0或者大于datasource.length,则会根据是否有emptyText属性来决定显示emptyText或者选中第一项。

{string} rawValue

控件的原始值,当有一项被选中时,返回该项的value属性。当未选中任何一项时,返回null

事件

change

当选中项变化时触发。

扩展点

{string} itemTemplate

itemTemplate属性可以用来指定控件中每一项生成时的HTML模板,其中占位符包含:

  • ${text}:显示的文本。
  • ${value}:数据项的值。

占位符的替换均在经过HTML编码后进行。

{function(Object): string} getItemHTML

该方法用于生成控件中每一项的HTML串,默认实现是基于itemTemplate属性的字符串格式化操作。

重写该方法后,itemTemplate的功能将失效。

控件DOM结构

控件主元素的固定结构如下:

<div>
    <span>${text}</span>
    <input name="${name}" type="hidden" value="${value}" />
</div>

Select控件会在document.body下创建一个元素,用于显示下拉层,该元素会在任意地方(除控件自身外)发生mousedown事件时隐藏。

<div class="ui-select-layer">
    <!-- for ${datasource} as ${item} -->
        ${getItemHTML}(${item})
    <!-- /for -->
</div>

默认的getItemHTML返回内容如下:

<span>${text}</span>

TODO

  • 当下拉层打开时,支持使用键盘进行选择。
  • 支持optgroup形式的分组内容。

onchange问题

select calendar schedule 等都有onchange事件。

  • 当setValue、setRawValue的时候需要执行onchange么?
  • onchange是否需要有返回值为false,取消行为的功能?

[评审]UI标准 - Tab

Tab控件标准

Tab控件可以用于标签页的切换。每个标签可以对应一个容器元素,Tab控件自动控制容器的切换。

Tab标签自身会创建一个导航条,包含各个标签的元素。

继承关系

- Control
    - Tab

主元素

`Tab`控件的主元素可以是一个<div>或者其它类型的流式元素,默认使用`<nav>`元素。

导航元素

当给定主元素时,主元素下可以存在一个导航元素,导航元素 必须 是一个<ul><ol>元素,内含若干个<li>元素。

导航元素 必须data-role="navigator"属性,且 必须 是主元素的 直接子元素

一个标准的导航元素结构如下:

<ul data-role="navigator">
    <li data-for="intro">简介</li>
    <li data-for="character">人物介绍</li>
    <li data-for="content">正文</li>
    <li data-for="publish">出版信息</li>
</ul>

当实例化控件时,如果未给定tabs属性或者tabs属性为空数组,且主元素下有导航元素,则按以下规则,从导航元素的子元素中提取出tabs属性:

  • innerText作为title属性。
  • data-for属性的值作为panel属性。

需要注意的是,导航元素的子元素仅起到标记的意义,不要依赖子元素的具体样式。当控件渲染时,会把导航元素中的内容 清空重新生成 符合规范的子元素结构。

面板元素

当实例化控件时,如果同时满足以下所有条件时:

  • 未给定tabs属性或tabs属性为空数组
  • 主元素下没有导航元素
  • 主元素下有其它元素

则会将主元素下的每一个直接子元素作为一个面板,按以下规则生成tabs属性:

  • title属性作为title属性。
  • id属性作为panel属性。

随后控件渲染时,会建立导航元素并作为主元素的 第一个子元素

属性

{Array.} tabs

标签页的配置,数组中的每一项称为 配置项 ,一个配置项的结构如下:

  • {string} title:标签页的显示名称。
  • {string=} panel:对应的元素的id,当一个标签被激活时,该标签配置项的panel属性对应的DOM元素将被显示出来,其它配置项对应的DOM元素则被隐藏。

{boolean} allowClose

是否允许删除标签,默认值为false。当此属性值为true时,会在每个标签页中显示一个关闭的标记,点击后会删除该标签页。

当标签页删除时,其panel属性对应的DOM元素并不会被删除,但会被隐藏。

{number} activeIndex

指定当前激活的标签的下标。当修改此属性时,会触发activate事件。

方法

{void} activate({Object} config)

激活config属性对应的标签页。

参数

  • {Object} config:需激活配置项,使用全等(===)进行判断。

{void} add({Object} config)

在最后添加一个新标签页。

参数

  • {Object} config:添加的标签页的配置项,不去重。

{void} insert({Object} config, {number} index)

在指定位置添加一个新标签页。

参数

  • {Object} config:添加的标签页的配置项,不去重。
  • {number} index:插入的位置。如果小于0则会插在第一个,如果大于现有标签页的数量则插在最后。

{void} remove({Object} config)

移除指定的标签页。

参数

  • {Object} config:需移除的标签页配置项,使用全等(===)进行判断。

{void} removeAt({number} index)

移除指定位置的标签页。

参数

  • {number} index:需移除的位置,如果在现有标签页范围之外则无效果。

事件

activate

一个标签由未激活状态转为激活状态时触发,事件对象的属性如下:

  • {number} activeInex:激活的标签的下标。
  • {Object} tab:激活的标签的配置项。

add

添加一个标签时触发,事件对象的属性如下:

  • {number} activeInex:激活的标签的下标。
  • {Object} tab:激活的标签的配置项。

remove

移除一个标签时触发,事件对象的属性如下:

  • {number} activeInex:激活的标签的下标。
  • {Object} tab:激活的标签的配置项。

扩展点

{string} contentTemplate

contentTemplate属性可以用来指定控件中每个标签页生成时的HTML模板,其中占位符包含:

  • ${title}:显示的文本。

占位符的替换均在经过HTML编码后进行。

{function(Object, boolean): string} getContentHTML

该方法用于生成控件中每一项的HTML串,默认实现是基于contentTemplate属性的字符串格式化操作。且当第2个参数(allowClose)为true时,追回一个<span>标签。

重写该方法后,contentTemplate的功能将失效。

控件DOM结构

默认生成的结构如下:

<nav>
    <ul>
        <!-- for ${tabs} as ${item} -->
        <li>${item.title}</li>
        <!-- /for -->
    </ul>
</nav>

当开启allowClose属性时,每个标签页中的内容变为

<li>
    ${item.title}
    <span class="ui-tab-close">关闭</span>
</li>

一个被激活的标签页会增加ui-tab-active类。

需要增加一个WizardGuide控件

有几个系统有向导型的表单交互,即先填一个页,然后“下一步”填另一些项,再“下一步”继续填写,然后完成

这个控件和Crumb控件有点像,但有一些不同:

  • WizardGuide会把所有的路径全显示出来,并且有一个“当前激活”的
  • WizardGuide的每一项并不是一个链接,且没有点击事件

这个控件和Tab也有点像,但又有一些不同:

  • WizardGuide的各项之间是有顺序的
  • WizardGuide由于通常关联着一个很复杂的大表单,因此本身并不一定是通过多个panel的显示和隐藏来完成逻辑的

配置说明:

  • steps:说明整个向导过程中的步骤,每个step包含以下:
    • text:显示的文字
    • panel:对应的面板,可以没有
  • finishText:有些向导在最后有一个“完成”的字样,点击不起任何作用,仅视觉效果,通过这个字段可配置
  • activeIndex:当前激活的步骤的索引,如果有finishText则可能取到超出steps.length - 1的值
  • activeStep只读 ,当前激活的步骤对象,如果有finishText则可能取到undefined,此时是在最后一步上

事件说明:

  • enter:进入某一步骤时触发,如果有panel属性会控制对应panel的隐藏和显示后再触发

方法说明:

  • stepNext:去下一步,如果已经是第一步则无反应
  • stepPrevious:去上一步,如果已经是最后一步(有finishText的情况下会多出一步)则无反应

[评审]UI标准 - Wizard

Wizard控件标准

Wizard控件用于多步骤的引导式导航。

继承关系

- Control
    - Wizard

主元素

Wizard控件的主元素 只能<ol><ul>元素,推荐(默认)使用<ol>

属性

{Array.} steps

指定步骤,数组中的每一项称为 单步数据项 ,一个单步数据项可以包含以下属性:

  • {string} text:显示的文字
  • {string=} panel:每个步骤可提供一个对应的面板的DOM元素的id,如果有该属性,则当该步骤激活时显示该面板,非激活的步骤对应的页面将被隐藏。

{string=} finishText

指定所有步骤完成时的文字。如果有该属性,则将在所有的步骤之后多出一个节点显示该文字

{number} activeIndex

当前激活的步骤的下标。

方法

{Object} getActiveStep()

获取当前激活的步骤对应的单步数据项。

返回值

当前激活的步骤对应的单步数据项。

{void} stepNext()

进入下一步,会随带触发enter事件。

{void} stepPrevious()

回到上一下,会随带触发enter事件。

事件

enter

在进入某一步时触发。

扩展点

{string} nodeTemplate

nodeTemplate属性可以用来指定控件中每一项生成时的HTML模板,其中占位符包含:

  • ${text}:显示的文本。

占位符的替换均在经过HTML编码后进行。

{function(Object): string} getNodeHTML

该方法用于生成控件中每一项的HTML串,默认实现是基于nodeTemplate属性的字符串格式化操作。

重写该方法后,nodeTemplate的功能将失效。

控件DOM结构

控件的标准DOM结构如下:

<ol class="ui-wizard">
    <!-- for ${steps} as ${step} -->
        <li class="ui-wizard-node">${step.text}</li>
    <!-- /for -->
    <!-- if ${finishText} -->
        <li class="ui-wizard-last ui-wizard-finish">${finishText}</li>
    <!-- /for -->
</ol>

与状态相关的class如下:

  • ui-wizard-node-first:第一个节点。
  • ui-wizard-node-last:最后一个节点,如果有finishText属性,则最后一个节点永远是完成提示文字的节点。
  • ui-wizard-active:当前的节点。
  • ui-wizard-done:已经完成的节点。
  • ui-wizard-active-prev:当前节点的前一个节点。
  • ui-wizard-last-active:既是最后一个节点又是激活的节点。
  • ui-wizard-panel-hidden:由Wizard控件控制的被隐藏的面板。

[评审]UI标准 - Button

ECOM UI组件标准 - Button

继承层级

Button
    - Control

prototype.type

"Button"

控件主元素

任何元素都可做为按钮的控件主元素。默认的控件主元素为button。

属性

{string} content

按钮的显示文字。不进行HTML过滤。

{number} height

按钮的高度。

{number} width

按钮的宽度。

公共实例方法

{void} setContent( {string} content )

设置按钮的显示文字。

参数:

  • {string} content: 按钮的显示文字

{void} setHeight( {number} height )

设置按钮的高度。

参数:

  • {number} height: 按钮的高度

{void} setWidth( {number} width )

设置按钮的高度。

参数:

  • {number} width: 按钮的宽度

事件

click

当按钮被点击时触发。

事件对象成员:

  • {string}type: 事件名

关于InputControl的交互行为

以下两点是我 强烈 要推动的(甚至希望强制之):

  1. 对IE6不作完美兼容(这里是带有强制性地放弃IE6的完美兼容,而不是“如果我做得到,我去做”这样消极的想法),如鼠标悬浮的样式(仅样式,如果有动态交互的行为另谈)直接不支持IE6。这有助于我们减少控件开发者和业务线人员的精力,逐渐推动IE6的淘汰。
  2. 输入控件支持键盘操作,如tab导航等。

因此,我提出以下建议:

  1. 控件是否加hoveractivepressfocus这些state由控件自己决定,仅样式上的变化的话,通过CSS处理,不通过鼠标事件管这事。
  2. 一定要有:hover:active:focus这几个伪类,样式通过这些伪类实现,不去使用这几个state相关的class。通过鼠标事件修改的class通常只能管理鼠标操作的情况,键盘操作不能覆盖,远没有伪类来得正确。
  3. InputControl的主元素必须有tabindex,默认值为0,这个可以在InputControl.prototype.initStructure中实现。
  4. 复杂的输入控件考虑键盘的操作,如Select应该能通过TAB聚焦,然后按方向键下或者回车打开层,使用方向键选择具体的项,按回车确认。

esui开发时的几个约定问题

私有成员

私有成员与方法是下划线_开头,还是仅仅通过注释标识?

常见集合数据类型的命名

状态是一个kv,做为控件的属性,是命名为statestatesstateMap

对Array类型的,应该是+s,这点应该没有疑问。

关于渲染和重渲染的差异

控件第一次渲染称为 渲染 ,已经渲染后因为属性的变化等导致的再次渲染(主要是setProperties这方法)称为 重渲染 ,希望确定几个事:

  1. 渲染和重渲染是否要暴露不同的事件,因为很多时候在afterrender事件上注册main的DOM事件,如果再次重渲染,这个DOM事件不应该再注册一次,因此建议是有区分的
  2. set方法是否默认调用repaint,我认为是不需要的,默认调用repaint的方法应该通过setXxx方法暴露出来。有时因为业务需要,想在控件上挂一点和控件本身没啥关系的数据,ESUI又不希望开发者直接挂属性,所以会用set,这要是set一次repaint()一次,会很糟糕

[评审]UI标准 - Form

Form控件标准

Form控件类同<form>元素,指代一个表单,基到普通的面板提供一些额外的方法。

继承关系

- Control
    - Panel
        - Form

主元素

Form控件的主元素可以是任何流式元素,推荐(默认)使用<form>

属性

{string=} action

指定表单提交的URL,暂无作用。如果主元素是<form>元素,则会设置对应的属性。

{string=} method

指定表单提交时的动作,暂无作用。如果主元素是<form>元素,则会设置对应的属性。

{string=} submitButton

可以用该属性指定一个Button控件的id,当点击该按钮时,表单将触发submit事件。

方法

{InputCollection} getInputControls({string=} name, {string=} type)

在当前Form下查询符合条件的InputControl,并返回一个集合。该方法对InputControl的判断采用以下策略:

  • 控件是InputControl或其子类的实例。
  • 控件的主元素在当前Form的元素下。
  • 没有指定name参数或者控件的name属性与name参数相等。
  • 没有指定type参数或者控件的type属性与type参数相等。
  • 控件与当前的Form使用同一个ViewContext实例。
  • 控件不作为另一个InputControl的子控件存在。

该方法返回一个InputCollection对象,InputCollection是对输入控件的集合的封装,除了正常的for循环外,还支持以下方法:

  • {void} checkAll():全选。
  • {void} uncheckAll():全部取消选择。
  • {void} checkInverse():反选。
  • {void} checkByValue(Array.<string> values):选中给定值的控件。
  • {string} getValueAsString():获取逗号分隔的值的字符串形式。

{Object} getData()

将当前Form下的InputControlrawValue收集起来并返回一个对象。对象的键为控件的name属性,值为rawValue属性。

该方法找InputControl的策略与getInputControls相同。

事件

submit

submitButton指定的按钮被点击时触发。

helper.getGUID不能使用当前时间

很多自动化测试和工具依赖于元素的id的稳定性,如果每次进页面的guid计算是以当前时间为基准的,会导致自动化的代码找不到对应的元素。

需要修改helper.getGUID的实现,使用一个与时间无关的算法,建议设置一个初始值,然后稳定自增

painters模块

概述

在ESUI中新加了painters模块,该模块用于生产实现repaint方法的对象,具体使用大致如下:

var paint = require('./painters');
XxxControl.prototype.repaint = require(./controlHelper').createRepaint(
    paint.style('width'),
    paint.html('content'),
    {
        name: 'personName',
        paint: function (control, value) {
            control.main.innerHTML = 'Hello ' + value;
        }
    }
);

大致描述过程可以总结为:

  1. 引用painters模块
  2. 创建一个数组,其中每一项是一个painter对象
  3. 使用controlHelper.createRepaint,以第2步创建的数组为参数,生成repaint方法的实现

painter对象

一个painter对象描述 使用什么方法渲染哪个属性painter对象是一个普通的javascript对象,但其必须有以下2个属性:

  • {string} name:指定负责的属性名称,一个painter对象只能负责一个属性
  • {function(Control, *)} paint:指定渲染该属性的方法,方法接受 控件的实例 和** 属性的值** 作为参数

内置有以下painter对象的生成函数(自己看源码更容易理解):

  • style({string} name, {string} property):将控件属性的值作为main元素的样式,会自动加上px
  • html({string} name, {string} member, {function} generate):将属性的值作为某个类型为HTMLElement的成员(默认为main)的innerHTML使用,可提供generate方法指定如何生成HTML
  • text({string} name, {string} member, {function} generate):将属性的值作为某个类型为HTMLElement的成员(默认为main)的innerText使用,可提供generate方法指定如何生成HTML,注意generate方法生成的是HTML,不需要转义
  • delegate({string} name, {string} member, {string} method):将更新委托给某个成员的某个方法,用于组合控件时如将title转给titleLabel.setText方法。

示例

以下是Select控件的示例,Select控件需要关注的属性有:

  • widthheight影响到主元素的样式
  • datasource修改后,下拉弹层中的内容需要更新
  • rawValue修改后,需要更新和值相关的界面
  • disabled变成true时,如果已经打开了下拉层,则要关上

实现如下:

/**
 * 根据控件的值更新其视图
 *
 * @param {Select} select Select控件实例
 * @inner
 */
function updateValue(select) {
    // 同步`value`
    var hidden = select.main.getElementsByTagName('input')[0];
    hidden.value = select.rawValue;

    // 同步显示的文字
    var selectedItem = select.datasource[select.selectedIndex];
    var displayText = selectedItem ? selectedItem.text : '';
    var textHolder = select.main.getElementsByTagName('span')[0];
    textHolder.innerHTML = require('./lib').encodeHTML(displayText);
}

var paint = require('./painters');

/**
 * 重绘
 *
 * @param {Array=} 更新过的属性集合
 * @protected
 */
Select.prototype.repaint = require('./controlHelper').createRepaint(
    // 宽和高通过样式设定
    paint.style('width'),
    paint.style('height'),
    // datasource更新后通过getLayerHTML生成selectionLayer的innerHTML,
    // selectionLayer是延迟生成的,可能不存在,painter会自行处理
    paint.html('datasource', 'selectionLayer', getLayerHTML),
    // 禁用时关上弹层
    {
        name: 'disabled',
        paint: function (select, value) {
            if (value && select.selectionLayer) {
                hideLayer(select);
            }
        }
    },
    // rawValue变化时,需要的逻辑有些复杂,且不通用,所以单独一个方法
    {
        name: 'rawValue',
        paint: updateValue
    }
);

框架变更

为了更好地使用这套机制,框架层面有以下变化:

setProperties方法添加返回值,将设置的值中有变化的部分返回,返回一个对象。对象的键是属性名,值为一个变更项,包含nameoldValuenewValue属性。这一变更是为了子类可以调用父类setProperties后知道哪些值有变化过,并触发如change之类的事件。

repaint现在有了2个参数,第2个参数是第一个参数的索引,没实际意义,只是实现了上面一点后这个毫无代价顺手就给了。

问题

  1. 有些控件,其实是存在多个属性其实对应一个元素的更新的,比如SelectselectedIndexrawValue,这种通常通过setProperties里额外处理,去掉其中一个属性来实现。但是painter是不是需要支持多个属性的管理,即多个属性更新一个或多个时,执行一次paint
  2. painter到底是通过OO实现还是函数式实现好

关于BoxGroup

几个意见:

  1. 我认为这东西不应该是个控件,反正不会在HTML中声明这个东西,也不会appendTorender,所以不需要继承InputControlControl,是个辅助类就行了
  2. 新的ESUI有Form控件,我建议在Form控件中提供getInputControlsByName({string} name, {string=} type)方法,不要再用getElementsByName + getControlFromElement的结构了,维持更好的结构性

@kitemao 参考,如果OK我去发动BoxGroupForm

Tree的设计提案

关于Tree控件的设计

Tree控件从实现上来说可能并不是最难的,但就交互来说,绝对可以算得上最烦杂的控件,甚至可以说没有之一。一个Tree控件最常用的交互有以下2个:

  • 展开节点,称之为expand
  • 收起节点,称之为collapse

但在这2个交互发生时,采取的行为却大为不同:

  • 数据可能是远程的,作为延迟加载,则要依赖XMLHttpRequest来加载
  • 数据可能是本地的,则从树状的对象中直接提取后展开或收起
  • 数据还可能是其它地方来的,比如通过节点运算从一个本地的数组中筛选出来(类似本地数据库)
  • 可能这个节点根本没有数据,则展开操作仅改变前面的图标不做任何操作

但是Tree作为一个控件,去引入XMLHttpRequest之类的,并且来根据自己的数据源判断进行这些操作,显然不合理且会增加实现的复杂度。

因此,我对Tree进行了一个重新设计,看下图:

tree

这个设计下,Tree控件仅提供最基本的事件和方法,有:

  • expandcollapse事件,提供节点对象(数据源中的对象,非DOM节点)作为参数
  • fillNode方法,向一个节点中填充子节点
  • emptyNode方法,清掉一个节点中的子节点
  • indicateNodeLoading方法:指示某个节点的数据正在加载

当点击一个节点时,Tree控件仅会根据节点的状态触发一个事件,本身不做任何的数据查询、加载、绘制工作,直到注册事件的逻辑来调用fillNodeemptyNode,才进行数据的填充操作

当然这么设计的话,控件使用会变得非常麻烦,因此再提供一个叫TreeDatasource的扩展(名字现在觉得不太对,要再想想),来实现这些功能。

TreeDatasource是一个针对Tree设计的类,esui提供的默认实现是接受一个树状的对象(参见EJSON相关章节)。当这个扩展起作用后,会注册Treeexpandcollapse事件,当事件发生时,在Treedatasource中找到对应节点,取出子数据,自动调用fillNodeemptyNode完成视图展现。

这么做的好处是,对于一个非常依赖数据,同时有可能需要延迟加载数据的控件,进一步把 数据视图 分离,保证控件只关注视图和交互,不关心数据的存取。如果需要延迟加载数据,则可以进一步提供一个RemoteTreeDatasource的扩展(后续由ef项目提供),这样esui不用关心XMLHttpRequest,又能非常简单地挂载上数据的远程加载。

对于这个设计,还存有几个问题:

  1. Tree提供的方法是不是够,从我对Tree的使用来看是够了,看看其它项目组有没进一步的需求(注意每个节点加个checkbox不是这一需求覆盖范围,有另外的设计支持)
  2. 这个TreeDatasource应该是个扩展,还是Tree自身抽象出来的一个组件。
    • 扩展的特点是可用可不用,不高兴了业务逻辑自己注册事件调用方法,但一个扩展只为一个类型的控件服务感觉别扭。
    • 如果是Tree自身要求的一个组件,则认为是必须有的,作为Tree的一个属性,同时避免扩展只为特定控件服务的情况。

关于控件的readOnly和disabled状态

几个问题:

  1. 是否所有控件都有disabled状态,各控件在disabled状态下的行为如何标准化,如Panel控件如果有disabled状态,则是否其子控件都进入disabled状态?
  2. 控件在disabled状态下,修改其属性是否重新渲染,如TextBoxdisabled状态下调用setValue(value)是否有效?
  3. 控件在readOnly状态下,修改属性是否重新渲染?

个人意见:

  1. 并非所有控件都有disabled状态,但InputControl一定会有。如LabelPanelDialog这一些并不需要这样的状态。
  2. disabledreadOnly状态下均 正常进行 repaint操作,这一点参考HTML的<input>系控件。

[评审]UI标准 - Region

ECOM UI组件标准 - Region

功能描述

Region用于地域选择。

Region支持单选和多选两种选择模式。

单选以下拉选框形式展示。

继承层级

Region
    - InputControl
        - Control

prototype.type

"Region"

控件主元素

控件主元素必须为div。

数据格式说明

多选模式下,value的字符串格式为逗号分割的地域id数组。

如果选择了某城市,则value串种除了包含该城市下的所有区域id,还包括该城市的id。

地域列表可通过静态参数配置,也可初始化时传入。地域列表是一个Array,列表中的每一项是一个可具有id、text、children属性的多重结构的数据。

[
    {
        id: "China",
        text: "**地区",
        children: [
            {
                id: "North",
                text: "华北地区",
                children: [
                    {id: "1", text: "北京"},
                    {id: "3", text: "天津"},
                    {id: "13", text: "河北"},
                    {id: "26", text: "山西"},
                    {id: "22", text: "内蒙古"}
                ]
            },
            {
                id: "NorthEast",
                text: "东北地区",
                children: [
                    {id: "21", text: "辽宁"},
                    {id: "18", text: "吉林"},
                    {id: "15", text: "黑龙江"}
                ]
            },
            {
                id: "East",
                text: "华东地区",
                children: [
                    {id: "2", text: "上海"},
                    {id: "19", text: "江苏"},
                    {id: "32", text: "浙江"},
                    {id: "9", text: "安徽"},
                    {id: "5", text: "福建"},
                    {id: "20", text: "江西"},
                    {id: "25", text: "山东"}
                ]
            },
            {
                id: "Middle",
                text: "华中地区",
                children: [
                    {id: "14", text: "河南"},
                    {id: "16", text: "湖北"},
                    {id: "17", text: "湖南"}
                ]
            },
            {
                id: "South",
                text: "华南地区",
                children: [
                    {id: "4", text: "广东"},
                    {id: "8", text: "海南"},
                    {id: "12", text: "广西"}
                ]
            },
            {
                id: "SouthWest",
                text: "西南地区",
                children: [
                    {id: "33", text: "重庆"},
                    {id: "28", text: "四川"},
                    {id: "10", text: "贵州"},
                    {id: "31", text: "云南"},
                    {id: "29", text: "西藏"}
                ]
            },
            {
                id: "NorthWest",
                text: "西北地区",
                children: [
                    {id: "27", text: "陕西"},
                    {id: "11", text: "甘肃"},
                    {id: "24", text: "青海"},
                    {id: "23", text: "宁夏"},
                    {id: "30", text: "**"}
                ]
            },
            {
                id: "Other",
                text: "其他地区",
                children: [
                    {id: "34", text: "香港"},
                    {id: "36", text: "澳门"},
                    {id: "35", text: "**"}
                ]
            }
        ]
    },
    {
        id: "Abroad",
        text: "国外",
        children: [
            {id: "7", text: "日本"},
            {id: "37", text: "其他国家"}
        ]
    }
]

属性

{Array} regionData

地域列表。默认应用构造器中的REGION_LIST设置。

{string} mode

选择模式。multi|single。默认值为multi。

{Array|String} rawValue

当前选中的地域value

当mode为‘single'时,地域id字符串格式。

当mode为‘multi'时,地域id数组格式。

公共实例方法

{Array} getRawValue()

获取选中的地域,数组格式。

{void} setRawValue( {Array} value )

通过数组格式,设置选中的地域。

参数:

  • {Array} value: 选中的地域

事件

change

当选中地域发生改变时触发。

事件对象成员:

  • {string}type: 事件名

关于lib的几个问题

看了下lib的函数,总结一下几个问题:

保持函数的简单性

比如bind函数,原实现是可以从scope中取fun为名字的方法的,我认为这是原tangram为了某些业务线的需求而产生的功能,我们不应该鼓励和提倡这种用法,因此我去掉了。

同样的问题在很多函数中都存在,例如:

  • format函数支持${name}{index}两种形式,是否只支持${name}就OK了
  • hasClassaddClassremoveClass支持多个class以空格分隔,确实这功能非常强大,jQuery也提供这一功能,但在一个UI控件体系中,代码都是可掌握的高质量代码,是否有必要提供这种 看似便利实则可能导致混乱和不一致性 的功能

关于继承

tangram的继承实现是没错的,但是它提供了一些额外的东西,包括:

  • 一个__type属性表示类型
  • 一个superClass属性引用基类
  • 一个extends方法生产子类

其中superClass比较广泛,可以认可(虽然我个人很反对这一做法),但是__typeextends从我的角度来看是完全没有存在的意义的,是否还需要保留。

关于深层次结构嵌套

现有的lib其实是比较混乱的,语言层面的东西(trimclone等)、字符串层面的东西(encodeHTML等)和DOM层面的东西(ghasClass等)混在一个下面,这种设计是没有问题的,我个人也很推崇这种,毕竟没多少东西,不必要再细分。

但是奇怪的是,有一个page二级“命名空间”,这货的存在意义是什么……?

题外话:如果现在lib中的东西是原封不动从tangram弄过来的话,这大概是我第一次认真地看tangram的源码,只想吐槽这代码质量是有多差……

Select控件标准的几个问题

关于emptyText

原定emptyText是在 什么都没选择 的情况下显示的文字,但这里有几点比较奇怪:

  • 事实上Select的交互过程中,只有刚渲染完会出现 什么都没选择 的情况,而为了保证后续还能回到这个状态,在下拉框中会保留一项
  • 原生的<select>元素是没这种东西的,默认选中第一项,由<option>控制
  • 有时候,datasource满足了emptyText的需求,即有一个默认项,此时去不掉emptyText的配置会很麻烦

因此,建议把这个属性修改一下,新的描述如下:

emptyText指定在datasource之外,增加一个value为空的选项的文字。如果此属性的值为空,则不增加额外的一项。额外的一项永远加在最前面。

关于staticText

据有关部门了解,这个属性是用来把Select控件变成一个命令菜单用的。但其实命令菜单不应该是InputControl,因此建议单独实现CommandMenu控件,优势如下:

  • CommandMenu只需{ text: handler }[ text ... ]的配置,而不需要{ text: value }的配置,因此两个控件的datasource是不同的
  • CommandMenu没有随表单提交的需求,不会额外生成提交的参数
  • Select控件中大量逻辑在处理valuerawValueselectedIndex这三个属性的同步问题,而CommandMenu不需要
  • Select处理同步已经有很大的负担,再额外增加staticText复杂性会太高

为body增加功能相关的class

有时候,一个控件的样式,可以使用CSS3来做,同时降级到低级浏览器能看。但又有时候,感觉降级的效果并不那么漂亮,所以要再加一些或者改变一下更合适。

因此,学习一下Modenizr,让esui也在body上加一些class,以方便写css吧。具体要什么样式,各控件自己控制吧,定义控件的时候加上去就好,但要求 **统一用ui-support-xxx的形式。

求讨论

需要增加一个Crumb控件

面包屑导航条是不少系统有的内容,但在业务级别上制作这个会有一些通用的东西可以抽取:

  • 当前页不使用链接,其它项都使用链接,需要区分
  • 各项之间的分隔符可以统一

建议提供一个简单的Crumb控件,接受一个数组作为数据源来绘制面包屑

关于Command扩展和Table控件

几个事情需要提一下:

  1. 所有的Extension是给业务人员用的,控件开发人员 不能使用 ,这就类似一个基础库的代码不能去依赖业务线的代码,这是一种强制性的设计理念。Table中有用到Command,虽然可能辛苦点,但希望Table可以清理一下这一块的代码,写成标准的DOM事件。不然会导致不少的问题(比如在使用Table的时候再配一个Command扩展会冲突等)
  2. Command在修发后,我发现当触发command事件时,会带上两个参数:eelement,这两个参数全是 DOM相关 的,而ESUI的理念是 屏蔽全部DOM相关的逻辑 ,因此在ESUI体系下暴露的事件对象中让使用者可以访问到DOM是 不合理 的,现在由于Table依赖着他,因此我还没去掉这些代码,希望清理Table后可以去除

Table由于过于庞大,且最近事多,没来得及详细看一遍,后续我会尽量抽时间Review。

@wurongyao 关注一下

实现无根节点的Tree控件

虽然E-JSON中说明树的数据源结构应该是一个Object,但是很多系统中的树,其实并不需要一个要,而是一个标题(<h3>之类)下直接放一列的一级节点,形如:

*选择频道*

    + 其它频道
    + 频道1
    - 频道2
        - 子频道1
        - 子频道2
        - 子频道3
    + 频道3

很多时候,树的根节点并没有什么意义,第一层直接显示才是正常的。

因此,需要让Tree控件的datasource属性支持一个数组。

关于initChildren的行为

现在的initChildren,会把一段HTML中的所有元素分解成控件,且 无论其层级,统一作为自己的子控件 ,如下代码:

var html = [
    '<div data-ui="type: Panel; childName: header;">',
        '<span data-ui="type: Label; childName: close;">关闭</span>',
    '</div>',
    '<div data-ui="type: Panel; childName: foot;">',
        '<div data-ui="type: Button; childName: close;">关闭</div>',
    '</div>'
];
this.main.innerHTML = html.join('');
this.initChildren();

会产生2个childNameclose 的控件,且不会 一个在header中,一个在foot中 ,而是统一作为自己的子控件。那么只会有一个可以通过this.getChild('close')访问到。

而我希望的是,可以通过this.getChild('foot').getChild('close').on('click', closeThis)来注册事件。

由于控件的开发者是不能在里面写id的,因此一个有层级的childName会起到更好的效果。不然childName就要像id一样写成一个是 headerClose 一个是 footClose ,这让childNameid的概念之间产生混淆。

从命名上来说,我认为initChildren的意思是 初始化子控件 ,但并不代码一定 所有控件都是子控件 ,而是应该以递归的方式创建出一个树。

Schedule快捷按钮无效

点击[全周投放]等快捷按钮时报错:

Uncaught TypeError: Cannot read property 'getVal' of undefined

ViewContext id的进一步问题

  1. 通过setViewContext更换时,是否要确保main上的data-ctrl-view-context属性同步变化?
  2. 我们是否让viewContext是只读的,以避免这一类问题的出现?想不到需要改变viewContext的情况。如果是只读的,我考虑删掉setViewContext方法。

@errorrik

Tab控件标准重新修订

以下是新的Tab控件UI标准,供大家讨论

功能描述

Tab控件可以用于标签页的切换。每个标签可以对应一个容器元素,Tab控件自动控制容器的切换。

Tab标签自身会创建一个导航条,包含各个标签的元素。

继承层级

Tab -> Control

prototype.type

"Tab"

配置项及属性

以下属性可在构造函数中传入,也可在运行过程中通过setsetProperties修改。

  • {Array} tabs:指定标签页的配置。
  • {boolean} allowClose:如果此项为 true ,则每个标签上会出现一个关闭元素,点击该元素会自动删除该标签。默认值为 false
  • {number} activeIndex:指定当前激活的标签的下标。默认值为 0

其中tabs中的每一项包含以下内容:

  • {string} title:标签的标题,必须有。
  • {string=} panel:标签对应的面板元素的id,可选。

其中activeIndex的变化如下:

  • 正常情况下,添加或移除标签,activeIndex始终指向当前激活的标签的下标。
  • 如果移除激活的标签,则该标签后面的那个标签会被激活。如果该标签是最后一个标签,则在移除后的最后一个标签被激活。
  • 如果当前没有任何标签,则activeIndex的值为 -1
  • 如果修改了tabs导致activeIndex超出标签的数量,则其值将会被修正为 0

控件主元素

Tab控件的控件主元素必须是<div>元素,如果构造控件时没给定主元素,则创建一个空的<div>元素作为主元素。

HTML声明的导航条

Tab控件的主元素中可以包含一个导航条元素,该导航条元素结构必须如下所示:

<ul data-role="navigator">
    <li data-for="a">tab1</li>
    <li data-for="b">tab2</li>
    <li data-for="c">tab3</li>
</ul>

导航条元素满足以下条件:

  • 元素必须为<ul><ol>等可包含<li>元素的容器。
  • 必须有data-role="navigator"属性。
  • 子元素必须是<li>元素。

Tab控件发现导航条后,会采取以下行为:

  1. 从其下的<li>元素抽取标签选项,以data-forpanel属性,以innerTexttitle属性。
  2. 使用该选项 覆盖构造函数传入的tabs选项
  3. 删除该导航条元素,后续Tab控件会自己建立符合需求的导航条。

HTML声明子元素

当符合以下 全部 条件时:

  • 构造函数未传入tabs选项,或该选项为一个空数组。
  • 主元素中没有导航条元素。
  • 主元素下有其它子元素。

Tab控件自动将每一个子元素作为一个标签配置项,元素的title属性作为配置项的title属性,元素的id属性作为配置项的panel属性。

公共方法

{void} add({Object} config)

在最后面添加一个标签,其中config包含title和可选的panel选项。

{void} insert({Object} config, {number} index)

在指定位置添加一个标签,其中config包含title和可选的panel选项。

{void} remove({Object} config)

移除指定的标签,其中config必须是控件tabs属性中已有的对象,通过引用判断相等。

{void} removeAt({number} index)

移除指定位置的标签。

{void} activate({Object} config)

激活指定的标签,其中config必须是控件tabs属性中已有的对象,通过引用判断相等。

{void} activateAt({number} index)

激活指定位置的标签。

事件

add

当添加或插入一个标签时触发,事件对象包含:

  • tab:添加的标签的配置项。

remove

通过方法或点击关闭标签的元素从而移除一个标签时触发,事件对象包含:

  • tab:移除的标签的配置项。

activate

因任何情况导致一个标签被激活时触发,事件对象包含:

  • activeIndex:当前激活的标签的下标
  • tab:当前激活的标签配置项

注意,当因为插入或移除标签导致activeIndex变化,但当前激活的标签并没有改变时, 不会 触发该事件。

问题

  1. 是否需要setActiveIndexgetActiveIndex等方法,现在可以使用set('activeIndex')来操作
  2. 是否添加属性使得控件在导航条最后面有一个“添加”按钮,点击后触发add事件,从而实现可变的标签页功能
  3. 移除一个标签的时候,对应的面板是否有默认的操作(隐藏?删除?)

[评审]UI标准 - RangeCalendar

ECOM UI组件标准 - RangeCalendar

功能描述

RangeCalendar用于选择日期区间。

继承层级

RangeCalendar 
    - InputControl
        - Control

prototype.type

"RangeCalendar"

控件主元素

控件主元素必须为div。

数据格式说明

RangeCalendar的value字符串格式为"yyyy-MM-dd,yyyy-MM-dd",逗号分隔的前后分别代表开始和结束日期。

对于rawValue以及可选择日期区间的数据格式,都以一个Object表示。该Object拥有name为begin和end的属性,分别为Date类型。

{
    begin: new Date(1983, 8, 3),
    end: new Date(2011, 10, 4)
}

默认属性

可以通过获取RangeCalendar类修改,效果作用于控件全局。

{string} dateFormat

日期显示的格式化方式。默认'yyyy-MM-dd'。

{string} paramDateFormat

日期参数的格式化方式。默认'yyyyMMdd'。

{string} shortCutItems

日期区间快捷选项列表。默认提供“昨天 | 最近7天 | 上周 | 本月 | 上个月 | 上个季度”六种快捷方式。每种快捷方式的定义格式如下:

            {
                name: '昨天',
                value: 0,
                getValue: function () {
                    var yesterday = new Date(this.now.getTime());
                    yesterday.setDate(yesterday.getDate() - 1);
                    return {
                        begin: yesterday,
                        end: yesterday
                    };
                }
            }

自定义属性

创建时定义,效果作用于单个控件实例。

{string} shownShortCut

需要显示的mini日历快捷方式。默认全部。格式:‘[快捷方式显示名],[快捷方式显示名]’,如'昨天,最近7天'

{Object} range

可选中的日期区间。默认

{
    begin: new Date(2011, 8, 3),
    end: new Date(2011, 10, 4)
}

{Object} rawValue

当前选中的日期区间,Object格式。

{
    begin: new Date(2011, 8, 3),
    end: new Date(2011, 10, 4)
}

{string} value

当前选中的日期区间的字符串形式,"yyyy-MM-dd hh:mm:ss,yyyy-MM-dd hh:mm:ss",逗号分隔的前后分别代表开始和结束日期。

如:'2011-08-03 00:00:00,2011-10-04 23:59:59'

{string | boolean} endlessCheck

日历可以配置为“无结束时间”即“无限”模式。endlessCheck为true时开启模式。rawValue中如果不包含结束时间,无限模式将被自动开启。

公共实例方法

{Object} getRawValue()

获取当前选中的日期区间,对象格式。

{void} setRange( {Object|string} range )

设置允许选中的日期区间。

参数:

  • {Object|string} range: 允许选中的日期区间

支持字符串型,‘,’分割起始时间和结束时间。如:1983-09-03,2046-11-04。

{void} setRawValue( {Object} value )

通过对象格式,设置当前选中的日期区间。

参数:

  • {Object} value: 当前选中的日期区间

事件

change

当选择的日期发生改变时触发。

事件对象成员:

  • {string}type: 事件名
  • {Object}rawValue: 选择的值,对象格式,参见本篇中的“数据格式说明”

关于控件创建时的各来源的参数的优先级

比如说我们new一个控件的时候吧,会传一个options对象,这个对象里可能有个main属性提供HTML元素,这时就有问题了。

有一些参数,会从main上去提取,但同时options对象里又有同名的参数,这里的优先级需要讨论,典型的如下代码:

var a = document.createElement('a');
a.href = 'http://www.baidu.com';
document.body.appendChild(a);
var link = new Link({ main: a, href: 'http://www.google.com' });
link.render();

那么,这个link控件的href到底应该是百度还是Google呢?

同样的问题也出在从HTML解析出控件的时候:

<a href="http://www.baidu.com"
    data-ui-type="Link"
    data-ui-href="http://www.google.com">
    This is a link
</a>

是否能为控件增加data属性?

  为了控件更好的封闭性,控件的main元素不宜暴露给用户。

  但是用户还是会有往控件上添加某些数据的需求,如其交互关联Dom元素的Id,或某些业务信息。
  这些数据以前用户往往会加在main元素的属性中,现在这个main元素不再返回了,可能会给用户带来一些麻烦。

  如果能往控件元素上增加存储数据的data属性,将方便用户的使用,用户也将大大降低对main元素本身的依赖。将数据属性和元素属性区分开来,好处是不会干扰控件本身的属性。

  方法可以参考html5 中的 data-* 属性,并在控件中增加 setData getData。( 或者其他合适的命名)

在mixin.less中增加几个mixin

  1. .inline-block()提供兼容IE的display: inline-block;的功能
  2. .iconize(url)将元素中的文字进行大量偏移使其消失,并设置背景图片为指定的图标
  3. .reset-list()<ul><ol>paddingmarginlist-style设置为无

[评审]UI标准 - Tree

Tree控件标准

Tree是树控件,将一个树状的数据源对象以树型显示出来,同时控制节点的展开/收起操作。

继承关系

- Control
    - Tree

主元素

Tree控件的主元素可以是<div>或任何流式元素,默认使用<div>元素。

属性

{Object} datasource

树的数据源,其中一个节点的对应的数据称为 节点数据项 ,一个节点数据项可包含以下属性:

  • {string} id:节点的唯一id,必须在所有节点中具有唯一性。
  • {string} text:显示的文字。
  • {Array.<Object>=} children:子节点,数组中每个对象是一个节点数据项。

{TreeStrategy} strategy

与当前树关联的交互策略对象,详见 扩展点 部分对TreeStrategy的介绍。

需注意的是,strategy属性是 一次性 的,在实例化的时候设置,随后 不能 修改。

方法

{void} expandNode({string} id, {Array.=} children)

展开指定的节点,如提供了children参数,则使用该数组作为子节点数据。

参数

  • {string} id:待展开的节点的id。
  • {Array.<Object>=} children:填充的子节点数据。

children未提供时,将按以下规则处理:

  1. 如果曾经展开过该节点,即子节点的DOM结构已经生成,则直接将子节点的DOM显示出来,不作进一步处理。
  2. 如果未加载子节点的DOM结构,则去取id对应的节点数据中的children属性作为数据填充。

通常来说,当对应的节点已经有children属性时,调用expandNode时不需要传递children参数,以免在原来节点已经展开过的情况下,再次生成子树的DOM结构造成不必要的性能损失。

{void} collapseNode({string} id, {boolean=} removeChild)

收起指定的节点,并根据removeChild参数决定是否移除已经生成的子树的DOM结构。

参数

  • {string} id:待收起的节点的id。
  • {boolean} removeChild:默认值为false。如果此参数为true,则在收起的时会移除子树的DOM结构。当需要有效减少节点数量和控制性能时,建议传递此参数。

{void} indicateNodeLoading({string} id)

将指定节点标记为“加载中”状态。通常当树的节点展开时需要从远程加载数据,则可以在数据加载期间调用此方法给予用户提示。

参数

  • {string} id:需要标记的节点的id。

事件

expand

当一个收起的节点被请求展开时触发。

需注意的是,Tree控件仅触发该事件,并不会直接展开节点,需要配合TreeStrategy来添加默认展开的效果。

事件对象属性如下:

  • {Object} node:待展开的节点对应的节点数据项。

collapse

当一个展开的节点被请求收起时触发。

需注意的是,Tree控件仅触发该事件,并不会直接收起节点,需要配合TreeStrategy来添加默认收起的效果。

事件对象属性如下:

  • {Object} node:待收起的节点对应的节点数据项。

扩展点

{string} itemTemplate

itemTemplate属性可以用来指定控件中每一项生成时的HTML模板,其中占位符包含:

  • ${text}:显示的文本。
  • ${id}:节点的id。

占位符的替换均在经过HTML编码后进行。

{string} getItemHTML({Object} node)

该方法用于生成控件中每一项的HTML串,默认实现是基于itemTemplate属性的字符串格式化操作。

重写该方法后,itemTemplate的功能将失效。

TreeStrategy

由于树是一个无限定深度的数据结构,Tree控件往往会面对数据量很大,需要按需加载的情形。但是控件应当负责 数据的展现 ,而不对 数据的获取和处理 负责,因此提供TreeStrategy类来对数据的获取和处理进行抽象。

TreeStrategy类是对 树的数据相头的策略 的抽象,其包含以下方法:

  • {boolean} isLeafNode({Object} node):判断一个节点是否为叶子节点,树对叶子节点的展现样式是特殊的。
  • {boolean} shouldExpand({Object} node):判断一个节点是否应当展开,当树渲染一个节点时会调用此方法。如果需要展开,则会进一步渲染其children属性。
  • {void} attachTo({Tree} tree):绑定到对应的控件上,使当前策略生效。

通常来说,一个TreeStrategyattachTo方法需注册expandcollapse事件,在事件的处理函数中获取数据,并调用expandNodecollapseNode来使树得到正确的交互。

collapse事件发生时,通常简单地调用collapseNode方法收起节点即可,需要时可以通过collapseNode方法的removeChild参数控制子节点的删除以回收内存和提高性能。

expand事件发生时,需要根据数据的获取方案来提供不同的逻辑,一般如下:

  1. 如果数据是静态的,则直接调用expandNode,不传递children参数,由控件自动查找数据并生成子节点。
  2. 如果数据是远程加载的,则:
    1. 调用indicateNodeLoading方法,使节点进入加载状态。
    2. 通过XMLHttpRequest等手段加载远程数据,在收到数据后调用expandNode并传递children参数展开节点。注意在调用expandNode时,如果提供了children参数,则Tree控件会将提供的参数作为该节点的children属性保存,因此下一次调用时不再需要传递该参数。

框架内默认提供的TreeStrategy类实现了对静态数据的处理,远程动态数据需使用方自行实现。

控件DOM结构

控件的标准DOM结构如下:

<div class="ui-tree">
    <div class="ui-tree-root ui-tree-node">
        <span class="ui-node-indicator">节点前的指示器</span>
        ${getItemHTML}(${datasource})
        <!-- if ${datasource.children} && ${datasource.children.length} > 0 -->
            <ul class="ui-tree-sub-root">
                <!-- for ${datasource.children} as ${node} -->
                <li>
                    <!-- 与上面一样 -->
                    <span class="ui-node-indicator">节点前的指示器</span>
                    ${getItemHTML}(${node})
                    <!-- if ${node.children} && ${node.children.length} > 0 -->
                        <ul class="ui-tree-sub-root">
                            <!-- 递归 -->
                        </ul>
                    <!-- /if -->
                </li>
                <!-- /for -->
            </ul>
        <!-- /if -->
    </div>
</div>

状态相关的class如下:

  • ui-tree-root-expanded:根节点处于展开状态
  • ui-tree-root-collapsed:根节点处于收起状态
  • ui-tree-node-expanded:该节点处于展开状态。
  • ui-tree-node-collapsed:该节点处于收起状态。
  • ui-tree-sub-root-expanded:该子树处于展开状态。
  • ui-tree-sub-root-collapsed:该子树处于收起状态。
  • ui-tree-node-indicator-expanded:该指示器对应的节点处于展开状态。
  • ui-tree-node-indicator-collapsed:该指示器对应的节点处于收起状态。
  • ui-tree-node-indicator-busy:该指示器对应的节点处于加载状态。

TODO

  • 每一项前带CheckBox的多选树的扩展点设计。

showValidity的实现

需要一个高灵活性的实现,不同项目展示验证消息的方式大不一样。

建议:把showValidity丢到InputControl.prototype下去,helper模块是不对外的,无法提供劫持的功能

问题:默认的实现怎么样的合适,在InputControl后面插一个label元素放内容?

@errorrik @DDDBear @kitemao @wurongyao 一起讨论下吧,现在各系统的验证显示是长啥样的?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.