Skip to content

Commit

Permalink
feat(listpicker): add list-picker demo (#1474)
Browse files Browse the repository at this point in the history
Co-authored-by: zhuzilv <61216850+zhuzilv@users.noreply.github.com>
  • Loading branch information
berber1016 and zhuzilv committed Nov 17, 2021
1 parent cfe3bf0 commit 00b89f7
Show file tree
Hide file tree
Showing 15 changed files with 266 additions and 49 deletions.
5 changes: 2 additions & 3 deletions src/cascader/style/index.less
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@import '../../stylesheet/variables/index.less';

@import '~@gio-design/utils/lib/less/mixins.less';
@list-prefix-cls: ~'@{component-prefix}-cascader-new';

.@{list-prefix-cls} {
Expand All @@ -12,8 +12,7 @@
max-width: 320px;
height: auto;
max-height: 520px;
// padding: 12px 8px !important;
overflow-y: auto;
.scrollbar();
background: @gray-0;
border: 1px solid #dfe4ee;
border-radius: 8px;
Expand Down
11 changes: 10 additions & 1 deletion src/list-picker/StaticListPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@ 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;
const { children, model, isSelection, options, empty, ...rest } = props;
// children has filter options, custom filter model such as :search | select | tab | other

if (!options.length) {
return (
<>
{children}
{empty?.()}
</>
);
}
return (
<>
{children}
Expand Down
153 changes: 150 additions & 3 deletions src/list-picker/demos/List-picker.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
import React, { useMemo, useState } from 'react';
import { Story, Meta } from '@storybook/react/types-6-0';
import '../style';
import { groupBy, uniqBy } from 'lodash';
import { AddCircleOutlined } from '@gio-design/icons';
import { groupBy, uniqBy, uniqueId } from 'lodash';
import {
AddCircleOutlined,
AttributionPropertyOutlined,
EditOutlined,
ItemOutlined,
TagOutlined,
UserOutlined,
} from '@gio-design/icons';
import { ListPickerProps, OptionProps } from '../interfance';
import ListPicker from '../listPicker';
import SearchBar from '../../search-bar';
import { Tab } from '../../tabs';
import { Tabs } from '../..';
import Page from '../../page';

export default {
title: 'Upgraded/ListPicker',
component: ListPicker,
} as Meta;
const groupIds = ['custom', 'virtual', 'tag', 'visible'];
const groupNames = ['埋点', '虚拟', '标签', '属性'];
const selectionValues = ['all', 'apple', 'banana'];
const selectionNames = ['全局', '苹果', '香蕉'];
const createOption = (index: number) => ({
label: uniqueId(`label-${index.toString()}`),
value: uniqueId(`value-${index.toString()}`),
groupId: groupIds[Math.floor(index % 4)],
groupName: groupNames[Math.floor(index % 4)],
selectionValue: selectionValues[Math.floor(index % 3)],
selectionTitle: selectionNames[Math.floor(index % 3)],
});
const largeOptions = new Array(200).fill(0).map((v, i) => createOption(i));
const options = [
{
label: '访问',
Expand Down Expand Up @@ -356,7 +377,133 @@ const Template: Story<ListPickerProps> = () => {
);
};
export const Default = Template.bind({});

Default.args = {
searchPlaceholder: '请输入',
};

const EventPickerTemplate: Story<ListPickerProps> = () => {
const [value, setValue] = useState(undefined);
const [selectedOption, setSelectedOption] = useState(undefined);
const [activeTab, setActiveTab] = useState('All');
const [search, setSearch] = useState('');
const tabs = [{ label: '全部', value: 'All' }].concat(
uniqBy(
largeOptions.reduce((prev, curr) => [...prev, { label: curr?.groupName, value: curr.groupId }], []),
'value'
)
);

const GroupOptions = useMemo(
() =>
Object.assign(
groupBy(
largeOptions?.filter((option) => activeTab === option.groupId),
'groupId'
),
{ All: largeOptions }
),
[activeTab]
);
const searchOptions = useMemo(
() => (GroupOptions[activeTab] ?? []).filter((o: OptionProps) => o.label.includes(search)),
[GroupOptions, activeTab, search]
);
const onChange = (val?: string | string[], opt?: OptionProps | OptionProps[]) => {
console.log('onChange执行', val, opt);
setValue(val);
setSelectedOption(opt);
};
const searchEmptyRender = () => (
<div style={{ width: '100%' }}>
<Page type="noResult" size="small" style={{ margin: '0 auto', padding: '40px 0px' }} />
</div>
);
// ['custom', 'virtual', 'tag', 'visible']
const renderPrefix = (option: OptionProps) => {
switch (option?.groupId) {
case 'custom':
return <UserOutlined size="14px" />;
case 'virtual':
return <AttributionPropertyOutlined size="14px" />;
case 'tag':
return <TagOutlined size="14px" />;
case 'visible':
return <ItemOutlined size="14px" />;
default:
return <EditOutlined size="14px" />;
}
};
const renderPreview = (opt: OptionProps) => (
<div
style={{
width: '320px',
padding: '8px',
height: '150px',
background: 'white',
border: '1px solid #DFE4EE',
boxShadow: '0px 8px 24px rgba(36, 46, 89, 0.1)',
borderRadius: '8px',
}}
>
<h2>{opt.label}</h2>
</div>
);
return (
<div>
<h3> event Picker Demo</h3>
<ListPicker
value={value}
options={searchOptions}
style={{ width: '320px' }}
size="small"
onChange={onChange}
showPreview
previewRender={renderPreview}
onClear={() => {
setValue('');
}}
triggerProps={{
allowClear: false,
prefix: renderPrefix(selectedOption),
}}
prefix={renderPrefix}
empty={!searchOptions.length ? searchEmptyRender : undefined}
placeholder="请选择"
>
<SearchBar style={{ width: '100%' }} placeholder="搜索名称" onSearch={(val: string) => setSearch(val)} />
<div style={{ margin: '8px 0px' }}>
<Tabs
value={activeTab}
defaultValue="fruits"
onChange={(key: string) => {
setActiveTab(key);
}}
>
{tabs.map((tab) => (
<Tab label={tab.label} value={tab.value} />
))}
</Tabs>
</div>
</ListPicker>
<h3>options empty</h3>
<ListPicker value={value} options={[]} style={{ width: '320px' }} size="small" placeholder="请选择">
<SearchBar style={{ width: '100%' }} placeholder="搜索名称" onSearch={(val: string) => setSearch(val)} />
<div style={{ margin: '8px 0px' }}>
<Tabs
value={activeTab}
defaultValue="fruits"
onChange={(key: string) => {
setActiveTab(key);
}}
>
{tabs.map((tab) => (
<Tab label={tab.label} value={tab.value} />
))}
</Tabs>
</div>
</ListPicker>
</div>
);
};
export const EventPicker = EventPickerTemplate.bind({});
EventPicker.args = {};
1 change: 1 addition & 0 deletions src/list-picker/demos/List-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default function ListPage() {
<Subheading>{formatMessage({ defaultMessage: '基本样式' })}</Subheading>
<Canvas>
<Story id="components-list-picker--default" />
<Story id="components-list-picker--eventPicker" />
</Canvas>

<ArgsTable of={ListPicker} />
Expand Down
3 changes: 2 additions & 1 deletion src/list-picker/interfance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface ListPickerProps extends Omit<ListProps, 'title' | 'id'> {
*/
triggerProps?: Pick<
InputButtonProps,
'disabled' | 'className' | 'style' | 'allowClear' | 'hidePrefix' | 'maxWidth' | 'value'
'disabled' | 'className' | 'style' | 'allowClear' | 'hidePrefix' | 'maxWidth' | 'value' | 'prefix' | 'suffix'
>;
/**
* custom trigger render
Expand All @@ -38,6 +38,7 @@ export interface ListPickerProps extends Omit<ListProps, 'title' | 'id'> {
onConfim?: (value: string | string[] | undefined, options?: OptionProps | OptionProps[]) => void;
placement?: Placement;
overlayStyle?: React.CSSProperties;
empty?: () => React.ReactNode;
}
export interface OptionProps extends ListOptionProps {
selectionValue?: string;
Expand Down
11 changes: 10 additions & 1 deletion src/list-picker/listPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useEffect, useMemo, useState } from 'react';
import classNames from 'classnames';
import { ListPickerProps } from './interfance';
import Popover from '../popover';
import Trigger from './Trigger';
Expand All @@ -9,6 +10,9 @@ import { OptionProps } from '../list/interfance';
import { getFlattenOptions, getLabelByValue } from './util';
import Button from '../button';
import { useCacheOptions } from '../list/util';
import Page from '../page';

const defaultEmpty = () => <Page type="noData" size="small" style={{ margin: '0 auto', padding: '40px 0px' }} />;

const ListPicker: React.FC<ListPickerProps> = (props) => {
const {
Expand All @@ -34,6 +38,10 @@ const ListPicker: React.FC<ListPickerProps> = (props) => {
needConfim = model === 'multiple',
confimText = '确定',
separator,
empty = defaultEmpty,
className,
style,

...otherProps // list props
} = props;
const defaultPrefix = usePrefixCls(prefixCls);
Expand Down Expand Up @@ -99,14 +107,15 @@ const ListPicker: React.FC<ListPickerProps> = (props) => {

// render
const renderOverlay = () => (
<div className={defaultPrefix}>
<div className={classNames(defaultPrefix, className)} style={style}>
<StaticListPicker
value={value}
onChange={handleChange}
options={selections}
isSelection={isSelection}
model={model}
disabled={disabled}
empty={empty}
{...otherProps}
>
{children}
Expand Down
6 changes: 3 additions & 3 deletions src/list-picker/style/index.less
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@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';
Expand All @@ -21,13 +21,13 @@
max-height: 360px;
// margin-bottom: 8px;
padding: 0;
overflow-y: auto;
.scrollbar();
}
& > .@{list-prefix-cls} {
height: auto;
max-height: 360px;
// margin-bottom: 8px;
padding: 0;
overflow-y: auto;
.scrollbar();
}
}
43 changes: 23 additions & 20 deletions src/list/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,29 @@ import { ItemProps } from './interfance';
import BaseItem from './inner/baseItem';
import CheckboxItem from './inner/ChckboxItem';
import CalcaderItem from './inner/CascaderItem';
import WithRef from '../utils/withRef';

const Item: React.FC<ItemProps & { isMultiple?: boolean; isCascader?: boolean }> = (props) => {
const { label, value, disabled, isMultiple = false, isCascader, selectedParent = [], selectValue, ...rest } = props;
const Item: React.ForwardRefRenderFunction<HTMLLIElement, ItemProps & { isMultiple?: boolean; isCascader?: boolean }> =
(props, ref?) => {
const { label, value, disabled, isMultiple = false, isCascader, selectedParent = [], selectValue, ...rest } = props;

if (isMultiple) {
return <CheckboxItem label={label} value={value} disabled={disabled} {...rest} />;
}
if (isCascader) {
return (
<CalcaderItem
label={label as string}
value={value as string}
selectValue={selectValue as string}
disabled={disabled}
selectedParent={selectedParent}
{...rest}
/>
);
}
return <BaseItem label={label} value={value} disabled={disabled} {...rest} />;
};
if (isMultiple) {
return <CheckboxItem ref={ref} label={label} value={value} disabled={disabled} {...rest} />;
}
if (isCascader) {
return (
<CalcaderItem
ref={ref}
label={label as string}
value={value as string}
selectValue={selectValue as string}
disabled={disabled}
selectedParent={selectedParent}
{...rest}
/>
);
}
return <BaseItem ref={ref} label={label} value={value} disabled={disabled} {...rest} />;
};

export default Item;
export default WithRef(Item);

1 comment on commit 00b89f7

@vercel
Copy link

@vercel vercel bot commented on 00b89f7 Nov 17, 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.