Skip to content

Commit

Permalink
feat(list-picker): list-picker multiple mode (#1487)
Browse files Browse the repository at this point in the history
Co-authored-by: shiliqian <shiliqian@growingio.com>
  • Loading branch information
berber1016 and shiliqian authored Nov 19, 2021
1 parent a499a2d commit f4666bc
Show file tree
Hide file tree
Showing 17 changed files with 81 additions and 55 deletions.
8 changes: 4 additions & 4 deletions src/cascader/Cascader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const Cascader: React.FC<CascaderProps> = (props) => {
const {
options,
value: controlledValue,
defaultValue = '',
defaultValue = undefined,
visible: controlledVisible,
onChange,
prefixCls = 'cascader--new',
Expand All @@ -37,7 +37,7 @@ const Cascader: React.FC<CascaderProps> = (props) => {
...rest
} = props;
const defaultPrefixCls = usePrefixCls(prefixCls);
const [value, setSelectValue] = useControlledState(controlledValue, defaultValue);
const [value, setValue] = useControlledState(controlledValue, defaultValue);
const [visible, setVisible] = useControlledState(controlledVisible, false);
const [title, setTitle] = useState('');
const cache = useChacheOptions();
Expand All @@ -55,7 +55,7 @@ const Cascader: React.FC<CascaderProps> = (props) => {

const handleChange = (val?: string | string[]) => {
onChange?.(val);
setSelectValue((val as string) ?? '');
setValue(val as string);
setVisible(false);
};

Expand All @@ -77,7 +77,7 @@ const Cascader: React.FC<CascaderProps> = (props) => {
disabled={disabled}
onClear={handleOnClear}
onInputChange={(val) => {
isEmpty(val) && handleChange('');
isEmpty(val) && handleChange();
}}
{...triggerProps}
/>
Expand Down
1 change: 0 additions & 1 deletion src/input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => {
() => (customizeSuffix ? <div className={suffixCls}>{customizeSuffix}</div> : null),
[suffixCls, customizeSuffix]
);

return (
<span className={inputClass} {...rest} style={style}>
{prefix}
Expand Down
1 change: 1 addition & 0 deletions src/list-picker/Trigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const Trigger: React.FC<TriggerProps> = (props) => {
onClear?.(e);
e.stopPropagation();
};

return <Input.Button placeholder={placeholder} value={value as string} onClear={handleClear} {...rest} />;
};

Expand Down
32 changes: 32 additions & 0 deletions src/list-picker/demos/List-picker.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default {

const Template: Story<ListPickerProps> = () => {
const [value, setValue] = useState('banana2');
const [multipleValue, setMultipleValue] = useState(['ziyi']);
const [activeTab, setActiveTab] = useState('tab1');
const [search, setSearch] = useState('');
const onChange = (val?: string, opt?: OptionProps | OptionProps[]) => {
Expand Down Expand Up @@ -149,6 +150,37 @@ const Template: Story<ListPickerProps> = () => {
/>
</ListPicker>
</div>
<div className="demo-box">
<ListPicker
value={multipleValue}
onChange={(val) => {
console.log('multiple onChange 并不会触发', val);
}}
onConfim={(val) => {
console.log(val);
setMultipleValue(val as string[]);
}}
overlayStyle={{ width: '240px' }}
model="multiple"
onClear={() => {
setValue('');
}}
placeholder="请选择"
>
<SearchBar
size="small"
style={{ width: '100%' }}
placeholder="请搜索名称"
onSearch={(val: string) => setSearch(val)}
/>
<List.Selection
options={[
{ label: '子一', value: 'ziyi' },
{ label: '子二', value: 'zier' },
]}
/>
</ListPicker>
</div>
</div>
</div>
);
Expand Down
20 changes: 7 additions & 13 deletions src/list-picker/listPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect, useState } from 'react';
import classNames from 'classnames';
import { isArray } from 'lodash';
import { ListPickerProps } from './interfance';
import Popover from '../popover';
import Trigger from './Trigger';
Expand Down Expand Up @@ -32,12 +31,12 @@ const ListPicker: React.FC<ListPickerProps> = (props) => {
overlayStyle,
children,
onConfim,
needConfim = false,
confimText = '确定',
separator = '',
className,
style,
model,
model = 'simple',
needConfim = model === 'multiple',
} = props;
const defaultPrefix = usePrefixCls(prefixCls);
const [visible, setVisible] = useControlledState(controlledVisible, false);
Expand All @@ -46,20 +45,16 @@ const ListPicker: React.FC<ListPickerProps> = (props) => {

const { options, setOptions, getLabelByValue, getOptionsByValue } = useCacheOptions();

// title仅跟随controlledValue变动
useEffect(() => {
setValue(controlledValue);
}, [controlledValue]);

useEffect(() => {
console.log('getLabelByValue(value, separator)', getLabelByValue(value, separator));
setTitle(getLabelByValue(value, separator));
}, [value, getLabelByValue, separator]);
setTitle(getLabelByValue(controlledValue, separator));
}, [controlledValue, getLabelByValue, separator]);

useEffect(() => {
if (!needConfim) {
setValue(controlledValue);
}
}, [controlledValue, needConfim]);
}, [controlledValue, needConfim, setValue]);
// methods
const handVisibleChange = (vis: boolean) => {
setVisible(vis);
Expand All @@ -72,7 +67,6 @@ const ListPicker: React.FC<ListPickerProps> = (props) => {

const handleChange = (val?: string | string[], opts?: OptionProps | OptionProps[]) => {
setValue(val);
setTitle(!isArray(opts) ? opts?.label : '');
if (!needConfim) {
onChange?.(val, opts);
}
Expand Down Expand Up @@ -116,7 +110,7 @@ const ListPicker: React.FC<ListPickerProps> = (props) => {
);

return (
<ListContext.Provider value={{ value, onChange: handleChange, options, setOptions }}>
<ListContext.Provider value={{ value, model, onChange: handleChange, options, setOptions }}>
<Popover
content={renderOverlay()}
trigger={trigger}
Expand Down
4 changes: 2 additions & 2 deletions src/list-picker/style/index.less
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
@import '../../stylesheet/variables/index.less';
@import '~@gio-design/utils/lib/less/mixins.less';
@list-picker-prefix-cls: ~'@{component-prefix}-list-picker--new';
@selection-prefix-cls: ~'gio-list-new--selection';
@list-prefix-cls: ~'gio-list-new';
@selection-prefix-cls: ~'@{component-prefix}-list-new--selection';
@list-prefix-cls: ~'@{component-prefix}-list-new';
.@{list-picker-prefix-cls} {
box-sizing: border-box;
min-width: 120px;
Expand Down
1 change: 0 additions & 1 deletion src/list-picker/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export const getFlattenOptions = (
});
});
const flattenOption: { groupName: string; groupId: string; options: OptionProps[] }[] = [];
console.log('groupMap', groupMap);
groupMap.forEach((value) => {
flattenOption.push({
groupName: value.label,
Expand Down
35 changes: 20 additions & 15 deletions src/list/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,19 @@ const List: React.ForwardRefRenderFunction<HTMLDivElement, ListProps> & {
const [collapse, setCollapse] = useState(initCollapse);

// value and onChange
const { value: contextValue, onChange: contextOnChange, setOptions: contextSetOptions } = useContext(ListContext);
const {
value: contextValue,
model: contextModel,
onChange: contextOnChange,
setOptions: contextSetOptions,
} = useContext(ListContext);
const mergedModel = contextModel ?? model;
const { value, onChange } = useValue(
controlledValue,
controlledOnChange,
contextValue,
contextOnChange,
isMultipe(model)
isMultipe(mergedModel)
);

const cache = useCacheOptions();
Expand All @@ -73,22 +79,21 @@ const List: React.ForwardRefRenderFunction<HTMLDivElement, ListProps> & {
}, [mergedOptions, setOptions]);

const renderOptions = initOptions?.length ? initOptions : React.Children.toArray(children);
console.log('renderOptions', renderOptions);
const childrens = renderOptions.slice(0, collapse);
const isNeedCollapse = useMemo(() => renderOptions?.length > collapse, [renderOptions, collapse]);
const handleClick = (val: string, opt?: OptionProps) => {
const handleClick = (val: string) => {
// multiple
if (isArray(value)) {
const resultValue = indexOf(value, val) !== -1 ? difference(value, [val]) : [...value, val];
onChange?.(resultValue, opt ?? cache.getOptionsByValue(resultValue));
onChange?.(resultValue, cache.getOptionsByValue(resultValue));
}
// cascader
else if (isCascader(model)) {
onChange?.(val, opt);
else if (isCascader(mergedModel)) {
onChange?.(val, cache.getOptionsByValue(val));
}
// normal
else if (value !== val) {
onChange?.(val, opt ?? cache.getOptionsByValue(val));
onChange?.(val, cache.getOptionsByValue(val));
}
};

Expand Down Expand Up @@ -119,8 +124,8 @@ const List: React.ForwardRefRenderFunction<HTMLDivElement, ListProps> & {
prefix={prefix?.(option)}
suffix={suffix?.(option)}
disabled={option?.disabled ?? disabled}
isMultiple={isMultipe(model)}
isCascader={isCascader(model)}
isMultiple={isMultipe(mergedModel)}
isCascader={isCascader(mergedModel)}
selectValue={value}
selected={selectStatus(option?.value, value)}
onClick={handleClick}
Expand All @@ -143,19 +148,19 @@ const List: React.ForwardRefRenderFunction<HTMLDivElement, ListProps> & {
} = node;

const item = { label: node?.props?.label, value: node?.props?.value } as OptionProps;
console.log('item', item);

return React.cloneElement(node, {
...rest,
disabled: itemDisabled ?? disabled,
prefix: itemPrefix ?? prefix?.(item),
suffix: itemSuffix ?? suffix?.(item),
isMultiple: isMultipe(model),
isCascader: isCascader(model),
isMultiple: isMultipe(mergedModel),
isCascader: isCascader(mergedModel),
selectValue: value,
selected: selectStatus(item.value, value),
onClick: (val: string, opt: OptionProps) => {
onClick: (val: string) => {
handleClick(val);
onClick?.(val, opt);
onClick?.(val);
},
});
}
Expand Down
2 changes: 0 additions & 2 deletions src/list/Selection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ const Selection: React.FC<SelectionProps> & { isSelection: boolean } = (props) =
const prefixCls = `${usePrefixCls(PREFIX)}--selection`;
const childrens = React.Children.toArray(children);
const isSelection = options?.every((val) => 'groupId' in val) ?? false;
console.log('isSelection', isSelection);
const selectionOptions: { groupId: string; groupName: string; options: OptionProps[] }[] | OptionProps[] = useMemo(
() => getFlattenOptions(options, isSelection),
[isSelection, options]
);
console.log('selectionOptions', selectionOptions);
// var cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }],
// result = cars.reduce(function (r, a) {
// r[a.make] = r[a.make] || [];
Expand Down
4 changes: 3 additions & 1 deletion src/list/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { OptionProps, ListProps } from './interfance';

interface ListContextProps {
value?: string | string[];
model?: 'simple' | 'cascader' | 'multiple';
onChange?: (value?: string | string[], options?: OptionProps | OptionProps[]) => void;
options?: Map<string, OptionProps>;
setOptions?: (options?: OptionProps[]) => void;
}
const defaultList = {
const defaultList: ListContextProps = {
value: '',
model: undefined,
onChange: noop,
setOptions: noop,
};
Expand Down
1 change: 0 additions & 1 deletion src/list/hooks/useCacheOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const useCacheOptions = () => {
: getOptionByValue(optValue);

const getLabelByValue = (val?: string | string[], separator = '') => {
console.log('val', val, separator);
if (val === '' || typeof val === 'undefined') {
return '';
}
Expand Down
5 changes: 1 addition & 4 deletions src/list/inner/CascaderItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ const CascaderItem: React.ForwardRefRenderFunction<

// list
const prefixClsItem = `${prefixCls}--item`;
const onClick = () =>
!disabled
? propsOnClick?.(generateString(value, selectParent), selectParent?.[0] ?? { label: '', value: '' })
: noop;
const onClick = () => (!disabled ? propsOnClick?.(generateString(value, selectParent)) : noop);

const content = () => {
if (!isEmpty(childrens)) {
Expand Down
2 changes: 1 addition & 1 deletion src/list/inner/ChckboxItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const CheckboxItem: React.ForwardRefRenderFunction<
disabled={disabled}
onClick={(e) => {
if (!disabled) {
onClick?.(value, { label: '', value: '' });
onClick?.(value);
e?.stopPropagation();
}
}}
Expand Down
2 changes: 1 addition & 1 deletion src/list/inner/baseItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const BaseItem: React.ForwardRefRenderFunction<
aria-hidden="true"
ref={ref}
{...rest}
onClick={() => (!disabled ? onClick?.(value, { label: content as string, value }) : noop)}
onClick={() => (!disabled ? onClick?.(value) : noop)}
>
{contentRender?.(contentElement)}
</li>
Expand Down
2 changes: 1 addition & 1 deletion src/list/interfance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,5 @@ export interface BaseItemProps extends Pick<OptionProps, 'value' | 'disabled'> {
selected?: boolean;
prefix?: string | React.ReactNode;
suffix?: string | React.ReactNode;
onClick?: (value: string, option: OptionProps) => void;
onClick?: (value: string) => void;
}
4 changes: 2 additions & 2 deletions src/list/style/index.less
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
@import '../../stylesheet/variables/index.less';
@import '~@gio-design/utils/lib/less/mixins.less';
@list-prefix-cls: ~'@{component-prefix}-list-new';
@cascader-prefix-cls: ~'gio-cascader--new';
@cascader-content-prefix-cls: ~'gio-cascader--new--cascader--content';
@cascader-prefix-cls: ~'@{component-prefix}-cascader--new';
@cascader-content-prefix-cls: ~'@{cascader-prefix-cls}--cascader--content';

.@{cascader-content-prefix-cls} {
&[data-popper-placement^='top'] {
Expand Down
Loading

1 comment on commit f4666bc

@vercel
Copy link

@vercel vercel bot commented on f4666bc Nov 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.