UNPKG

ecui

Version:

Enterprise Classic User Interface.

559 lines (501 loc) 19.6 kB
/* Select - 定义模拟下拉框行为的基本操作。 下拉框控件,继承自输入控件,实现了选项组接口,扩展了原生 SelectElement 的功能,允许指定下拉选项框的最大选项数量,在屏幕显示不下的时候,会自动显示在下拉框的上方。在没有选项时,下拉选项框有一个选项的高度。下拉框控件允许使用键盘与滚轮操作,在下拉选项框打开时,可以通过回车键或鼠标点击选择,上下键选择选项的当前条目,在关闭下拉选项框后,只要拥有焦点,就可以通过滚轮上下选择选项。 下拉框控件直接HTML初始化的例子: <select ecui="type:select" name="sex"> <option value="male" selected="selected">男</option> <option value="female">女</option> </select> 或 <div ecui="type:select;name:sex;value:male"> <div ecui="value:male">男</div> <div ecui="value:female">女</div> </div> 属性 _nOptionSize - 下接选择框可以用于选择的条目数量 _cSelected - 当前选中的选项 _uText - 下拉框的文本框 _uButton - 下拉框的按钮 _uOptions - 下拉选择框 */ //{if 0}// (function() { var core = ecui, array = core.array, dom = core.dom, string = core.string, ui = core.ui, util = core.util, undefined, DOCUMENT = document, MATH = Math, MAX = MATH.max, MIN = MATH.min, indexOf = array.indexOf, children = dom.children, createDom = dom.create, getParent = dom.getParent, getPosition = dom.getPosition, getText = dom.getText, insertAfter = dom.insertAfter, insertBefore = dom.insertBefore, moveElements = dom.moveElements, removeDom = dom.remove, encodeHTML = string.encodeHTML, extend = util.extend, getView = util.getView, setDefault = util.setDefault, $fastCreate = core.$fastCreate, getAttributeName = core.getAttributeName, getFocused = core.getFocused, inheritsControl = core.inherits, intercept = core.intercept, mask = core.mask, restore = core.restore, setFocused = core.setFocused, triggerEvent = core.triggerEvent, UI_INPUT_CONTROL = ui.InputControl, UI_INPUT_CONTROL_CLASS = UI_INPUT_CONTROL.prototype, UI_BUTTON = ui.Button, UI_SCROLLBAR = ui.Scrollbar, UI_PANEL = ui.Panel, UI_PANEL_CLASS = UI_PANEL.prototype, UI_ITEM = ui.Item, UI_ITEM_CLASS = UI_ITEM.prototype, UI_ITEMS = ui.Items; //{/if}// //{if $phase == "define"}// ///__gzip_original__UI_SELECT ///__gzip_original__UI_SELECT_CLASS /** * 初始化下拉框控件。 * options 对象支持的属性如下: * browser 是否使用浏览器原生的滚动条,默认使用模拟的滚动条 * optionSize 下拉框最大允许显示的选项数量,默认为5 * optionsElement 下拉选项主元素 * @public * * @param {Object} options 初始化选项 */ var UI_SELECT = ui.Select = inheritsControl( UI_INPUT_CONTROL, 'ui-select', function(el, options) { var name = el.name || options.name || '', type = this.getType(), optionsEl = createDom( type + '-options' + this.Options.TYPES, 'position:absolute;z-index:65535;display:none' ); options.value = options.value ? (options.value + '') : undefined; setDefault(options, 'hidden', true); if (el.tagName == 'SELECT') { var i = 0, list = [], elements = el.options, o = el; options.value = el.value; // 移除select标签 el = insertBefore(createDom(el.className, el.style.cssText, 'span'), el); removeDom(o); // 转化select标签 for (; o = elements[i];) { // 这里的text不进行转义,特殊字符不保证安全 list[i++] = '<div ' + getAttributeName() + '="value:' + encodeHTML(o.value) + '">' + o.text + '</div>'; } optionsEl.innerHTML = list.join(''); } else { moveElements(el, optionsEl); } el.innerHTML = '<span class="' + type + '-text' + UI_ITEM.TYPES + '"></span><span class="' + type + '-button' + UI_BUTTON.TYPES + '" style="position:absolute"></span><input name="' + name + '" value="' + encodeHTML(options.value || '') + '">'; el.appendChild(optionsEl); return el; }, function(el, options) { el = children(el); this._uText = $fastCreate(UI_ITEM, el[0], this, { capturable: false }); this._uButton = $fastCreate(UI_BUTTON, el[1], this, { capturable: false }); this._uOptions = $fastCreate( this.Options, removeDom(el[3]), this, { hScroll: false, browser: options.browser } ); this.$setBody(this._uOptions.getBody()); // 初始化下拉区域最多显示的选项数量 this._nOptionSize = options.optionSize || 5; this.$initItems(); } ), UI_SELECT_CLASS = UI_SELECT.prototype, /** * 初始化下拉框控件的下拉选项框部件。 * @public * * @param {Object} options 初始化选项 */ UI_SELECT_OPTIONS_CLASS = (UI_SELECT_CLASS.Options = inheritsControl(UI_PANEL)).prototype, /** * 初始化下拉框控件的选项部件。 * @public * * @param {Object} options 初始化选项 */ UI_SELECT_ITEM_CLASS = (UI_SELECT_CLASS.Item = inheritsControl( UI_ITEM, null, null, function(el, options) { this._sValue = options.value === undefined ? getText(el) : '' + options.value; } )).prototype; //{else}// /** * 下拉框刷新 * @private * * @param {ecui.ui.Select} control 下拉框控件 */ function UI_SELECT_FLUSH(control) { var options = control._uOptions, scrollbar = options.$getSection('VScrollbar'), el = options.getOuter(), pos = getPosition(control.getOuter()), selected = control._cSelected, optionTop = pos.top + control.getHeight(); if (!getParent(el)) { // 第一次显示时需要进行下拉选项部分的初始化,将其挂载到 DOM 树中 DOCUMENT.body.appendChild(el); control.cache(false, true); control.$alterItems(); } if (options.isShow()) { if (selected) { setFocused(selected); } scrollbar.setValue(scrollbar.getStep() * indexOf(control.getItems(), selected)); // 以下使用control代替optionHeight control = options.getHeight(); // 如果浏览器下部高度不够,将显示在控件的上部 options.setPosition( pos.left, optionTop + control <= getView().bottom ? optionTop : pos.top - control ); } } /** * 改变下拉框当前选中的项。 * @private * * @param {ecui.ui.Select} control 下拉框控件 * @param {ecui.ui.Select.Item} item 新选中的项 */ function UI_SELECT_CHANGE_SELECTED(control, item) { if (item !== control._cSelected) { control._uText.setContent(item ? item.getBody().innerHTML : ''); UI_INPUT_CONTROL_CLASS.setValue.call(control, item ? item._sValue : ''); control._cSelected = item; if (control._uOptions.isShow()) { setFocused(item); } } } extend(UI_SELECT_CLASS, UI_ITEMS); /** * 销毁选项框部件时需要检查是否展开,如果展开需要先关闭。 * @override */ UI_SELECT_OPTIONS_CLASS.$dispose = function() { this.hide(); UI_PANEL_CLASS.$dispose.call(this); }; /** * 关闭选项框部件时,需要恢复强制拦截的环境。 * @override */ UI_SELECT_OPTIONS_CLASS.$hide = function() { UI_PANEL_CLASS.$hide.call(this); mask(); restore(); }; /** * 对于下拉框选项,鼠标移入即自动获得焦点。 * @override */ UI_SELECT_ITEM_CLASS.$mouseover = function(event) { UI_ITEM_CLASS.$mouseover.call(this, event); setFocused(this); }; /** * 获取选项的值。 * getValue 方法返回选项控件的值,即选项选中时整个下拉框控件的值。 * @public * * @return {string} 选项的值 */ UI_SELECT_ITEM_CLASS.getValue = function() { return this._sValue; }; /** * 设置选项的值。 * setValue 方法设置选项控件的值,即选项选中时整个下拉框控件的值。 * @public * * @param {string} value 选项的值 */ UI_SELECT_ITEM_CLASS.setValue = function(value) { var parent = this.getParent(); this._sValue = value; if (parent && this == parent._cSelected) { // 当前被选中项的值发生变更需要同步更新控件的值 UI_INPUT_CONTROL_CLASS.setValue.call(parent, value); } }; /** * 下拉框控件激活时,显示选项框,产生遮罩层阻止对页面内 DOM 节点的点击,并设置框架进入强制点击拦截状态。 * @override */ UI_SELECT_CLASS.$activate = function(event) { if (!(event.getControl() instanceof UI_SCROLLBAR)) { UI_INPUT_CONTROL_CLASS.$activate.call(this, event); this._uOptions.show(); // 拦截之后的点击,同时屏蔽所有的控件点击事件 intercept(this); mask(0, 65534); UI_SELECT_FLUSH(this); event.stopPropagation(); } }; /** * 选项控件发生变化的处理。 * 在 选项组接口 中,选项控件发生添加/移除操作时调用此方法。虚方法,子控件必须实现。 * @protected */ UI_SELECT_CLASS.$alterItems = function() { var options = this._uOptions, scrollbar = options.$getSection('VScrollbar'), optionSize = this._nOptionSize, step = this.getBodyHeight(), width = this.getWidth(), itemLength = this.getItems().length; if (getParent(options.getOuter())) { // 设置选项框 scrollbar.setStep(step); // 为了设置激活状态样式, 因此必须控制下拉框中的选项必须在滚动条以内 this.setItemSize( width - options.getMinimumWidth() - (itemLength > optionSize ? scrollbar.getWidth() : 0), step ); // 设置options框的大小,如果没有元素,至少有一个单位的高度 options.$$mainHeight = itemLength * step + options.$$bodyHeightRevise; options.$setSize(width, (MIN(itemLength, optionSize) || 1) * step + options.getMinimumHeight()); } }; /** * @override */ UI_SELECT_CLASS.$cache = function(style, cacheSize) { (getParent(this._uOptions.getOuter()) ? UI_ITEMS : UI_INPUT_CONTROL_CLASS) .$cache.call(this, style, cacheSize); this._uText.cache(false, true); this._uButton.cache(false, true); this._uOptions.cache(false, true); }; /** * 控件在下拉框展开时,需要拦截浏览器的点击事件,如果点击在下拉选项区域,则选中当前项,否则直接隐藏下拉选项框。 * @override */ UI_SELECT_CLASS.$intercept = function(event) { //__transform__control_o this._uOptions.hide(); for (var control = event.getControl(); control; control = control.getParent()) { if (control instanceof this.Item) { if (control != this._cSelected) { // 检查点击是否在当前下拉框的选项上 UI_SELECT_CHANGE_SELECTED(this, control); triggerEvent(this, 'change'); } break; } } event.exit(); }; /** * 接管对上下键与回车/ESC键的处理。 * @override */ UI_SELECT_CLASS.$keydown = UI_SELECT_CLASS.$keypress = function(event) { UI_INPUT_CONTROL_CLASS['$' + event.type](event); var options = this._uOptions, scrollbar = options.$getSection('VScrollbar'), optionSize = this._nOptionSize, which = event.which, list = this.getItems(), length = list.length, focus = getFocused(); if (this.isFocused()) { // 当前不能存在鼠标操作,否则屏蔽按键 if (which == 40 || which == 38) { if (length) { if (options.isShow()) { setFocused(list[which = MIN(MAX(0, indexOf(list, focus) + which - 39), length - 1)]); which -= scrollbar.getValue() / scrollbar.getStep(); scrollbar.skip(which < 0 ? which : which >= optionSize ? which - optionSize + 1 : 0); } else { this.setSelectedIndex(MIN(MAX(0, indexOf(list, this._cSelected) + which - 39), length - 1)); } } return false; } else if (which == 27 || which == 13 && options.isShow()) { // 回车键选中,ESC键取消 options.hide(); if (which == 13) { UI_SELECT_CHANGE_SELECTED(this, focus); //触发change事件 triggerEvent(this, 'change'); } return false; } } }; /** * 如果控件拥有焦点,则当前选中项随滚轮滚动而自动指向前一项或者后一项。 * @override */ UI_SELECT_CLASS.$mousewheel = function(event) { if (this.isFocused()) { var options = this._uOptions, list = this.getItems(), length = list.length; if (options.isShow()) { options.$mousewheel(event); } else { //options表示当前选项的index // options = indexOf(list, this._cSelected) + (event.detail > 0 ? 1 : -1) // this.setSelectedIndex( // length ? // MIN(MAX(0, options), length - 1) : null // ); // if (options >= 0 && options < length) { // //鼠标滚动触发change事件 // triggerEvent(this, 'change'); // } } event.exit(); } }; /** * @override */ UI_SELECT_CLASS.$ready = function() { this.setValue(this.getValue()); }; /** * 下拉框移除子选项时,如果选项是否被选中,需要先取消选中。 * @override */ UI_SELECT_CLASS.remove = function(item) { if ('number' == typeof item) { item = this.getItems()[item]; } if (item == this._cSelected) { UI_SELECT_CHANGE_SELECTED(this); } return UI_ITEMS.remove.call(this, item); }; /** * 添加选项需要根据情况继续cache操作 * @override */ UI_SELECT_CLASS.add = function(item, index, options) { item = UI_ITEMS.add.call(this, item, index, options); if (getParent(this._uOptions.getOuter())) { item.cache(true, true); } return item; }; /** * @override */ UI_SELECT_CLASS.$setSize = function(width, height) { UI_INPUT_CONTROL_CLASS.$setSize.call(this, width, height); this.$locate(); height = this.getBodyHeight(); // 设置文本区域 this._uText.$setSize(width = this.getBodyWidth() - height, height); // 设置下拉按钮 this._uButton.$setSize(height, height); this._uButton.setPosition(width, 0); }; /** * 获取被选中的选项控件。 * @public * * @return {ecui.ui.Item} 选项控件 */ UI_SELECT_CLASS.getSelected = function() { return this._cSelected || null; }; /** * 设置下拉框允许显示的选项数量。 * 如果实际选项数量小于这个数量,没有影响,否则将出现垂直滚动条,通过滚动条控制其它选项的显示。 * @public * * @param {number} value 显示的选项数量,必须大于 1 */ UI_SELECT_CLASS.setOptionSize = function(value) { this._nOptionSize = value; this.$alterItems(); UI_SELECT_FLUSH(this); }; /** * 根据序号选中选项。 * @public * * @param {number} index 选项的序号 */ UI_SELECT_CLASS.setSelectedIndex = function(index) { UI_SELECT_CHANGE_SELECTED(this, this.getItems()[index]); }; /** * 设置控件的值。 * setValue 方法设置控件的值,设置的值必须与一个子选项的值相等,否则将被设置为空,使用 getValue 方法获取设置的值。 * @public * * @param {string} value 需要选中的值 */ UI_SELECT_CLASS.setValue = function(value) { for (var i = 0, list = this.getItems(), o; o = list[i++];) { if (o._sValue == value) { UI_SELECT_CHANGE_SELECTED(this, o); return; } } // 找不到满足条件的项,将选中的值清除 UI_SELECT_CHANGE_SELECTED(this); }; UI_SELECT_CLASS.clear = function() { var items = this.getItems() || [], len = items.length; while (len-- > 0) { this.remove(0); } this._uOptions.reset(); }; //{/if}// //{if 0}// })(); //{/if}//