Skip to content

Commit

Permalink
feat(list-picker): add list-picker (#1461)
Browse files Browse the repository at this point in the history
  • Loading branch information
berber1016 committed Nov 16, 2021
1 parent 1e8458a commit c5bc076
Show file tree
Hide file tree
Showing 42 changed files with 1,181 additions and 323 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
"styled-css",
"sugarss",
"svelte"
]
],
"typescript.tsdk": "node_modules/typescript/lib"

//stylelint -----settings end-----
}
63 changes: 30 additions & 33 deletions src/cascader/Cascader.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import classNames from 'classnames';
import { isEmpty } from 'lodash';
import React from 'react';
import List from '../list';
import CascaderItem from '../list/inner/CascaderItem';
import React, { useState } from 'react';
import List, { OptionProps } from '../list';
import Popover from '../popover';
import usePrefixCls from '../utils/hooks/use-prefix-cls';
import useControlledState from '../utils/hooks/useControlledState';
import { CascaderProps, CascaderItemProps } from './interfance';
import { CascaderProps } from './interfance';
import { getLabelByValue } from './util';
import Trigger from './Trigger';
import { useCacheOptions } from './utils';

const Cascader: React.FC<CascaderProps> = (props) => {
const {
Expand All @@ -34,20 +33,22 @@ const Cascader: React.FC<CascaderProps> = (props) => {
} = props;
const [value, setSelectValue] = useControlledState(controlledValue, defaultValue);
const [visible, setVisible] = useControlledState(controlledVisible, false);
const [selectedTitle, setSelectedTitle] = useState(undefined);
const defaultPrefixCls = usePrefixCls(prefixCls);
const prefixIcon = prefix?.(undefined);
const suffixIcon = suffix?.(undefined);
const { setCacheOptions, getOptionByValue } = useCacheOptions();
setCacheOptions(options);
// 这里实现的不好

const handVisibleChange = (vis: boolean) => {
setVisible(vis);
onVisibleChange?.(vis);
};

const handleChange = (val?: string | number | (string | number)[]) => {
onChange?.(val as string);
const handleChange = (val?: string | string[], opts?: OptionProps | OptionProps[]) => {
onChange?.(val);
setSelectValue((val as string) ?? '');
setSelectedTitle(getLabelByValue(val, opts, separator));

setVisible(false);
};

Expand All @@ -64,48 +65,44 @@ const Cascader: React.FC<CascaderProps> = (props) => {
onClick={() => setVisible(!visible)}
>
<Trigger
value={value}
value={selectedTitle}
size={size}
prefix={prefixIcon}
suffix={suffixIcon}
disabled={disabled}
separator={separator}
onClear={handleOnClear}
onInputChange={(val) => {
isEmpty(val) && handleChange('');
}}
getOptionByValue={getOptionByValue}
{...triggerProps}
/>
</div>
);
const renderItems = () =>
options?.map((option: CascaderItemProps) => {
const { childrens = [], ...optionRest } = option;
if (!isEmpty(childrens)) {
return (
<CascaderItem {...optionRest}>
<List isCascader className={`${defaultPrefixCls}--list`} isMultiple={false}>
{childrens.map((child) => (
<CascaderItem {...child} />
))}
</List>
</CascaderItem>
);
}
return <CascaderItem {...optionRest} />;
});
// const renderItems = () =>
// options?.map((option: CascaderItemProps) => {
// const { childrens = [], ...optionRest } = option;
// if (!isEmpty(childrens)) {
// return (
// <Item isCascader {...optionRest}>
// <List model="cascader" className={`${defaultPrefixCls}--list`}>
// {childrens.map((child) => (
// <Item isCascader {...child} />
// ))}
// </List>
// </Item>
// );
// }
// return <Item {...optionRest} />;
// });
const renderOverlay = () => (
<List
{...rest}
options={options}
className={`${defaultPrefixCls}--list`}
isCascader
isMultiple={false}
model="cascader"
value={value}
onChange={handleChange}
>
{renderItems()}
</List>
/>
);

return (
Expand Down
9 changes: 2 additions & 7 deletions src/cascader/Trigger.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { isEmpty } from 'lodash';
import React from 'react';
import Input from '../input';
import { TriggerProps } from './interfance';

const Trigger: React.FC<TriggerProps> = (props) => {
const { value, placeholder, getOptionByValue, separator, ...rest } = props;
const name = value
?.split('.')
?.map((val) => getOptionByValue?.(val)?.label)
?.join(separator);
return <Input.Button placeholder={placeholder} value={isEmpty(value) ? undefined : name?.toString()} {...rest} />;
const { value, placeholder, ...rest } = props;
return <Input.Button placeholder={placeholder} value={value} {...rest} />;
};

export default Trigger;
10 changes: 8 additions & 2 deletions src/cascader/demos/Cascader.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,20 @@ const Template: Story<CascaderProps> = (props) => {
<Cascader options={options} triggerProps={{ placeholder: '请选择' }} size="normal" {...props} />
</div>
<div className="demo-box">
<Cascader disabled options={options} triggerProps={{ placeholder: '请选择' }} size="normal" {...props} />
<Cascader
disabled
options={options}
triggerProps={{ placeholder: '请选择', allowClear: false }}
size="normal"
{...props}
/>
</div>
<h3>自定义list,trigger宽度</h3>
<div className="demo-box">
<Cascader
options={options}
overlayStyle={{ width: 240 }}
triggerProps={{ placeholder: '请选择', style: { width: 240, textAlign: 'left' } }}
triggerProps={{ placeholder: '请选择', allowClear: false, style: { width: 240, textAlign: 'left' } }}
size="small"
{...props}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/cascader/demos/style.less
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
width: 240px;
// height: 50px;
margin: 20px;
padding: 8px;
// padding: 8px;
border: 0.5px dashed #dcdfed;
border-radius: 4px;
}
24 changes: 9 additions & 15 deletions src/cascader/interfance.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,31 @@
import { InputButtonProps } from '../input';
import { BaseItemProps, ListProps } from '../list/interfance';
import { ListProps, OptionProps as ListOptionProps } from '../list/interfance';
import { Placement } from '../popover/interface';

export interface CascaderProps extends Omit<ListProps, 'options' | 'onChange' | 'value'> {
value?: string;
defaultValue?: string;
options: CascaderItemProps[];
options: OptionProps[];
size?: 'small' | 'normal';
/**
多级文本的连接字符
*/
separator?: string;

visible?: boolean;
onVisibleChange?: (visible: boolean) => void;
prefixCls?: string;
triggerProps: Omit<TriggerProps, 'prefixCls' | 'onInputChange' | 'disabled' | 'getOptionByValue' | 'separator'>;
onChange?: (value?: string) => void;
onChange?: (val?: string | string[], options?: OptionProps | OptionProps[]) => void;
getContainer?: (node: HTMLElement) => HTMLElement;
overlayStyle?: React.CSSProperties;
placement?: Placement;
}

export interface TriggerProps extends Omit<InputButtonProps, 'value' | 'active'> {
value?: string;
separator?: string;
getOptionByValue: (optValue?: string) => CascaderItemProps | undefined;
}

export interface CascaderItemProps extends BaseItemProps {
export interface OptionProps extends ListOptionProps {
label: string;
value: string;
selectValue?: string;
selectedParent?: string[];
onChange?: (value: string) => void;
childrens?: CascaderItemProps[];
childrens?: OptionProps[];
}
export interface TriggerProps extends Omit<InputButtonProps, 'value' | 'active'> {
value?: string;
}
2 changes: 1 addition & 1 deletion src/cascader/style/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
max-width: 320px;
height: auto;
max-height: 520px;
padding: 12px 8px !important;
// padding: 12px 8px !important;
overflow-y: auto;
background: @gray-0;
border: 1px solid #dfe4ee;
Expand Down
16 changes: 16 additions & 0 deletions src/cascader/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { OptionProps } from '../list/interfance';

export const getLabelByValue = (val?: string | string[], opts?: OptionProps | OptionProps[], separator = '') =>
val?.includes('.')
? (val as any)
?.split('.')
?.reduce(
(prev: string[], curr: string) => [...prev, (opts as OptionProps[]).find((o) => o.value === curr)?.label],
[]
)
?.join(separator)
: (opts as OptionProps).label;

export default {
getLabelByValue,
};
41 changes: 0 additions & 41 deletions src/cascader/utils.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/legacy/select/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ export interface SelectorProps {
deleteValue: (value: React.ReactText[], v: string | number) => void;
arrowComponent?: React.ReactElement | undefined;
closeComponent?: React.ReactElement | undefined;
[key: string]: any
[key: string]: any;
}

export interface VirtualListProps {
Expand Down Expand Up @@ -294,6 +294,6 @@ export interface OptionsListProps {
height?: number | undefined;
itemHeight?: number | undefined;
activeIndex: number;
setActiveIndex: React.Dispatch<React.SetStateAction<number>>
onOptionListKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void
setActiveIndex: React.Dispatch<React.SetStateAction<number>>;
onOptionListKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void;
}
30 changes: 30 additions & 0 deletions src/list-picker/StaticListPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import List, { OptionProps } from '../list';
import { SelectionProps, StaticListPickerProps } from './interfance';

const StaticListPicker: React.FC<StaticListPickerProps & { needConfim?: boolean }> = (props) => {
const { children, model, isSelection, options, ...rest } = props;
// children has filter options, custom filter model such as :search | select | tab | other
return (
<>
{children}
{isSelection ? (
<List.Selection>
{(options as SelectionProps[]).map((option: SelectionProps) => (
<List
{...rest}
id={option.selectionValue}
title={option.selectionTitle}
options={option.options}
model={model}
/>
))}
</List.Selection>
) : (
<List {...rest} options={options as OptionProps[]} model={model} />
)}
</>
);
};

export default StaticListPicker;
14 changes: 14 additions & 0 deletions src/list-picker/Trigger.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import Input from '../input';
import { TriggerProps } from './interfance';

const Trigger: React.FC<TriggerProps> = (props) => {
const { value, placeholder, onClear, ...rest } = props;
const handleClear = (e: React.MouseEvent<Element, MouseEvent>) => {
onClear?.(e);
e.stopPropagation();
};
return <Input.Button placeholder={placeholder} value={value} onClear={handleClear} {...rest} />;
};

export default Trigger;

1 comment on commit c5bc076

@vercel
Copy link

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