Skip to content

Commit

Permalink
feat(select): independent select
Browse files Browse the repository at this point in the history
Remove list component dependency

fix growingio#677
  • Loading branch information
shiliqian committed Jan 22, 2021
1 parent 66fa6a1 commit 9dbc1ac
Show file tree
Hide file tree
Showing 13 changed files with 1,146 additions and 325 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"rc-trigger": "^4.3.0",
"rc-upload": "^3.2.0",
"rc-util": "^5.0.5",
"rc-virtual-list": "^3.2.6",
"react-dnd": "^11.1.3",
"react-dnd-html5-backend": "^11.1.3",
"react-use": "^15.3.3",
Expand Down
220 changes: 176 additions & 44 deletions src/components/pagination/__test__/__snapshots__/Pagination.test.js.snap

Large diffs are not rendered by default.

26 changes: 0 additions & 26 deletions src/components/select/Empty.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,6 @@ import './style/empty.less';

function SvgComponent(): React.ReactElement {
return (
// <svg width={60} height={61} viewBox="0 0 60 61">
// <g fill="none" fillRule="evenodd">
// <path d="M.588 36.882l10-21.764h38.824l10 21.764h-23.53a5.882 5.882 0 11-11.764 0H.588z" fill="#F7F8FC" />
// <path
// d="M.588 36.882h23.53a5.882 5.882 0 1011.764 0h23.53v20.53a3 3 0 01-3 3H3.588a3 3 0 01-3-3v-20.53z"
// fill="#FFF"
// />
// <path
// d="M47.436 15.618a3.497 3.497 0 013.18 2.038h0l8.564 18.64c.211.458.32.957.32 1.461h0V57c0 .966-.392 1.841-1.025 2.475A3.489 3.489 0 0156 60.5h0H4a3.489 3.489 0 01-2.475-1.025A3.489 3.489 0 01.5 57h0V37.757a3.5 3.5 0 01.32-1.46h0l8.564-18.64a3.497 3.497 0 013.18-2.04h0z"
// stroke="#DCDFED"
// />
// <path
// d="M.588 36.882h23.53a5.882 5.882 0 1011.764 0h23.53"
// stroke="#DCDFED"
// strokeLinecap="round"
// strokeLinejoin="round"
// />
// <path
// stroke="#DCDFED"
// strokeWidth={1.5}
// strokeLinecap="round"
// strokeLinejoin="round"
// d="M30 1v9.412M40.588 2.261l-4.706 8.151M19.412 2.261l4.706 8.151"
// />
// </g>
// </svg>
<svg viewBox="0 0 60 60" width={48} height={48}>
<g id="图层_2" data-name="图层 2">
<g id="图层_1-2" data-name="图层 1">
Expand Down
140 changes: 140 additions & 0 deletions src/components/select/OptionsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React, { useMemo, useRef } from 'react';
import { noop } from 'lodash';
import classnames from 'classnames';
import VirtualList from './VirtualList';
import { OptionsListProps, Option } from './interface';
import Tooltip from '../tooltip';
import Checkbox from '../checkbox';

const OptionsList: React.FC<OptionsListProps> = (props) => {
const {
labelRenderer,
prefixCls,
data,
groupStyle,
optionStyle,
selected,
multiple,
hasGroup,
height,
itemHeight,
onOptionClick,
optionLabelRenderer,
...restProps
} = props;

const flettensOptions = useMemo(() => {
const groupMap = new Map();
if (!hasGroup) return data;
data.map((cur) => {
const gvalue = groupMap.get(cur.groupValue)
if (gvalue) {
const { options, ...rest } = gvalue;
return groupMap.set(cur.groupValue, {
options: [...options, cur],
...rest
})
}
return groupMap.set(cur.groupValue, {
label: cur.groupLabel,
value: cur.groupValue,
isSelectOptGroup: true,
options: [cur]
});
})
const flettenOption: Option[] = [];
groupMap.forEach((value) => {
flettenOption.push(value)
flettenOption.push(...value.options)
})
return flettenOption;
}, [data, hasGroup])

const renderGroupItem = (option: Option) => {
const { value, label } = option;
return (
<div
className={`${prefixCls}-list-group`}
style={groupStyle}
aria-hidden="true"
>
{label !== undefined ? label : value}
</div>
)
}

const renderTootip = (option: Option, render: JSX.Element & React.ReactNode) => {
const { tooltip } = option;
if (tooltip) {
return (
<Tooltip
title={tooltip}
placement="top"
>
{render}
</Tooltip>
)
}
return render
}

const renderOption = (option: Option) => {
const { value, disabled, tooltip, groupValue, groupLabel, label, ...restOption } = option;
const isSelected = (typeof selected === 'string' || typeof selected === 'number' || typeof selected === 'undefined') ? selected === value : selected.includes(value);
const onClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
event.stopPropagation();
event.preventDefault();
if (onOptionClick) {
onOptionClick(value)
}
};
const labelNode = labelRenderer ? labelRenderer({
value,
disabled,
tooltip,
groupValue,
groupLabel,
label: optionLabelRenderer(value, option),
...restOption
}, false) : optionLabelRenderer(value, option);
return (
<div
className={`${prefixCls}-list-option-container`}
style={optionStyle}
>
<div
className={classnames(`${prefixCls}-list-option`, {
[`${prefixCls}-list-option-isSelected`]: isSelected,
[`${prefixCls}-list-option-disabled`]: disabled,
})}
onClick={disabled ? undefined : onClick}
onKeyDown={noop}
aria-hidden="true"
>
{multiple && (
<>
<Checkbox checked={isSelected} disabled={disabled} onChange={noop} />
<span style={{ width: 10 }} />
</>
)}
{labelNode}
</div>
</div>

)
}
return (
<VirtualList
itemKey='value'
prefixCls={prefixCls}
data={flettensOptions}
height={height}
itemHeight={itemHeight}
{...restProps}
>
{(option: Option & { isSelectOptGroup: boolean }) => option.isSelectOptGroup ? renderGroupItem(option) : renderTootip(option, renderOption(option))}
</VirtualList>
)
}

export default OptionsList;
132 changes: 126 additions & 6 deletions src/components/select/Select.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React from 'react';
import React, { createRef, useState } from 'react';
import { Story, Meta } from '@storybook/react/types-6-0';

import Select from './index';
import Button from '../button'
import Radio from '../radio'
import { SelectProps } from './interface';
import './style';
import { SizeType } from '../config-provider/SizeContext';

export default {
title: 'Components/Functional/Select',
Expand All @@ -12,16 +15,133 @@ export default {

const labels = ['全部', '已上线', '待上线', '已下线', '草稿'];
const values = ['all', 'online', 'pending', 'off', 'draft'];
const optionsWithoutGroup = values.map((value, index) => ({

const defaultValues = [];
let i = 0;

while (i < 100) {
defaultValues.push(`${values[i % 5]}${i}`);
i +=1;
}

const options = values.map((value,index)=>({
value,
label: labels[index],
}));
label:labels[index],
disabled:!!(index % 2),
}))

export const Default: Story<SelectProps> = (args) => <Select {...args} />;
const optionsWithoutGroup = defaultValues.map((value, index) => ({
groupLabel:labels[index % 5],
groupValue:values[index % 5],
value,
label: `${labels[index % 5]}-${index}`,
}));

const normal = options;
export const Default: Story<SelectProps> = (args) => {
const [size, setSize] = useState<SizeType>('middle');
const [disabled, setDisabled] = useState<boolean>(false);
const [bordered, setBordered] = useState<boolean>(true);
const [allowClear,setAllowClear] = useState<boolean>(false);
const [searchable, setSearchable] = useState<boolean>(false);
const selectRef = createRef<HTMLElement>();
return (
<>
<div style={{display:'flex'}}>

<div style={{ margin: 16 }}>
<h2>size</h2>
<Radio.Group value={size} onChange={(e) => setSize(e.target.value)}>
<Radio value="large"></Radio>
<Radio value="middle"></Radio>
<Radio value="small"></Radio>
</Radio.Group>
</div>
{/* <div style={{ margin: 16 }}>
<h2>multiple</h2>
<Radio.Group value={multiple} onChange={(e) => setMultiple(e.target.value)}>
<Radio value={false}>单选</Radio>
<Radio value>多选</Radio>
</Radio.Group>
</div> */}
<div style={{ margin: 16 }}>
<h2>disabled</h2>
<Radio.Group value={disabled} onChange={(e) => setDisabled(e.target.value)}>
<Radio value={false}>不禁用</Radio>
<Radio value>禁用</Radio>
</Radio.Group>
</div>
<div style={{ margin: 16 }}>
<h2>bordered</h2>
<Radio.Group value={bordered} onChange={(e) => setBordered(e.target.value)}>
<Radio value={false}>无边框</Radio>
<Radio value>有边框</Radio>
</Radio.Group>
</div>
<div style={{ margin: 16 }}>
<h2>allowClear</h2>
<Radio.Group value={allowClear} onChange={(e) => setAllowClear(e.target.value)}>
<Radio value={false}>不开启</Radio>
<Radio value>开启</Radio>
</Radio.Group>
</div>
<div style={{ margin: 16 }}>
<h2>searchable</h2>
<Radio.Group value={searchable} onChange={(e) => setSearchable(e.target.value)}>
<Radio value={false}>不开启</Radio>
<Radio value>开启</Radio>
</Radio.Group>
</div>

</div>
<div style={{display:'flex',justifyContent:'space-around',marginTop:50}}>
<div>
<h1>单选</h1>
<Select
ref={selectRef}
disabled={disabled}
bordered={bordered}
options={options}
allowClear={allowClear}
searchable={searchable}
size={size}
style={{ width: 190 }}
placeholder="请选择"
/>
</div>
<div>
<h1>多选</h1>
<Select
ref={selectRef}
disabled={disabled}
bordered={bordered}
multiple
searchable={searchable}
options={optionsWithoutGroup}
// autoWidth={false}
allowClear={allowClear}
size={size}
style={{ width: 190 }}
placeholder="请选择"
/>
</div>
</div>
</>
)
};
export const Search: Story<SelectProps> = (args) => <Select {...args} />;
Default.args = {
size: 'small',
style: { width: 140 },
placeholder: '请选择',
options: optionsWithoutGroup,
};

Search.args = {
// size: 'small',
searchable:true,
multiple:true,
placeholder: '请选择',
allowClear:true,
// allowCustomOption:true,
options: normal,
};
Loading

0 comments on commit 9dbc1ac

Please sign in to comment.