Skip to content

Commit e9b14fc

Browse files
authored
feat: Sender support Sender.Header (#156)
* feat: support Sender.Header * chore: demo * docs: Semantic DOM * test: update snapshot * docs: update doc * docs: add closable
1 parent a7adab9 commit e9b14fc

File tree

17 files changed

+1720
-1064
lines changed

17 files changed

+1720
-1064
lines changed

.dumi/components/SemanticPreview.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const useStyle = createStyles(({ token }, markPos: [number, number, number, numb
6767

6868
export interface SemanticPreviewProps {
6969
semantics: { name: string; desc: string; version?: string }[];
70-
children: React.ReactElement;
70+
children: React.ReactElement | ((injectProps: any) => React.ReactElement);
7171
height?: number;
7272
}
7373

@@ -91,9 +91,14 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
9191
return classNames;
9292
}, [semantics]);
9393

94-
const cloneNode = React.cloneElement(children, {
94+
const injectProps = {
9595
classNames: semanticClassNames,
96-
});
96+
};
97+
98+
const cloneNode =
99+
typeof children === 'function'
100+
? children(injectProps)
101+
: React.cloneElement(children, injectProps);
97102

98103
// ======================== Hover =========================
99104
const containerRef = React.useRef<HTMLDivElement>(null);

components/sender/SenderHeader.tsx

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { CloseOutlined } from '@ant-design/icons';
2+
import { Button } from 'antd';
3+
import classNames from 'classnames';
4+
import CSSMotion, { type MotionEventHandler } from 'rc-motion';
5+
import * as React from 'react';
6+
7+
export interface SendHeaderContextProps {
8+
prefixCls: string;
9+
}
10+
11+
export const SendHeaderContext = React.createContext<SendHeaderContextProps>({} as any);
12+
13+
export type SemanticType = 'header' | 'content';
14+
15+
export interface SenderHeaderProps {
16+
open?: boolean;
17+
onOpenChange?: (open: boolean) => void;
18+
title?: React.ReactNode;
19+
children?: React.ReactNode;
20+
className?: string;
21+
style?: React.CSSProperties;
22+
classNames?: Partial<Record<SemanticType, string>>;
23+
styles?: Partial<Record<SemanticType, React.CSSProperties>>;
24+
closable?: boolean;
25+
}
26+
27+
const collapseHeight: MotionEventHandler = () => ({
28+
height: 0,
29+
});
30+
const expandedHeight: MotionEventHandler = (ele) => ({
31+
height: ele.scrollHeight,
32+
});
33+
34+
export default function SenderHeader(props: SenderHeaderProps) {
35+
const {
36+
title,
37+
onOpenChange,
38+
open,
39+
children,
40+
className,
41+
style,
42+
classNames: classes = {},
43+
styles = {},
44+
closable,
45+
} = props;
46+
47+
const { prefixCls } = React.useContext(SendHeaderContext);
48+
49+
const headerCls = `${prefixCls}-header`;
50+
51+
return (
52+
<CSSMotion
53+
motionEnter
54+
motionLeave
55+
motionName={`${headerCls}-motion`}
56+
leavedClassName={`${headerCls}-motion-hidden`}
57+
onEnterStart={collapseHeight}
58+
onEnterActive={expandedHeight}
59+
onLeaveStart={expandedHeight}
60+
onLeaveActive={collapseHeight}
61+
visible={open}
62+
>
63+
{({ className: motionClassName, style: motionStyle }) => {
64+
return (
65+
<div
66+
className={classNames(headerCls, motionClassName, className)}
67+
style={{
68+
...motionStyle,
69+
...style,
70+
}}
71+
>
72+
{/* Header */}
73+
{closable !== false && (
74+
<div
75+
className={
76+
// We follow antd naming standard here.
77+
// So the header part is use `-header` suffix.
78+
// Though its little bit weird for double `-header`.
79+
classNames(`${headerCls}-header`, classes.header)
80+
}
81+
style={{
82+
...styles.header,
83+
}}
84+
>
85+
<div className={`${headerCls}-title`}>{title}</div>
86+
<div className={`${headerCls}-close`}>
87+
<Button
88+
type="text"
89+
icon={<CloseOutlined />}
90+
size="small"
91+
onClick={() => {
92+
onOpenChange?.(!open);
93+
}}
94+
/>
95+
</div>
96+
</div>
97+
)}
98+
99+
{/* Content */}
100+
{children && (
101+
<div
102+
className={classNames(`${headerCls}-content`, classes.content)}
103+
style={{
104+
...styles.content,
105+
}}
106+
>
107+
{children}
108+
</div>
109+
)}
110+
</div>
111+
);
112+
}}
113+
</CSSMotion>
114+
);
115+
}

0 commit comments

Comments
 (0)