feat(form-select): 支持创建新 option (#1618)

* feat(form-select):  支持创建新 option

* docs: 更新 select 文档

* fix: escape

* fix: 比较 option 未区分大小写

* update code

* docs: form

* fix: 缺少闭合标签

* fix: 删除 escape, 和初始 <dd> 行为一致
pull/1682/head
morning-star 9 months ago committed by GitHub
parent cecf33a83d
commit 18ac6d58f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -117,6 +117,7 @@ form 还可以借助*栅格*实现更灵活的响应式布局。
| lay-affix | [#详见](input.html#affix) | 输入框动态点缀,`<input type="text">`元素 **私有属性** | | lay-affix | [#详见](input.html#affix) | 输入框动态点缀,`<input type="text">`元素 **私有属性** |
| lay-skin | [#详见](checkbox.html#default) | 设置 UI 风格。 `<input type="checkbox">` 元素 **私有属性** | | lay-skin | [#详见](checkbox.html#default) | 设置 UI 风格。 `<input type="checkbox">` 元素 **私有属性** |
| lay-search | 默认不区分大小写;<br>设置`cs`区分大小写 | 给 `select` 组件开启搜索功能。`<select>` 元素 **私有属性** | | lay-search | 默认不区分大小写;<br>设置`cs`区分大小写 | 给 `select` 组件开启搜索功能。`<select>` 元素 **私有属性** |
| lay-creatable <sup>2.9.7+</sup> | 无需值 | 是否允许创建新条目,需要配合 `lay-search` 使用。`<select>` 元素 **私有属性** } |
| lay-submit | 无需值 | 设置元素(一般为`<button>` 标签)触发 `submit` 提交事件 | | lay-submit | 无需值 | 设置元素(一般为`<button>` 标签)触发 `submit` 提交事件 |
| lay-ignore | 无需值 | 设置表单元素忽略渲染,即让元素保留系统原始 UI 风格 | | lay-ignore | 无需值 | 设置表单元素忽略渲染,即让元素保留系统原始 UI 风格 |

@ -161,6 +161,18 @@ toc: true
</optgroup> </optgroup>
</select> </select>
</div> </div>
<div class="layui-col-md6">
<select lay-search="" lay-creatable="">
<option value="">可创建新的 option</option>
<option value="1">AAA</option>
<option value="2">aaa</option>
<option value="3">BBB</option>
<option value="4">bbb</option>
<option value="5">ABC</option>
<option value="6">abc</option>
<option value="7">AbC</option>
</select>
</div>
</div> </div>
<!-- import layui --> <!-- import layui -->

@ -375,6 +375,7 @@ layui.define(['lay', 'layer', 'util'], function(exports){
var CLASS = 'layui-form-select'; var CLASS = 'layui-form-select';
var TITLE = 'layui-select-title'; var TITLE = 'layui-select-title';
var NONE = 'layui-select-none'; var NONE = 'layui-select-none';
var CREATE_OPTION = 'layui-select-create-option';
var initValue = ''; var initValue = '';
var thatInput; var thatInput;
var selects = elem || elemForm.find('select'); var selects = elem || elemForm.find('select');
@ -382,14 +383,18 @@ layui.define(['lay', 'layer', 'util'], function(exports){
// 隐藏 select // 隐藏 select
var hide = function(e, clear){ var hide = function(e, clear){
if(!$(e.target).parent().hasClass(TITLE) || clear){ if(!$(e.target).parent().hasClass(TITLE) || clear){
$('.'+CLASS).removeClass(CLASS+'ed ' + CLASS+'up'); var elem = $('.' + CLASS);
elem.removeClass(CLASS+'ed ' + CLASS+'up');
if(elem.hasClass('layui-select-creatable')){
elem.children('dl').children('.' + CREATE_OPTION).remove();
}
thatInput && initValue && thatInput.val(initValue); thatInput && initValue && thatInput.val(initValue);
} }
thatInput = null; thatInput = null;
}; };
// 各种事件 // 各种事件
var events = function(reElem, disabled, isSearch){ var events = function(reElem, disabled, isSearch, isCreatable){
var select = $(this); var select = $(this);
var title = reElem.find('.' + TITLE); var title = reElem.find('.' + TITLE);
var input = title.find('input'); var input = title.find('input');
@ -408,6 +413,7 @@ layui.define(['lay', 'layer', 'util'], function(exports){
var showDown = function(){ var showDown = function(){
var top = reElem.offset().top + reElem.outerHeight() + 5 - $win.scrollTop(); var top = reElem.offset().top + reElem.outerHeight() + 5 - $win.scrollTop();
var dlHeight = dl.outerHeight(); var dlHeight = dl.outerHeight();
var dds = dl.children('dd');
index = select[0].selectedIndex; // 获取最新的 selectedIndex index = select[0].selectedIndex; // 获取最新的 selectedIndex
reElem.addClass(CLASS+'ed'); reElem.addClass(CLASS+'ed');
@ -432,6 +438,7 @@ layui.define(['lay', 'layer', 'util'], function(exports){
reElem.removeClass(CLASS+'ed ' + CLASS+'up'); reElem.removeClass(CLASS+'ed ' + CLASS+'up');
input.blur(); input.blur();
nearElem = null; nearElem = null;
isCreatable && dl.children('.' + CREATE_OPTION).remove();
if(choose) return; if(choose) return;
@ -569,10 +576,18 @@ layui.define(['lay', 'layer', 'util'], function(exports){
// 检测值是否不属于 select 项 // 检测值是否不属于 select 项
var notOption = function(value, callback, origin){ var notOption = function(value, callback, origin){
var num = 0; var num = 0;
var dds = dl.children('dd');
var hasEquals = false;
var rawValue = value;
layui.each(dds, function(){ layui.each(dds, function(){
var othis = $(this); var othis = $(this);
var text = othis.text(); var text = othis.text();
// 需要区分大小写
if(isCreatable && text === rawValue){
hasEquals = true;
}
// 是否区分大小写 // 是否区分大小写
if(laySearch !== 'cs'){ if(laySearch !== 'cs'){
text = text.toLowerCase(); text = text.toLowerCase();
@ -583,17 +598,18 @@ layui.define(['lay', 'layer', 'util'], function(exports){
var not = text.indexOf(value) === -1; var not = text.indexOf(value) === -1;
if(value === '' || (origin === 'blur') ? value !== text : not) num++; if(value === '' || (origin === 'blur') ? value !== text : not) num++;
origin === 'keyup' && othis[not ? 'addClass' : 'removeClass'](HIDE); origin === 'keyup' && othis[(not && (isCreatable ? !othis.hasClass(CREATE_OPTION) : true)) ? 'addClass' : 'removeClass'](HIDE);
}); });
// 处理 select 分组元素 // 处理 select 分组元素
origin === 'keyup' && layui.each(dts, function(){ origin === 'keyup' && layui.each(dts, function(){
var othis = $(this) var othis = $(this);
,thisDds = othis.nextUntil('dt').filter('dd') // 当前分组下的dd元素 var thisDds = othis.nextUntil('dt').filter('dd'); // 当前分组下的dd元素
,allHide = thisDds.length == thisDds.filter('.' + HIDE).length; // 当前分组下所有dd元素都隐藏了 if(isCreatable) thisDds = thisDds.not('.' + CREATE_OPTION);
var allHide = thisDds.length == thisDds.filter('.' + HIDE).length; // 当前分组下所有dd元素都隐藏了
othis[allHide ? 'addClass' : 'removeClass'](HIDE); othis[allHide ? 'addClass' : 'removeClass'](HIDE);
}); });
var none = num === dds.length; var none = num === dds.length;
return callback(none), none; return callback(none, hasEquals), none;
}; };
// 搜索匹配 // 搜索匹配
@ -607,12 +623,28 @@ layui.define(['lay', 'layer', 'util'], function(exports){
return false; return false;
} }
notOption(value, function(none){ notOption(value, function(none, hasEquals){
if(isCreatable){
if(hasEquals){
dl.children('.' + CREATE_OPTION).remove();
}else{
// 和初始渲染保持行为一致
var textVal = $('<div>' + value +'</div>').text();
var createOptionElem = dl.children('.' + CREATE_OPTION);
if(createOptionElem[0]){
createOptionElem.attr('lay-value', value);
createOptionElem.text(textVal);
}else{
dl.append('<dd class="' + CREATE_OPTION + '" lay-value="'+ value +'">' + textVal + '</dd>');
}
}
}else{
if(none){ if(none){
dl.find('.'+NONE)[0] || dl.append('<p class="'+ NONE +'">无匹配项</p>'); dl.find('.'+NONE)[0] || dl.append('<p class="'+ NONE +'">无匹配项</p>');
} else { } else {
dl.find('.'+NONE).remove(); dl.find('.'+NONE).remove();
} }
}
}, 'keyup'); }, 'keyup');
// 当搜索值清空时 // 当搜索值清空时
@ -622,6 +654,7 @@ layui.define(['lay', 'layer', 'util'], function(exports){
dl.find('.'+ THIS).removeClass(THIS); dl.find('.'+ THIS).removeClass(THIS);
(select[0].options[0] || {}).value || dl.children('dd:eq(0)').addClass(THIS); (select[0].options[0] || {}).value || dl.children('dd:eq(0)').addClass(THIS);
dl.find('.'+ NONE).remove(); dl.find('.'+ NONE).remove();
isCreatable && dl.children('.' + CREATE_OPTION).remove();
} }
followScroll(); // 定位滚动条 followScroll(); // 定位滚动条
@ -653,7 +686,7 @@ layui.define(['lay', 'layer', 'util'], function(exports){
} }
// 选择 // 选择
dds.on('click', function(){ dl.on('click', 'dd', function(){
var othis = $(this), value = othis.attr('lay-value'); var othis = $(this), value = othis.attr('lay-value');
var filter = select.attr('lay-filter'); // 获取过滤器 var filter = select.attr('lay-filter'); // 获取过滤器
@ -666,6 +699,11 @@ layui.define(['lay', 'layer', 'util'], function(exports){
othis.addClass(THIS); othis.addClass(THIS);
} }
if(isCreatable && othis.hasClass(CREATE_OPTION)){
othis.removeClass(CREATE_OPTION);
select.append('<option value="' + value + '">' + value + '</option>');
}
othis.siblings().removeClass(THIS); othis.siblings().removeClass(THIS);
select.val(value).removeClass('layui-form-danger'); select.val(value).removeClass('layui-form-danger');
@ -698,13 +736,15 @@ layui.define(['lay', 'layer', 'util'], function(exports){
if(typeof othis.attr('lay-ignore') === 'string') return othis.show(); if(typeof othis.attr('lay-ignore') === 'string') return othis.show();
var isSearch = typeof othis.attr('lay-search') === 'string' var isSearch = typeof othis.attr('lay-search') === 'string'
,isCreatable = typeof othis.attr('lay-creatable') === 'string' && isSearch
,placeholder = optionsFirst ? ( ,placeholder = optionsFirst ? (
optionsFirst.value ? TIPS : (optionsFirst.innerHTML || TIPS) optionsFirst.value ? TIPS : (optionsFirst.innerHTML || TIPS)
) : TIPS; ) : TIPS;
// 替代元素 // 替代元素
var reElem = $(['<div class="'+ (isSearch ? '' : 'layui-unselect ') + CLASS var reElem = $(['<div class="'+ (isSearch ? '' : 'layui-unselect ') + CLASS
,(disabled ? ' layui-select-disabled' : '') +'">' ,(disabled ? ' layui-select-disabled' : '')
,(isCreatable ? ' layui-select-creatable' : '') + '">'
,'<div class="'+ TITLE +'">' ,'<div class="'+ TITLE +'">'
,('<input type="text" placeholder="'+ util.escape($.trim(placeholder)) +'" ' ,('<input type="text" placeholder="'+ util.escape($.trim(placeholder)) +'" '
+('value="'+ util.escape($.trim(value ? selected.html() : '')) +'"') // 默认值 +('value="'+ util.escape($.trim(value ? selected.html() : '')) +'"') // 默认值
@ -734,7 +774,7 @@ layui.define(['lay', 'layer', 'util'], function(exports){
hasRender[0] && hasRender.remove(); // 如果已经渲染则Rerender hasRender[0] && hasRender.remove(); // 如果已经渲染则Rerender
othis.after(reElem); othis.after(reElem);
events.call(this, reElem, disabled, isSearch); events.call(this, reElem, disabled, isSearch, isCreatable);
}); });
} }

Loading…
Cancel
Save