Skip to content

Commit

Permalink
fix(list-picker): optimization list-picker (#1561)
Browse files Browse the repository at this point in the history
  • Loading branch information
berber1016 committed Nov 30, 2021
1 parent 76b6fee commit a014a86
Show file tree
Hide file tree
Showing 21 changed files with 263 additions and 132 deletions.
11 changes: 8 additions & 3 deletions src/cascader/Cascader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,14 @@ export const Cascader: React.FC<CascaderProps> = ({
e.stopPropagation();
};

const renderTrigger = () => {
const triggerClick = () => !disabled && setVisible(!visible);
// trigger
const renderTrigger = (): React.ReactElement => {
if (typeof propsRenderTrigger === 'function') {
return propsRenderTrigger?.();
const node = propsRenderTrigger?.();
return React.cloneElement(node, {
onClick: triggerClick,
});
}
return (
<Trigger
Expand All @@ -90,7 +95,7 @@ export const Cascader: React.FC<CascaderProps> = ({
disabled={disabled}
allowClear={allowClear}
onClear={handleOnClear}
onClick={() => !disabled && setVisible(!visible)}
onClick={triggerClick}
onInputChange={(val) => {
isEmpty(val) && handleChange();
}}
Expand Down
1 change: 1 addition & 0 deletions src/cascader/interfance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export interface OptionProps extends ListOptionProps {
value: string;
childrens?: OptionProps[];
}

export interface TriggerProps extends Omit<InputButtonProps, 'value' | 'active'> {
value?: string;
}
64 changes: 31 additions & 33 deletions src/input/style/InputButton.less
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,51 @@
@input-btn-prefix-cls: ~'@{component-prefix}-input-btn';
@icon-prefix-cls: ~'@{component-prefix}-input__prefix';

@input-btn-input-prefix-cls: ~'@{component-prefix}-input-btn__input';
@input-btn-input-prefix-cls: ~'@{component-prefix}-input-btn';

.@{input-btn-prefix-cls} {
.@{input-btn-input-prefix-cls} {
input {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
background-color: @gray-0;
cursor: pointer;
.@{input-btn-input-prefix-cls} {
input {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
background-color: @gray-0;
cursor: pointer;

&:not([disabled])::placeholder {
color: @gray-5;
&:not([disabled])::placeholder {
color: @gray-5;
.text-body1();
}
&:disabled {
color: @gray-3;
background-color: @gray-0;
&::placeholder {
color: @gray-3;
.text-body1();
}
&:disabled {
&:hover {
color: @gray-3;
background-color: @gray-0;
&::placeholder {
color: @gray-3;
.text-body1();
}
&:hover {
color: @gray-3;
background-color: @gray-0;
&::placeholder {
color: @gray-3;
}
}
}
}

&:not([disabled]):hover {
color: @blue-3;
border: 1px solid @gray-2;
}
&:is([disabled]):hover {
color: @gray-3;
border: 1px solid @gray-2;
}
&:not([disabled]):hover {
color: @blue-3;
border: 1px solid @gray-2;
}
&:is([disabled]):hover {
color: @gray-3;
border: 1px solid @gray-2;
}

&:not([disabled]):focus {
border: 1px solid @gray-2;
outline: none;
}
&:not([disabled]):focus {
border: 1px solid @gray-2;
outline: none;
}

&__active,
&__hover {
&:hover {
Expand Down
2 changes: 1 addition & 1 deletion src/list-picker/Recent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface RecentProps {
}

const Recent: React.FC<RecentProps> & { isRecent: boolean } = (props) => {
const { max = 5, title } = props;
const { max = 5, title = '最近使用' } = props;
const context = useContext(ListContext);
const localStorageValue = window?.localStorage?.getItem(ITEM_KEY) || '[]';
const matchValue = JSON.parse(localStorageValue); // localStorage.getItem('__GIO_SELECTION_KEY')
Expand Down
43 changes: 40 additions & 3 deletions src/list-picker/Trigger.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,53 @@
import React from 'react';
import React, { useContext, useMemo } from 'react';
import Input from '../input';
import { ListContext } from '../list/context';
import { collectOptions } from '../list/util';
import WithRef from '../utils/withRef';
import { TriggerProps } from './interfance';

const Trigger: React.ForwardRefRenderFunction<HTMLInputElement, TriggerProps> = (props, ref) => {
const { value, placeholder, onClear, ...rest } = props;
const {
value,
title,
placeholder,
onClear,
children,
hidePrefix = false,
prefix: propPrefix,
suffix: propSuffix,
...rest
} = props;
const { options, setOptions, getOptionByValue, getLabelByValue } = useContext(ListContext);
setOptions?.(collectOptions(children));

const handleClear = (e: React.MouseEvent<Element, MouseEvent>) => {
onClear?.(e);
e.stopPropagation();
};

const prefix = useMemo(() => {
if (!hidePrefix) {
return propPrefix ?? getOptionByValue?.(value as string)?.prefix;
}
return undefined;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [options?.size, hidePrefix, propPrefix, value]);

const suffix = useMemo(
() => propSuffix ?? getOptionByValue?.(value as string)?.suffix,
// eslint-disable-next-line react-hooks/exhaustive-deps
[options?.size, hidePrefix, propPrefix, value]
);
return (
<Input.Button ref={ref} placeholder={placeholder} value={(value as string) ?? ''} onClear={handleClear} {...rest} />
<Input.Button
ref={ref}
prefix={prefix}
suffix={suffix}
placeholder={placeholder}
value={title ?? getLabelByValue?.(value as string)}
onClear={handleClear}
{...rest}
/>
);
};

Expand Down
11 changes: 4 additions & 7 deletions src/list-picker/demos/List-picker.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import React, { useState } from 'react';
import { Story, Meta } from '@storybook/react/types-6-0';
import '../style';
import { isEqual, uniqueId } from 'lodash';
// import { UserOutlined } from '@gio-design/icons';
import CheckboxItem from '../../list/inner/CheckboxItem';
import { OptionProps } from '../../list/interfance';
// import { uniqueId } from 'lodash';
// import { uniqBy, uniqueId } from 'lodash';
// import { AttributionPropertyOutlined, EditOutlined, ItemOutlined, TagOutlined, UserOutlined } from '@gio-design/icons';
import { ListPickerProps } from '../interfance';
import ListPicker from '../listPicker';
import SearchBar from '../../search-bar';
Expand Down Expand Up @@ -47,7 +47,7 @@ const createSingleOption = (index: number) => ({
const largeOptions = new Array(200).fill(0).map((v, i) => createOption(i));
const simpleLargeOptions = new Array(200).fill(0).map((v, i) => createSingleOption(i));
const Template: Story<ListPickerProps> = () => {
const [value, setValue] = useState<undefined | string>('banana');
const [value, setValue] = useState<undefined | string>('ziyi');
const [multipleValue, setMultipleValue] = useState<undefined | string[]>(undefined);
const [activeTab, setActiveTab] = useState('tab1');
const [search, setSearch] = useState('');
Expand Down Expand Up @@ -238,11 +238,7 @@ const Template: Story<ListPickerProps> = () => {
<Tab label="tab1" value="tab1">
<List.Selection>
{(context) => {
const isEqualValue = isEqual(
Array.from(context.options.values()).reduce((p, v) => [...p, v.value], []),
context.value
);
console.log(isEqualValue);
const isEqualValue = context.value.length === 2;
return (
<>
<CheckboxItem
Expand Down Expand Up @@ -370,6 +366,7 @@ const Template: Story<ListPickerProps> = () => {
<List.Selection options={largeOptions} />
</ListPicker>
</div>
<div className="demo-box" />
</div>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions src/list-picker/interfance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,5 @@ export interface TriggerProps extends Omit<InputButtonProps, 'value' | 'active'>
disabled?: boolean;
value?: string | React.ReactNode;
separator?: string;
[key: string]: any;
}
56 changes: 32 additions & 24 deletions src/list-picker/listPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect } from 'react';
import classNames from 'classnames';
import { isEqual, isNil, isString } from 'lodash';
import { isEqual, isNil } from 'lodash';
import { ListPickerProps } from './interfance';
import Popover from '../popover';
import Trigger from './Trigger';
Expand Down Expand Up @@ -42,29 +42,18 @@ const ListPicker: React.FC<ListPickerProps> = (props) => {
needConfim = model === 'multiple',

allowClear,
title: controlledTitle,
title,
triggerPrefix,
triggerSuffix,
hidePrefix = false,
maxWidth,
} = props;
const defaultPrefix = usePrefixCls(prefixCls);
const [visible, setVisible] = useControlledState(controlledVisible, false);
const [value, setValue] = useState(controlledValue || defaultValue);
const [title, setTitle] = useState<string | React.ReactNode>(controlledTitle);
const [titlePrefix, setTitlePrefix] = useState<string | React.ReactNode>(undefined);
const { options, setOptions, getLabelByValue, getOptionByValue, getOptionsByValue } = useCacheOptions();

// title仅跟随controlledValue变动
useEffect(() => {
setTitle(getLabelByValue(controlledValue, separator));
}, [controlledValue, getLabelByValue, separator, setTitle]);
// prefix
useEffect(() => {
if (model === 'single' && isString(controlledValue) && !hidePrefix) {
setTitlePrefix(triggerPrefix ?? getOptionByValue(controlledValue)?.prefix);
}
}, [controlledValue, getOptionByValue, hidePrefix, model, triggerPrefix]);
const [value, setValue] = useControlledState(controlledValue, defaultValue);
const { options, setOptions, getOptionByValue, getLabelByValue, getOptionsByValue } = useCacheOptions();

useEffect(() => {
setValue(controlledValue);
}, [controlledValue, setValue]);
Expand Down Expand Up @@ -101,27 +90,35 @@ const ListPicker: React.FC<ListPickerProps> = (props) => {
setValue(undefined);
onClear?.();
};
const triggerClick = () => !disabled && setVisible(!visible);
// trigger
const renderTrigger = (): React.ReactElement => {
if (typeof propsRenderTrigger === 'function') {
return propsRenderTrigger?.();
const node = propsRenderTrigger?.();
return React.cloneElement(node, {
onClick: triggerClick,
});
}
return (
<Trigger
size={size}
value={controlledTitle ?? title}
value={controlledValue}
style={style}
className={className}
maxWidth={maxWidth}
disabled={disabled}
placeholder={placeholder}
suffix={triggerSuffix}
prefix={titlePrefix}
prefix={triggerPrefix}
allowClear={allowClear}
onClear={clearInput}
separator={separator}
onClick={() => !disabled && setVisible(!visible)}
/>
onClick={triggerClick}
title={title}
hidePrefix={hidePrefix}
>
{children}
</Trigger>
);
};
// render
Expand All @@ -138,9 +135,19 @@ const ListPicker: React.FC<ListPickerProps> = (props) => {
);

return (
<ListContext.Provider value={{ value, model, onChange: handleChange, options, setOptions }}>
<ListContext.Provider
value={{
value,
model,
onChange: handleChange,
options,
setOptions,
getOptionByValue,
getOptionsByValue,
getLabelByValue,
}}
>
<Popover
distoryOnHide={false}
disabled={disabled}
content={renderOverlay()}
trigger={trigger}
Expand All @@ -150,6 +157,7 @@ const ListPicker: React.FC<ListPickerProps> = (props) => {
overlayClassName={classNames(`${defaultPrefix}--content`, overlayClassName)}
placement={placement}
overlayStyle={overlayStyle}
strategy="fixed"
>
{renderTrigger()}
</Popover>
Expand Down
6 changes: 5 additions & 1 deletion src/list/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const selectStatus = (value?: string, values?: string | string[]) => {
return undefined;
};

export const List = WithRef<HTMLDivElement, ListProps>((props, ref?) => {
export const InnerList = WithRef<HTMLDivElement, ListProps>((props, ref?) => {
const {
id,
title,
Expand Down Expand Up @@ -185,4 +185,8 @@ export const List = WithRef<HTMLDivElement, ListProps>((props, ref?) => {
</ListContext.Provider>
);
});

const List: React.ForwardRefExoticComponent<ListProps & React.RefAttributes<HTMLDivElement>> & { isList?: boolean } =
InnerList;
List.isList = true;
export default List;
4 changes: 2 additions & 2 deletions src/list/Selection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import useCacheOptions from './hooks/useCacheOptions';

type nodeType = React.ReactElement<ListProps> & { type: { isGIOList: boolean; isRecent: boolean } };

const Selection: React.FC<SelectionProps> = (props) => {
const Selection: React.FC<SelectionProps> & { isSelection?: boolean } = (props) => {
const { className, style, options = [], children, ...rest } = props;
const prefixCls = `${usePrefixCls(PREFIX)}--selection`;
const isSelection = options?.every((val) => 'groupId' in val) ?? false;
Expand Down Expand Up @@ -108,5 +108,5 @@ const Selection: React.FC<SelectionProps> = (props) => {
);
return selectionProvider(renderNormal);
};

Selection.isSelection = true;
export default Selection;

1 comment on commit a014a86

@vercel
Copy link

@vercel vercel bot commented on a014a86 Nov 30, 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.