Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions .umirc.ts

This file was deleted.

10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ ReactDOM.render(App, container);
<th>-</th>
<td>specify whether the panel of children is collapsible or the area of collapsible.</td>
</tr>
<tr>
<td>items</td>
<td>
<a href="./src/interface.ts#ItemType">interface.ts#ItemType</a>
</td>
<th>-</th>
<td>collapse items content</td>
</tr>
</tbody>
</table>

Expand All @@ -116,6 +124,8 @@ If `accordion` is true, only one panel can be open. Opening another panel will c

### Collapse.Panel props

> **deprecated** use `items` instead, will be removed in `v4.0.0`

<table class="table table-bordered table-striped">
<thead>
<tr>
Expand Down
8 changes: 8 additions & 0 deletions docs/demo/basic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: Basic
nav:
title: Demo
path: /demo
---

<code src="../examples/basic.tsx"></code>
27 changes: 27 additions & 0 deletions docs/examples/basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { CollapseProps } from 'rc-collapse';
import Collapse from 'rc-collapse';
import * as React from 'react';
import '../../assets/index.less';

const App = () => {
const items: CollapseProps['items'] = [
{
label: 'title',
children: 'content',
},
{
label: 'title 2',
children: 'content 2',
collapsible: 'disabled',
},
{
label: 'title 3',
children: 'content 3',
onItemClick: console.log,
},
];

return <Collapse items={items} />;
};

export default App;
91 changes: 27 additions & 64 deletions src/Collapse.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import classNames from 'classnames';
import toArray from 'rc-util/lib/Children/toArray';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import warning from 'rc-util/lib/warning';
import React from 'react';
import type { CollapsePanelProps, CollapseProps, CollapsibleType } from './interface';
import useItems from './hooks/useItems';
import type { CollapseProps } from './interface';
import CollapsePanel from './Panel';

function getActiveKeysArray(activeKey: React.Key | React.Key[]) {
Expand All @@ -22,13 +23,14 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
style,
accordion,
className,
children: rawChildren,
children,
collapsible,
openMotion,
expandIcon,
activeKey: rawActiveKey,
defaultActiveKey,
onChange,
items,
} = props;

const collapseClassName = classNames(prefixCls, className);
Expand All @@ -40,7 +42,7 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
postState: getActiveKeysArray,
});

const onClickItem = (key: React.Key) =>
const onItemClick = (key: React.Key) =>
setActiveKey(() => {
if (accordion) {
return activeKey[0] === key ? [] : [key];
Expand All @@ -56,65 +58,21 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
});

// ======================== Children ========================
const getNewChild = (child: React.ReactElement<CollapsePanelProps>, index: number) => {
if (!child) return null;

const key = child.key || String(index);

const {
header,
headerClass,
destroyInactivePanel: childDestroyInactivePanel,
collapsible: childCollapsible,
onItemClick: childOnItemClick,
} = child.props;

let isActive = false;
if (accordion) {
isActive = activeKey[0] === key;
} else {
isActive = activeKey.indexOf(key) > -1;
}

const mergeCollapsible: CollapsibleType = childCollapsible ?? collapsible;

const handleItemClick = (value: React.Key) => {
if (mergeCollapsible === 'disabled') return;
onClickItem(value);
childOnItemClick?.(value);
};

const childProps = {
key,
panelKey: key,
header,
headerClass,
isActive,
prefixCls,
destroyInactivePanel: childDestroyInactivePanel ?? destroyInactivePanel,
openMotion,
accordion,
children: child.props.children,
onItemClick: handleItemClick,
expandIcon,
collapsible: mergeCollapsible,
};

// https://github.com/ant-design/ant-design/issues/20479
if (typeof child.type === 'string') {
return child;
}

Object.keys(childProps).forEach((propName) => {
if (typeof childProps[propName] === 'undefined') {
delete childProps[propName];
}
});

return React.cloneElement(child, childProps);
};
warning(
!children,
'`children` will be removed in next major version. Please use `items` instead.',
);

const children = toArray(rawChildren).map(getNewChild);
const mergedChildren = useItems(items, children, {
prefixCls,
accordion,
openMotion,
expandIcon,
collapsible,
destroyInactivePanel,
onItemClick,
activeKey,
});

// ======================== Render ========================
return (
Expand All @@ -124,9 +82,14 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
style={style}
role={accordion ? 'tablist' : undefined}
>
{children}
{mergedChildren}
</div>
);
});

