Skip to content

Commit

Permalink
feat: Sender support Sender.Header (#156)
Browse files Browse the repository at this point in the history
* feat: support Sender.Header

* chore: demo

* docs: Semantic DOM

* test: update snapshot

* docs: update doc

* docs: add closable
  • Loading branch information
zombieJ authored Oct 16, 2024
1 parent a7adab9 commit e9b14fc
Show file tree
Hide file tree
Showing 17 changed files with 1,720 additions and 1,064 deletions.
11 changes: 8 additions & 3 deletions .dumi/components/SemanticPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const useStyle = createStyles(({ token }, markPos: [number, number, number, numb

export interface SemanticPreviewProps {
semantics: { name: string; desc: string; version?: string }[];
children: React.ReactElement;
children: React.ReactElement | ((injectProps: any) => React.ReactElement);
height?: number;
}

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

const cloneNode = React.cloneElement(children, {
const injectProps = {
classNames: semanticClassNames,
});
};

const cloneNode =
typeof children === 'function'
? children(injectProps)
: React.cloneElement(children, injectProps);

// ======================== Hover =========================
const containerRef = React.useRef<HTMLDivElement>(null);
Expand Down
115 changes: 115 additions & 0 deletions components/sender/SenderHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { CloseOutlined } from '@ant-design/icons';
import { Button } from 'antd';
import classNames from 'classnames';
import CSSMotion, { type MotionEventHandler } from 'rc-motion';
import * as React from 'react';

export interface SendHeaderContextProps {
prefixCls: string;
}

export const SendHeaderContext = React.createContext<SendHeaderContextProps>({} as any);

export type SemanticType = 'header' | 'content';

export interface SenderHeaderProps {
open?: boolean;
onOpenChange?: (open: boolean) => void;
title?: React.ReactNode;
children?: React.ReactNode;
className?: string;
style?: React.CSSProperties;
classNames?: Partial<Record<SemanticType, string>>;
styles?: Partial<Record<SemanticType, React.CSSProperties>>;
closable?: boolean;
}

const collapseHeight: MotionEventHandler = () => ({
height: 0,
});
const expandedHeight: MotionEventHandler = (ele) => ({
height: ele.scrollHeight,
});

export default function SenderHeader(props: SenderHeaderProps) {
const {
title,
onOpenChange,
open,
children,
className,
style,
classNames: classes = {},
styles = {},
closable,
} = props;

const { prefixCls } = React.useContext(SendHeaderContext);

const headerCls = `${prefixCls}-header`;

return (
<CSSMotion
motionEnter
motionLeave
motionName={`${headerCls}-motion`}
leavedClassName={`${headerCls}-motion-hidden`}
onEnterStart={collapseHeight}
onEnterActive={expandedHeight}
onLeaveStart={expandedHeight}
onLeaveActive={collapseHeight}
visible={open}
>
{({ className: motionClassName, style: motionStyle }) => {
return (
<div
className={classNames(headerCls, motionClassName, className)}
style={{
...motionStyle,
...style,
}}
>
{/* Header */}
{closable !== false && (
<div
className={
// We follow antd naming standard here.
// So the header part is use `-header` suffix.
// Though its little bit weird for double `-header`.
classNames(`${headerCls}-header`, classes.header)
}
style={{
...styles.header,
}}
>
<div className={`${headerCls}-title`}>{title}</div>
<div className={`${headerCls}-close`}>
<Button
type="text"
icon={<CloseOutlined />}
size="small"
onClick={() => {
onOpenChange?.(!open);
}}
/>
</div>
</div>
)}

{/* Content */}
{children && (
<div
className={classNames(`${headerCls}-content`, classes.content)}
style={{
...styles.content,
}}
>
{children}
</div>
)}
</div>
);
}}
</CSSMotion>
);
}
Loading

0 comments on commit e9b14fc

Please sign in to comment.