Skip to content

Commit

Permalink
feat(SearchSelect): 增加labelInValue & 控制value的格式 & 样式调整与组件库一致 (#545)
Browse files Browse the repository at this point in the history
* fix(Form): 修复ref获取不到表单方法

* fix(Form):使用useLayoutEffect替换useEffect绑定绑定ForwardedRef

* refactor:(Form) 修改Ref类型定义

* fix(Form): 修复无法重置(initialValue)表单问题

* fix(Form): 类型导出错误

* feat(search-select):增加多选功能

* fix(form):增加设置表单值方法

* feat(form):增加必传表单项标注 #534

* fix(search-select):增加Tag依赖

* fix(form):增加获取异常信息方法

* fix(form):修复afterSubmit回调失效问题

* fix(search-select):缺失依赖

* refactor(search-select):重命名类型名称

* style(search-select): 多选select样式

* style(search-select): 多选select下拉弹层样式

* fix(Table):data不支持对象内包含布尔类型 #517

* style(search-select):规范样式命名

* style(SearchSelect): 规范命名

* ifx(SearchSelect): 增加value受控支持,多选模式下为数组

* fix(SearchSelect): 修复defaultValue支持

* fix(Tree):修复autoExpandParent参数时效,无法展开父节点

* feat(SearchSelect): 增加参数控制tag可见数量,多余显示省略

* feat(SearchSelect): 增加参数控制tag可见数量,多余显示省略

* fix(SearchSelect): 防止对选择的循序排序

* fix(SearchSelect): 增加labelInValue,控制value的格式

* style(SearchSelec): 样式调整与组件库一直

* fix(SearchSelect):存在tag时不显示 placeholder

* docs(SearchSelect):labelInValue, value参数说明
  • Loading branch information
nullptr-z committed Feb 18, 2022
1 parent 5ef5127 commit e30f928
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 70 deletions.
23 changes: 13 additions & 10 deletions packages/react-search-select/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ const Demo = () => {

const [option, setOption] = React.useState(selectOption);
const [loading, setLoading] = React.useState(false);
const [value, setValue] = React.useState([2,5]);
const [value, setValue] = React.useState([{label: 'a8', value: 8}]);
function handleSearch(e) {
setValue([3,4])
setLoading(true)
setTimeout(() => {
setOption();
Expand All @@ -42,9 +41,10 @@ const Demo = () => {
return(
<SearchSelect
mode="multiple"
style={{ maxWidth: 200 }}
style={{ width: 176 }}
showSearch={true}
maxTagCount={2}
labelInValue={true}
maxTagCount={6}
allowClear
value={value}
disabled={false}
Expand All @@ -54,6 +54,7 @@ const Demo = () => {
loading={loading}
option={option}
onChange={(value) => {
console.log('value',value)
setValue(value)
}}
/>
Expand Down Expand Up @@ -167,11 +168,13 @@ ReactDOM.render(<Demo />, _mount_);
| disabled | 禁用选择器 | Boolean | `false` |
| mode | 选择模式: `multiple` `single` | String | `single` |
| defaultValue | 指定默认选中的条目 | String/Number | - |
| value | 指定选中的条目的值 | String/Number 多选模式下`value`皆为`Array<String/Number>` | - |
| value | 指定当前选中的条目,多选时为一个数组 | String \| Number \| String[] \| Number[] \| LabeledValue \| LabeledValue[] | - |
| placeholder | 选择框默认文字 | String | - |
| maxTagCount | 多选模式下展示tag的个数,默认所有 | number | - |
| labelInValue | 开启会把 Select 的 value 类型从 `string/number` 变为 `{ value: string/number, label: string }` | Boolean | `false` |
| showSearch | 使单选模式可搜索 | Boolean | - |
| size | 选择框尺寸 | Enum{`large`, `default`, `small` } | `default` |
| onChange | 选中 option,或 input 的 value,调用此函数 | `function(value: String/Number, option:Array<Option>)` | - |
| onSearch | 文本框值变化时回调 | `function(value: String)` | - |
| onSelect | 被选中时调用,参数为选中项的 value | `function(value: String/Number )` | - |
| loading | 加载中状态 | Boolean | false |
| size | 选择框尺寸 | Enum{large, default, small } | `default` |
| onChange | 选中 option,或 input 的 value,调用此函数 | function(value:String \| Number \| String[] \| Number[] \| LabeledValue \| LabeledValue[]) | - |
| onSearch | 文本框值变化时回调 | function(value: String) | - |
| onSelect | 被选中时调用,参数为选中项的 value | function(value: String/Number ) | - |
| loading | 加载中状态 | Boolean | `false` |
145 changes: 85 additions & 60 deletions packages/react-search-select/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ export interface SearchSelectProps extends IProps, DropdownProps {
mode?: 'single' | 'multiple';
size?: 'large' | 'default' | 'small';
maxTagCount?: number;
labelInValue?: boolean;
loading?: boolean;
showSearch?: boolean;
allowClear?: boolean;
defaultValue?: ValueType | Array<ValueType>;
value?: ValueType | Array<ValueType>;
option: SearchSelectOptionData[];
onSelect?: (value: ValueType | Array<ValueType>) => void;
onSelect?: (value: ValueType | Array<ValueType> | Array<SearchSelectOptionData>) => void;
onSearch?: (value: string) => void;
onChange?: (value: ValueType | Array<ValueType>) => void;
onChange?: (value: ValueType | Array<ValueType> | Array<SearchSelectOptionData>) => void;
}

export interface SearchSelectOptionData {
Expand All @@ -40,6 +41,7 @@ export default function SearchSelect(props: SearchSelectProps) {
maxTagCount,
option = [],
loading = false,
labelInValue = false,
prefixCls = 'w-search-select',
className,
mode = 'single',
Expand Down Expand Up @@ -82,18 +84,31 @@ export default function SearchSelect(props: SearchSelectProps) {
}
}, [JSON.stringify(value)]);

function selectedValueChange(changeValue: ValueType | Array<ValueType>) {
const getSelectOption = (option: Array<SearchSelectOptionData>, value: ValueType) => {
const findResult = option.find((item) => item.value === value);
return findResult;
};

function selectedValueChange(
changeValue: ValueType | Array<ValueType> | SearchSelectOptionData | Array<SearchSelectOptionData>,
) {
let opts: Array<SearchSelectOptionData> = [];
if (Array.isArray(changeValue)) {
opts = option.filter((item) => {
const findResult = changeValue.find((v) => item.value === v);
return findResult;
});
} else if (!isMultiple) {
const findResult = option.find((item) => item.value === changeValue);
if (findResult) {
setSelectedLabel(findResult.label);
opts.push(findResult);

if (labelInValue) {
if (Array.isArray(changeValue)) {
opts = changeValue as Array<SearchSelectOptionData>;
} else {
opts.push(changeValue as SearchSelectOptionData);
}
} else {
if (Array.isArray(changeValue)) {
opts = changeValue.map((v) => getSelectOption(option, v as ValueType)!);
} else {
const findResult = getSelectOption(option, changeValue as ValueType);
if (findResult) {
setSelectedLabel(findResult.label);
opts.push(findResult);
}
}
}
setSelectedValue(opts);
Expand All @@ -111,9 +126,10 @@ export default function SearchSelect(props: SearchSelectProps) {
const values = [item];
setSelectedLabel(item.label);
const resultValue = item.value;
value === undefined && setSelectedValue(values); // 如果存在props.value,内部不维护value状态
onSelect && onSelect(resultValue);
handleSelectChange(resultValue); // 支持form组件
handleSelectChange(resultValue, values); // 支持form组件

value === undefined && setSelectedValue(values); // 如果存在props.value,内部不维护value状态
}

function handleItemsClick(item: SearchSelectOptionData) {
Expand All @@ -125,9 +141,10 @@ export default function SearchSelect(props: SearchSelectProps) {
values = [...selectedValue, item];
}
const resultValue = values.map((item) => item.value);
value === undefined && setSelectedValue(values);
onSelect && onSelect(resultValue);
handleSelectChange(resultValue); // 支持form组件
handleSelectChange(resultValue, values); // 支持form组件

value === undefined && setSelectedValue(values);
}

// 渲染icon
Expand Down Expand Up @@ -155,14 +172,16 @@ export default function SearchSelect(props: SearchSelectProps) {
setSelectedValue([]);
setSelectedLabel('');
setSelectIconType('');
handleSelectChange('');
handleSelectChange('', []);
}
function handleSelectChange(value: ValueType | Array<ValueType>) {
onChange && onChange(value);
function handleSelectChange(value: ValueType | Array<ValueType>, options: Array<SearchSelectOptionData>) {
if (!onChange) return;

onChange(labelInValue ? options : value);
}

function inputKeyDown(e: any) {
if (selectedValue.length > 0 && !!selectedLabel && e.keyCode === 8) {
if (isMultiple && selectedValue.length > 0 && !selectedLabel && e.keyCode === 8) {
const values = removeSelectItem(selectedValue.length - 1);
setSelectedValue(values);
}
Expand All @@ -186,22 +205,22 @@ export default function SearchSelect(props: SearchSelectProps) {
style={{
minHeight: 25,
maxHeight: 280,
minWidth: style?.minWidth || 200,
minWidth: 200,
overflowY: 'scroll',
width: divRef.current ? divRef.current.offsetWidth : 'auto',
}}
>
{!option || option.length === 0 ? (
<div style={{ color: '#c7c7c7', fontSize: 12 }}>{loading ? '正在加载数据...' : '没有数据'}</div>
) : (
option.map((item, idx) => {
const active = !!selectedValue.find((finds) => finds.value === item.value);
option.map((opt, idx) => {
const active = !!getSelectOption(selectedValue, opt.value);
return (
<Menu.Item
active={active}
key={idx}
text={item.label}
onClick={() => (isMultiple ? handleItemsClick(item) : handleItemClick(item))}
text={opt.label}
onClick={() => (isMultiple ? handleItemsClick(opt) : handleItemClick(opt))}
/>
);
})
Expand All @@ -215,44 +234,50 @@ export default function SearchSelect(props: SearchSelectProps) {
onMouseLeave={() => renderSelectIcon('leave')}
style={{ width: '100%', maxWidth: 'none', ...style }}
>
<div
style={
isMultiple
? {
display: 'flex',
flexFlow: 'wrap',
borderRadius: 3,
boxShadow: '0px 0px 2px #333',
}
: undefined
}
>
{isMultiple &&
selectedValue.slice(0, maxTagCount).map((item, index) => {
return (
<Tag
style={{ margin: 2, display: 'flex', alignItems: 'center' }}
key={index}
closable
onClose={() => setSelectedValue(removeSelectItem(index))}
color="#ccc"
>
{item.label}
{isMultiple ? (
<div className={`${prefixCls}-inner`}>
<div style={{ display: 'flex', flexFlow: 'wrap' }}>
{isMultiple &&
selectedValue.slice(0, maxTagCount).map((item, index) => {
return (
<Tag
style={{ height: 20, margin: 1, display: 'flex', alignItems: 'center' }}
className={`${prefixCls}-tag`}
key={index}
closable
onClose={() => setSelectedValue(removeSelectItem(index))}
color="#ccc"
>
{item.label}
</Tag>
);
})}
{!!omitTagCount && (
<Tag style={{ height: 20, margin: 1, display: 'flex', alignItems: 'center' }} disabled={true}>
+{omitTagCount}{' '}
</Tag>
);
})}
{!!omitTagCount && (
<Tag style={{ margin: 2, display: 'flex', alignItems: 'center' }} disabled={true}>
+{omitTagCount}{' '}
</Tag>
)}
)}
<Input
style={{ flex: 1 }}
className={`${prefixCls}-input-contents`}
readOnly={!showSearch}
size={size}
disabled={disabled}
onKeyDown={inputKeyDown}
onChange={handleInputChange}
value={selectedLabel}
placeholder={selectedValue.length ? '' : placeholder}
/>
</div>
{(selectIconType === 'close' || (selectIconType === 'loading' && loading)) && (
<Icon type={selectIconType} spin={loading && selectIconType === 'loading'} onClick={resetSelectedValue} />
)}
</div>
) : (
<Input
style={{ flex: 1, boxShadow: 'none' }}
className={isMultiple ? `${prefixCls}-input-contents` : undefined}
readOnly={!showSearch}
size={size}
disabled={disabled}
onKeyDown={inputKeyDown}
onChange={handleInputChange}
value={selectedLabel}
placeholder={placeholder}
Expand All @@ -266,7 +291,7 @@ export default function SearchSelect(props: SearchSelectProps) {
)
}
/>
</div>
)}
</div>
</Dropdown>
);
Expand Down
46 changes: 46 additions & 0 deletions packages/react-search-select/src/style/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
&-input-contents {
input {
box-shadow: none;
padding: 0px;
height: 20px;
}

.w-input-inner {
Expand All @@ -16,4 +18,48 @@
}
}
}

&-inner {
display: flex;
justify-content: space-between;
outline: none;
border: none;
align-items: center;
border-radius: 3px;
box-shadow: 0 0 0 0 rgba(19, 124, 189, 0), 0 0 0 0 rgba(19, 124, 189, 0), inset 0 0 0 1px rgba(16, 22, 26, 0.15),
inset 0 1px 1px rgba(16, 22, 26, 0.2);
box-sizing: border-box;
background: #fff;
min-height: 30px;
margin: 0 !important;
padding: 3px 10px 3px 10px;
vertical-align: middle;
line-height: 30px;
color: #393e48;
font-weight: 400;
font-size: inherit;
transition: box-shadow 0.3s cubic-bezier(0.4, 1, 0.75, 0.9);
appearance: none;

&:focus {
box-shadow: 0 0 0 1px #393e48, 0 0 0 3px rgba(57, 62, 72, 0.17), inset 0 1px 1px rgba(16, 22, 26, 0.2);
}

&:hover {
box-shadow: 0 0 0 1px #6e6e6e, 0 0 0 3px rgba(57, 62, 72, 0), inset 0 1px 1px rgba(16, 22, 26, 0.2);
}

&:focus&:hover {
box-shadow: 0 0 0 1px #6e6e6e, 0 0 0 3px rgba(57, 62, 72, 0.17), inset 0 1px 1px rgba(16, 22, 26, 0.2);
}

&:disabled {
box-shadow: none;
background: #dddddd;
opacity: 0.75;
color: #a5a5a5;
cursor: not-allowed;
resize: none;
}
}
}

0 comments on commit e30f928

Please sign in to comment.