diff --git a/packages/apsara-ui/src/Alert/Alert.stories.tsx b/packages/apsara-ui/src/Alert/Alert.stories.tsx new file mode 100644 index 00000000..c8b2a3e3 --- /dev/null +++ b/packages/apsara-ui/src/Alert/Alert.stories.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import Alert from "./Alert"; + +export default { + title: "General/Alert", + component: Alert, +}; + +export const alert = () => { + return ( +
+ + + + +
+ ); +}; diff --git a/packages/apsara-ui/src/Alert/Alert.styles.tsx b/packages/apsara-ui/src/Alert/Alert.styles.tsx new file mode 100644 index 00000000..997db9d0 --- /dev/null +++ b/packages/apsara-ui/src/Alert/Alert.styles.tsx @@ -0,0 +1,103 @@ +import styled from "styled-components"; + +export const AlertWrapper = styled.div` + .apsara-alert { + box-sizing: border-box; + margin: 0; + padding: 0; + color: rgba(0, 0, 0, 0.85); + font-size: 14px; + font-variant: tabular-nums; + line-height: 1.5715; + list-style: none; + font-feature-settings: "tnum"; + position: relative; + display: flex; + align-items: center; + padding: 8px 15px; + word-wrap: break-word; + border-radius: 2px; + + &-content { + flex: 1; + min-width: 0; + } + &-icon { + margin-right: 8px; + } + &-description { + display: none; + font-size: 14px; + line-height: 22px; + } + + &-success { + background-color: #f6ffed; + border: 1px solid #b7eb8f; + + .apsara-alert-icon { + color: #52c41a; + } + } + &-info { + background-color: #e6f7ff; + border: 1px solid #91d5ff; + + .apsara-alert-icon { + color: #1890ff; + } + } + &-warning { + background-color: #fffbe6; + border: 1px solid #ffe58f; + + .apsara-alert-icon { + color: #faad14; + } + } + &-error { + background-color: #fff2f0; + border: 1px solid #ffccc7; + + .apsara-alert-icon { + color: #ff4d4f; + } + + .apsara-alert-description > pre { + margin: 0; + padding: 0; + } + } + + &-action { + margin-left: 8px; + } + + &-with-description { + align-items: flex-start; + padding: 15px 15px 15px 24px; + + &.apsara-alert-no-icon { + padding: 15px 15px; + } + .apsara-alert-icon { + margin-right: 15px; + font-size: 24px; + } + + .apsara-alert-message { + display: block; + margin-bottom: 4px; + color: rgba(0, 0, 0, 0.85); + font-size: 16px; + } + .apsara-alert-description { + display: block; + } + } + + &-message { + color: rgba(0, 0, 0, 0.85); + } + } +`; diff --git a/packages/apsara-ui/src/Alert/Alert.tsx b/packages/apsara-ui/src/Alert/Alert.tsx new file mode 100644 index 00000000..fdad9909 --- /dev/null +++ b/packages/apsara-ui/src/Alert/Alert.tsx @@ -0,0 +1,94 @@ +import * as React from "react"; +import CheckCircleOutlined from "@ant-design/icons/CheckCircleOutlined"; +import ExclamationCircleOutlined from "@ant-design/icons/ExclamationCircleOutlined"; +import InfoCircleOutlined from "@ant-design/icons/InfoCircleOutlined"; +import CloseCircleOutlined from "@ant-design/icons/CloseCircleOutlined"; +import CheckCircleFilled from "@ant-design/icons/CheckCircleFilled"; +import ExclamationCircleFilled from "@ant-design/icons/ExclamationCircleFilled"; +import InfoCircleFilled from "@ant-design/icons/InfoCircleFilled"; +import CloseCircleFilled from "@ant-design/icons/CloseCircleFilled"; +import { replaceElement } from "../FormBuilder/utils/reactNode"; +import classNames from "classnames"; +import { AlertWrapper } from "./Alert.styles"; + +export interface AlertProps { + type?: "success" | "info" | "warning" | "error"; + message: React.ReactNode; + description?: React.ReactNode; + showIcon?: boolean; + style?: React.CSSProperties; + className?: string; + icon?: React.ReactNode; + action?: React.ReactNode; +} + +const iconMapFilled = { + success: CheckCircleFilled, + info: InfoCircleFilled, + error: CloseCircleFilled, + warning: ExclamationCircleFilled, +}; + +const iconMapOutlined = { + success: CheckCircleOutlined, + info: InfoCircleOutlined, + error: CloseCircleOutlined, + warning: ExclamationCircleOutlined, +}; + +const Alert = ({ description, message, className = "", style, showIcon = false, action, ...props }: AlertProps) => { + const [closed, _setClosed] = React.useState(false); + + const prefixCls = "apsara-alert"; + + const getType = () => { + const { type } = props; + if (type !== undefined) { + return type; + } + return "info"; + }; + + const type = getType(); + + const renderIconNode = () => { + const { icon } = props; + const iconType = (description ? iconMapOutlined : iconMapFilled)[type] || null; + if (icon) { + return replaceElement(icon, {icon}, () => ({ + className: classNames(`${prefixCls}-icon`, { + [(icon as any).props.className]: (icon as any).props.className, + }), + })); + } + return React.createElement(iconType, { className: `${prefixCls}-icon` }); + }; + + const isShowIcon = showIcon === undefined ? true : showIcon; + + const alertClasses = classNames( + prefixCls, + `${prefixCls}-${type}`, + { + [`${prefixCls}-with-description`]: !!description, + [`${prefixCls}-no-icon`]: !isShowIcon, + }, + className, + ); + + return ( + +
+ {isShowIcon ? renderIconNode() : null} +
+
{message}
+
{description}
+
+ + {action ?
{action}
: null} +
+
+ ); +}; + +export default Alert; diff --git a/packages/apsara-ui/src/Alert/index.tsx b/packages/apsara-ui/src/Alert/index.tsx new file mode 100644 index 00000000..f1a2cf4d --- /dev/null +++ b/packages/apsara-ui/src/Alert/index.tsx @@ -0,0 +1,3 @@ +import Alert from "./Alert"; + +export default Alert; diff --git a/packages/apsara-ui/src/index.ts b/packages/apsara-ui/src/index.ts index c5d5d6b9..18b32f87 100644 --- a/packages/apsara-ui/src/index.ts +++ b/packages/apsara-ui/src/index.ts @@ -50,6 +50,7 @@ import { Col } from "./FormBuilder/grid/"; import { InputProps } from "./Input/Input"; import { TooltipPlacement } from "./Table/TableProps"; import Modal from "./Modal"; +import Alert from "./Alert"; export { DynamicList } from "./DynamicList"; export * from "./Notification"; @@ -113,4 +114,5 @@ export { InputProps, TooltipPlacement, Modal, + Alert, };