export default Object.assign(Collapse, { Panel: CollapsePanel });
export default Object.assign(Collapse, {
/**
* @deprecated use `items` instead, will be removed in `v4.0.0`
*/
Panel: CollapsePanel,
});
162 changes: 162 additions & 0 deletions src/hooks/useItems.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import toArray from 'rc-util/lib/Children/toArray';
import React from 'react';
import type { CollapsePanelProps, CollapseProps, ItemType } from '../interface';
import CollapsePanel from '../Panel';

type Props = Pick<CollapsePanelProps, 'prefixCls' | 'onItemClick' | 'openMotion' | 'expandIcon'> &
Pick<CollapseProps, 'accordion' | 'collapsible' | 'destroyInactivePanel'> & {
activeKey: React.Key[];
};

const convertItemsToNodes = (items: ItemType[], props: Props) => {
const {
prefixCls,
accordion,
collapsible,
destroyInactivePanel,
onItemClick,
activeKey,
openMotion,
expandIcon,
} = props;

return items.map((item, index) => {
const {
children,
label,
key: rawKey,
collapsible: rawCollapsible,
onItemClick: rawOnItemClick,
destroyInactivePanel: rawDestroyInactivePanel,
...restProps
} = item;

// You may be puzzled why you want to convert them all into strings, me too.
// Maybe: https://github.com/react-component/collapse/blob/aac303a8b6ff30e35060b4f8fecde6f4556fcbe2/src/Collapse.tsx#L15
const key = String(rawKey ?? index);
const mergeCollapsible = rawCollapsible ?? collapsible;
const mergeDestroyInactivePanel = rawDestroyInactivePanel ?? destroyInactivePanel;

const handleItemClick = (value: React.Key) => {
if (mergeCollapsible === 'disabled') return;
onItemClick(value);
rawOnItemClick?.(value);
};

let isActive = false;
if (accordion) {
isActive = activeKey[0] === key;
} else {
isActive = activeKey.indexOf(key) > -1;
}

return (
<CollapsePanel
{...restProps}
prefixCls={prefixCls}
key={key}
panelKey={key}
isActive={isActive}
accordion={accordion}
openMotion={openMotion}
expandIcon={expandIcon}
header={label}
collapsible={mergeCollapsible}
onItemClick={handleItemClick}
destroyInactivePanel={mergeDestroyInactivePanel}
>
{children}
</CollapsePanel>
);
});
};

/**
* @deprecated The next major version will be removed
*/
const getNewChild = (
child: React.ReactElement<CollapsePanelProps>,
index: number,
props: Props,
) => {
if (!child) return null;

const {
prefixCls,
accordion,
collapsible,
destroyInactivePanel,
onItemClick,
activeKey,
openMotion,
expandIcon,
} = props;

const key = child.key || String(index);

const {
header,
headerClass,
destroyInactivePanel: childDestroyInactivePanel,
collapsible: childCollapsible,
onItemClick: childOnItemClick,
} = child.props;

let isActive = false;
if (accordion) {
isActive = activeKey[0] === key;
} else {
isActive = activeKey.indexOf(key) > -1;
}

const mergeCollapsible = childCollapsible ?? collapsible;

const handleItemClick = (value: React.Key) => {
if (mergeCollapsible === 'disabled') return;
onItemClick(value);
childOnItemClick?.(value);
};

const childProps = {
key,
panelKey: key,
header,
headerClass,
isActive,
prefixCls,
destroyInactivePanel: childDestroyInactivePanel ?? destroyInactivePanel,
openMotion,
accordion,
children: child.props.children,
onItemClick: handleItemClick,
expandIcon,
collapsible: mergeCollapsible,
};

// https://github.com/ant-design/ant-design/issues/20479
if (typeof child.type === 'string') {
return child;
}

Object.keys(childProps).forEach((propName) => {
if (typeof childProps[propName] === 'undefined') {
delete childProps[propName];
}
});

return React.cloneElement(child, childProps);
};

function useItems(
items?: ItemType[],
rawChildren?: React.ReactNode,
props?: Props,
): React.ReactElement<CollapsePanelProps>[] {
if (Array.isArray(items)) {
return convertItemsToNodes(items, props);
}

return toArray(rawChildren).map((child, index) => getNewChild(child, index, props));
}

export default useItems;
4 changes: 4 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ import Collapse from './Collapse';
export type { CollapsePanelProps, CollapseProps } from './interface';

export default Collapse;

/**
* @deprecated use `items` instead, will be removed in `v4.0.0`
*/
export const { Panel } = Collapse;
Loading