未验证 提交 e7d2dd6d 编写于 作者: _ __Oasis__ 提交者: GitHub

Merge branch 'nutui-react' into nutui-react

......@@ -157,7 +157,38 @@
},
{
"name": "布局组件",
"packages": []
"packages": [
{
"version": "1.0.0",
"name": "Layout",
"type": "component",
"cName": "布局",
"desc": "协助进行页面级整体布局。",
"sort": 1,
"show": true,
"author": "yushuang24"
},
{
"version": "1.0.0",
"name": "Row",
"type": "component",
"cName": "行",
"desc": "布局组件中的行",
"sort": 2,
"show": true,
"author": "yushuang24"
},
{
"version": "1.0.0",
"name": "Col",
"type": "component",
"cName": "列",
"desc": "布局组件中的列",
"sort": 3,
"show": true,
"author": "yushuang24"
}
]
},
{
"name": "操作反馈",
......@@ -251,6 +282,16 @@
"sort": 7,
"show": true,
"author": "swag~jun"
},
{
"version": "1.0.0",
"name": "Popup",
"sort": 9,
"cName": "弹出层",
"type": "component",
"show": true,
"desc": "弹出层容器,用于展示弹窗、信息提示等内容,支持多个弹出层叠加展示",
"author": "szg2008"
}
]
},
......@@ -296,6 +337,16 @@
"sort": 4,
"show": true,
"author": "songsong"
},
{
"version": "1.0.0",
"name": "FixedNav",
"type": "component",
"cName": "悬浮导航",
"desc": "悬浮收齐体验交互,用于快捷导航",
"sort": 5,
"show": true,
"author": "Ymm0008"
}
]
},
......@@ -352,6 +403,36 @@
"show": true,
"author": "oasis"
},
{
"version": "1.0.0",
"name": "Tag",
"type": "component",
"cName": "标签",
"desc": "用于标记和分类。",
"sort": 4,
"show": true,
"author": "lzz"
},
{
"version": "1.0.0",
"name": "Badge",
"type": "component",
"cName": "徽标",
"desc": "在图标或文字右上角的红色圆点、数字或者文字,表示有新内容或者待处理的信息。",
"sort": 4,
"show": true,
"author": "lzz"
},
{
"version": "1.0.0",
"name": "Popover",
"type": "component",
"cName": "气泡弹出框",
"desc": "点击或在元素上悬停鼠标,弹出气泡卡片浮层。",
"sort": 4,
"show": true,
"author": "lzz"
},
{
"version": "1.0.0",
"name": "AnimatingNumbers",
......
.nut-badge {
position: relative;
display: inline-block;
width: 40px;
height: 40px;
margin-right: 40px;
.slot-icons {
position: absolute;
background: $badge-background-color;
border-radius: 7px;
top: -20%;
right: -20%;
padding: 0 5px;
text-align: center;
border-radius: 14px;
z-index: 1;
}
.sup {
position: absolute;
background: $badge-background-color;
border-radius: 7px;
padding: 0 5px;
text-align: center;
border-radius: 14px;
font-size: $badge-font-size;
font-weight: normal;
color: $badge-default-background-color;
line-height: 16px;
}
.nut-badge__content {
transform: translateY(-50%) translateX(100%);
}
.is-dot {
width: 7px;
height: 7px;
border-radius: 7px;
padding: 0;
}
}
import React, {
CSSProperties,
FunctionComponent,
MouseEventHandler,
useEffect,
useState,
} from 'react'
import './badge.scss'
import Icon from '@/packages/icon'
export interface BadgeProps {
value: any
dot: boolean
max: Number
top: string
right: string
zIndex: string
color: string
icons: any
}
export type BadgeType = 'default' | 'primary' | 'success' | 'warning' | 'danger'
const defaultProps = {
value: '',
dot: false,
max: 10000,
top: '0',
right: '0',
zIndex: '0',
color: '',
icons: '',
} as BadgeProps
export const Badge: FunctionComponent<Partial<BadgeProps>> = (props) => {
const { value, children, dot, max, top, right, zIndex, color, icons } = {
...defaultProps,
...props,
}
const content = () => {
if (dot) return
const value = props.value
const max = props.max
if (typeof value === 'number' && typeof max === 'number') {
return max < value ? `${max}+` : value
}
return value
}
const getStyle = () => {
const style: CSSProperties = {}
style.top = `${top}px`
style.right = `${right}px`
style.zIndex = zIndex
style.background = color
return style
}
return (
<div className="nut-badge">
{icons != '' && (
<div className="slot-icons">
<Icon className="_icon" name={icons} color="#ffffff" size="12"></Icon>
</div>
)}
<div>{children}</div>
<div className={`${dot ? 'is-dot' : ''} nut-badge__content sup`} style={getStyle()}>
{content()}
</div>
</div>
)
}
Badge.defaultProps = defaultProps
Badge.displayName = 'NutBadge'
import React, { useState } from 'react'
import { Badge } from './badge'
import Cell from '@/packages/cell'
import CellGroup from '@/packages/cellgroup'
import Avatar from '@/packages/avatar'
const BadgeDemo = () => {
return (
<>
<div className="demo">
<CellGroup title="默认用法">
<Cell>
<Badge value={8}>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value={76}>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value="NEW">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge dot>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
</Cell>
</CellGroup>
<CellGroup title="最大值">
<Cell>
<Badge value={200} max={9}>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value={200} max={20}>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value={200} max={99}>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
</Cell>
</CellGroup>
<CellGroup title="自定义颜色">
<Cell>
<Badge
value={8}
color="linear-gradient(315deg, rgba(73,143,242,1) 0%,rgba(73,101,242,1) 100%)"
>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge
value={76}
color="linear-gradient(315deg, rgba(73,143,242,1) 0%,rgba(73,101,242,1) 100%)"
>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge
value="NEW"
color="linear-gradient(315deg, rgba(73,143,242,1) 0%,rgba(73,101,242,1) 100%)"
>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge
dot
color="linear-gradient(315deg, rgba(73,143,242,1) 0%,rgba(73,101,242,1) 100%)"
>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
</Cell>
</CellGroup>
<CellGroup title="自定义徽标内容">
<Cell>
<Badge icons="checklist">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge icons="link">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge icons="download">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
</Cell>
</CellGroup>
<CellGroup title="自定义位置">
<Cell>
<Badge value={8} top="5" right="5">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value={76} top="10" right="10">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value="NEW">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
</Cell>
</CellGroup>
<CellGroup title="独立展示">
<Cell>
<Badge value={8}></Badge>
<Badge value={76}></Badge>
<Badge value="NEW"></Badge>
</Cell>
</CellGroup>
</div>
</>
)
}
export default BadgeDemo
# Badge 徽标
### 介绍
出现在图标或文字右上角的红色圆点、数字或者文字,表示有新内容或者待处理的信息。
### 安装
``` javascript
import { Badge } from '@nutui/nutui';
```
## 代码实例
### 基本用法
```tsx
<Badge value={8}>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value={76}>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value="NEW">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge dot>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
```
### 最大值
```tsx
<Badge value={200} max={9}>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value={200} max={20}>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value={200} max={99}>>
<Avatar icon="my" shape="square"></Avatar>
</Badge>
```
### 自定义颜色
```tsx
<Badge value={8} color="linear-gradient(315deg, rgba(73,143,242,1) 0%,rgba(73,101,242,1) 100%)">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value={76} color="linear-gradient(315deg, rgba(73,143,242,1) 0%,rgba(73,101,242,1) 100%)">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value="NEW" color="linear-gradient(315deg, rgba(73,143,242,1) 0%,rgba(73,101,242,1) 100%)">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge dot color="linear-gradient(315deg, rgba(73,143,242,1) 0%,rgba(73,101,242,1) 100%)">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
```
### 自定义徽标内容
```tsx
<Badge icon="checklist">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge icon="link">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge icon="download">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
```
### 自定义位置
```tsx
<Badge value={8} top="5" right="5">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value={76} top="10" right="10">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
<Badge value="NEW">
<Avatar icon="my" shape="square"></Avatar>
</Badge>
```
### 独立展示
```tsx
<Badge value={8}> </Badge>
<Badge value={76}> </Badge>
<Badge value="NEW"> </Badge>
```
## API
### Props
| 字段 | 说明 | 类型 | 默认值 |
|---------|--------------------------------------------|---------|-----------|
| value | 显示的内容 | String | - |
| max | value 为数值时,最大值 | Number | `10000` |
| z-index | 徽标的 z-index 值 | Number | `10` |
| dot | 是否为小点 | Boolean | `false` |
| hidden | 是否隐藏 | Boolean | `false` |
| top | 上下偏移量,支持单位设置,可设置为:5px 等 | Number | `0` |
| right | 左右偏移量,支持单位设置,可设置为:5px 等 | Number | `0` |
| color | 徽标背景颜色 | String | `#fa2c19` |
| icons | 徽标自定义 | String | - |
import { Badge } from './badge'
export default Badge
.nut-col {
float: left;
box-sizing: border-box;
word-break: break-all;
&-gutter {
&:last-child {
padding-right: 0 !important;
}
&:first-child {
padding-left: 0 !important;
}
}
}
@for $i from 1 through 24 {
.nut-col-offset-#{$i} {
margin-left: 100/ 24 * $i * 1%;
}
.nut-col-#{$i} {
width: 100/ 24 * $i * 1%;
}
}
import React, { FunctionComponent, useEffect, useState, CSSProperties, useContext } from 'react'
import { DataContext } from '@/packages/row/UserContext'
import './col.scss'
export interface ColProps {
span: string | number
offset: string | number
gutter: string | number
}
const defaultProps = {
span: '24',
offset: '0',
gutter: '0',
} as ColProps
export const Col: FunctionComponent<Partial<ColProps> & React.HTMLAttributes<HTMLDivElement>> = (
props
) => {
const { span, offset, children } = { ...defaultProps, ...props }
const [colName, setColName] = useState('')
const [colStyle, setColStyle] = useState({})
const gutter = (useContext(DataContext) as any).gutter
const classs = () => {
//定义col的class类
const prefixCls = 'nut-col'
return `${prefixCls} ${prefixCls}-${span} ${
gutter ? `${prefixCls}-gutter` : ''
} ${prefixCls}-${offset}`
}
const getStyle = () => {
//定义col的style类
const style: CSSProperties = {}
style.paddingLeft = `${(gutter as number) / 2}px`
style.paddingRight = `${(gutter as number) / 2}px`
return style
}
useEffect(() => {
setColName(classs)
setColStyle(getStyle)
}, [span, offset, gutter])
return (
<div className={`${colName}`} style={{ ...colStyle }}>
{children}
</div>
)
}
Col.defaultProps = defaultProps
Col.displayName = 'NutCol'
import { Col } from './col'
export default Col
import React, { useState, MouseEvent } from 'react'
import { Icon } from '../icon/icon'
import { FixedNav } from './fixednav'
import Drag from './../drag'
const FixedNavDemo = () => {
const navList = [
{
id: 1,
text: '首页',
icon: 'https://img11.360buyimg.com/imagetools/jfs/t1/117646/2/11112/1297/5ef83e95E81d77f05/daf8e3b1c81e3c98.png',
},
{
id: 2,
text: '分类',
icon: 'https://img12.360buyimg.com/imagetools/jfs/t1/119490/8/9568/1798/5ef83e95E968c69a6/dd029326f7d5042e.png',
},
{
id: 3,
text: '购物车',
num: 2,
icon: 'https://img14.360buyimg.com/imagetools/jfs/t1/130725/4/3157/1704/5ef83e95Eb976644f/b36c6cfc1cc1a99d.png',
},
{
id: 4,
text: '我的',
icon: 'https://img12.360buyimg.com/imagetools/jfs/t1/147573/29/1603/1721/5ef83e94E1393a678/5ddf1695ec989373.png',
},
]
const [visible1, setVisible1] = useState(false)
const [visible2, setVisible2] = useState(false)
const [visible3, setVisible3] = useState(false)
const [visible4, setVisible4] = useState(false)
const [visible5, setVisible5] = useState(false)
const change1 = (value: boolean) => {
setVisible1(value)
}
const selected1 = (item: any, event: MouseEvent) => {
console.log(item, event)
}
const change2 = (value: boolean) => {
setVisible2(value)
}
const selected2 = (item: any, event: MouseEvent) => {
console.log(item, event)
}
const change3 = (value: boolean) => {
setVisible3(value)
}
const selected3 = (item: any, event: MouseEvent) => {
console.log(item, event)
}
const change4 = (value: boolean) => {
setVisible4(value)
}
const selected4 = (item: any, event: MouseEvent) => {
console.log(item, event)
}
const change5 = (value: boolean) => {
setVisible5(value)
}
const selected5 = (item: any, event: MouseEvent) => {
console.log(item, event)
}
return (
<>
<div className="demo">
<FixedNav
navList={navList}
activeText="基础用法"
overlay={true}
position={{ top: '70px' }}
change={change1}
visible={visible1}
selected={selected1}
/>
<FixedNav
navList={navList}
type={'left'}
position={{ top: '140px' }}
visible={visible2}
activeText={'左侧收起'}
unActiveText={'左侧展开'}
change={change2}
selected={selected2}
/>
<FixedNav
navList={navList}
position={{ top: '210px' }}
overlay={false}
visible={visible3}
change={change3}
selected={selected3}
/>
<FixedNav
position={{ top: '280px' }}
type={'left'}
visible={visible4}
change={change4}
selected={selected4}
slotList={
<ul className="nut-fixednav__list" slot={'list'}>
<li className="nut-fixednav__list-item">1</li>
<li className="nut-fixednav__list-item">2</li>
<li className="nut-fixednav__list-item">3</li>
<li className="nut-fixednav__list-item">4</li>
<li className="nut-fixednav__list-item">5</li>
</ul>
}
slotBtn={
<>
<Icon name="retweet" color="#fff">
{' '}
</Icon>
<span className="text">{visible4 ? '自定义开' : '自定义关'}</span>
</>
}
/>
{/* <!-- 配合 Drag 支持拖拽 ,小程序暂不支持 --> */}
<Drag direction={'y'} style={{ right: '0px', bottom: '240px' }}>
<FixedNav
navList={navList}
unActiveText="支持拖拽"
visible={visible5}
change={change5}
selected={selected5}
/>
</Drag>
</div>
</>
)
}
export default FixedNavDemo
# FixedNav 悬浮导航
### 介绍
悬浮收齐体验交互,用于快捷导航
### 安装
``` javascript
import { FixedNav } from '@nutui/nutui-react';
```
### 基础用法
``` tsx
<FixedNav
navList={navList}
activeText="基础用法"
overlay={true}
position={{ top: '70px' }}
change={change}
visible={visible}
selected={selected}
>
```
``` tsx
const navList = [
{
id: 1,
text: '首页',
icon: 'https://img11.360buyimg.com/imagetools/jfs/t1/117646/2/11112/1297/5ef83e95E81d77f05/daf8e3b1c81e3c98.png'
},
{
id: 2,
text: '分类',
icon: 'https://img12.360buyimg.com/imagetools/jfs/t1/119490/8/9568/1798/5ef83e95E968c69a6/dd029326f7d5042e.png'
},
{
id: 3,
text: '购物车',
num: 2,
icon: 'https://img14.360buyimg.com/imagetools/jfs/t1/130725/4/3157/1704/5ef83e95Eb976644f/b36c6cfc1cc1a99d.png'
},
{
id: 4,
text: '我的',
icon: 'https://img12.360buyimg.com/imagetools/jfs/t1/147573/29/1603/1721/5ef83e94E1393a678/5ddf1695ec989373.png'
}
];
const [visible, setVisible] = useState(false);
const change = (value: boolean) => {
setVisible(value);
};
const selected = (item: any, event: MouseEvent) => {
console.log(item, event);
};
```
### 左侧效果
``` tsx
<FixedNav
navList={navList}
type={"left"}
position={{ top: '140px' }}
visible={visible}
activeText={"左侧收起"}
unActiveText={"左侧展开"}
change={change}
selected={selected}
/>
```
### 取消背景遮罩
``` tsx
<FixedNav
navList={navList}
position={{ top: '210px' }}
overlay={false}
visible={visible}
change={change}
selected={selected}
/>
```
### 自定义使用
``` tsx
<FixedNav
position={{ top: '280px' }}
type={"left"}
visible={visible}
change={change}
selected={selected}
slotList={
<ul className="nut-fixednav__list" slot={"list"}>
<li className="nut-fixednav__list-item">1</li>
<li className="nut-fixednav__list-item">2</li>
<li className="nut-fixednav__list-item">3</li>
<li className="nut-fixednav__list-item">4</li>
<li className="nut-fixednav__list-item">5</li>
</ul>
}
slotBtn={
<>
<Icon name="retweet" color="#fff"> </Icon>
<span className="text">{ visible ? '自定义开' : '自定义关' }</span>
</>
}
/>
```
### 支持拖拽
``` tsx
// 配合 Drag 支持拖拽 ,小程序暂不支持
<Drag direction={"y"} style={{ right: '0px', bottom: '240px' }}>
<FixedNav
navList={navList}
unActiveText="支持拖拽"
visible={visible}
change={change}
selected={selected} />
</Drag>
```
### Prop
| 字段 | 说明 | 类型 | 默认值 |
|:---------------|:---------------------------|:--------|:-----------------------------|
| visible | 是否打开 | Boolean | false |
| navList | 悬浮列表内容数据 | Array | [] |
| activeText | 收起列表按钮文案 | String | 收起导航 |
| unActiveText | 展开列表按钮文案 | String | 快速导航 |
| type | 导航方向,可选值 left right | String | right |
| overlay | 展开时是否显示遮罩 | Boolean | true |
| position | fixed 垂直位置 | Object | {top: 'auto',bottom: 'auto'} |
| slotList | 自定义展开列表内容 | HTMLElement | - |
| slotBtn | 自定义按钮 | HTMLElement | - |
### Event
| 字段 | 说明 | 回调参数 |
|----------|--------------|--------------------------|
| change | 展开收起按钮回调 | {value:boolean} |
| selected | 选择之后触发 | {item:item, event:MouseEvent} |
\ No newline at end of file
.nut-fixednav {
position: fixed;
z-index: $fixednav-index;
display: inline-block;
height: 50px;
right: 0;
&.active {
.nut-icon {
transform: rotate(180deg);
}
.nut-fixednav__list {
transform: translateX(0%) !important;
}
&.left {
.nut-icon {
transform: rotate(0deg) !important;
}
}
}
&__btn {
box-sizing: border-box;
position: absolute;
right: 0;
z-index: $fixednav-index + 1;
width: 80px;
padding-left: 12px;
height: 100%;
background: $fixednav-btn-bg;
border-radius: 45px 0px 0px 45px;
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
justify-content: center;
.text {
width: 24px;
line-height: 13px;
font-size: 10px;
color: $fixednav-bg-color;
flex-shrink: 0;
}
.nut-icon {
margin-right: 5px;
transition: all 0.3s;
transform: rotate(0deg);
transition: all 0.3s;
}
}
&__list {
position: absolute;
right: 0;
transform: translateX(100%);
transition: all 0.5s;
z-index: $fixednav-index;
flex-shrink: 0;
height: 100%;
background: $fixednav-bg-color;
display: flex;
justify-content: space-between;
border-radius: 25px 0px 0px 25px;
box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, 0.2);
padding: {
left: 20px;
right: 80px;
}
&-item {
position: relative;
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-width: 50px;
flex-shrink: 0;
img {
width: 20px;
height: 20px;
margin-bottom: 2px;
}
.span {
font-size: 10px;
color: $fixednav-font-color;
}
.b {
position: absolute;
right: 0;
top: 1px;
height: 14px;
line-height: 14px;
font-size: 10px;
padding: 0 3px;
color: white;
background: $primary-color;
border-radius: 7px;
text-align: center;
min-width: 12px;
}
}
}
&.left {
right: auto;
left: 0;
.nut-fixednav__btn {
flex-direction: row-reverse;
right: auto;
left: 0;
border-radius: 0 45px 45px 0;
.nut-icon {
transform: rotate(180deg);
}
}
.nut-fixednav__list {
transform: translateX(-100%);
right: auto;
border-radius: 0px 25px 25px 0px;
padding: {
left: 80px;
right: 20px;
}
}
}
}
import React, { FunctionComponent, useState, useEffect, MouseEvent, HTMLProps } from 'react'
import { Icon } from '../icon/icon'
import { Overlay } from '../overlay/overlay'
import classNames from 'classnames'
import './fixednav.scss'
export interface FixedNavProps {
prefixCls: string
visible: boolean
overlay: boolean
navList: Array<object>
activeText: string
unActiveText: string
position: object
type: string
change: Function
selected: Function
slotList: HTMLProps<HTMLElement>
slotBtn: HTMLProps<HTMLElement>
}
const defaultProps = {
prefixCls: 'nut-fixednav',
activeText: '收起导航',
unActiveText: '快速导航',
position: {
top: 'auto',
bottom: 'auto',
},
type: 'right',
} as FixedNavProps
export const FixedNav: FunctionComponent<
Partial<FixedNavProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => {
const {
prefixCls,
children,
overlay,
visible,
navList,
activeText,
unActiveText,
position,
change,
selected,
type,
slotList,
slotBtn,
} = { ...defaultProps, ...props }
const selectCb = (event: MouseEvent, item: any) => {
selected(item, event)
}
const updateValue = (value: boolean = !visible) => {
change(value)
}
const [classNames, setClassNames] = useState('')
const classes = () => {
return `${prefixCls} ${type} ${visible ? 'active' : ''}`
}
useEffect(() => {
setClassNames(classes())
}, [visible])
return (
<div className={`${classNames}`} style={position}>
{overlay && (
<Overlay visible={visible} zIndex={200} onClick={() => updateValue(false)}></Overlay>
)}
<div className="list">
{slotList ? (
slotList
) : (
<div className="nut-fixednav__list">
{navList.map((item: any, index) => {
return (
<div
className="nut-fixednav__list-item"
onClick={(event) => selectCb(event, item)}
key={item.id || index}
>
<img src={item.icon} />
<div className="span">{item.text}</div>
{item.num && <div className="b">{item.num}</div>}
</div>
)
})}
</div>
)}
</div>
<div className="nut-fixednav__btn" onClick={() => updateValue()}>
{slotBtn ? (
slotBtn
) : (
<>
<Icon name="left" color="#fff" />
<div className="text">{visible ? activeText : unActiveText}</div>
</>
)}
</div>
</div>
)
}
FixedNav.defaultProps = defaultProps
FixedNav.displayName = 'NutFixedNav'
import { FixedNav } from './fixednav'
export default FixedNav
.box-item {
background: #fff;
margin-bottom: 20px;
padding: 20px 0;
}
.demo .nut-row {
overflow: hidden;
&:not(:last-child) .nut-col {
margin-bottom: 15px;
}
}
.flex-content {
line-height: 40px;
color: #fff;
text-align: center;
border-radius: 6px;
background: #ff8881;
&.flex-content-light {
background: #ffc7c4;
}
&.flex-content-height {
height: 50px;
}
}
.demo {
padding-left: 0px !important;
padding-right: 0px !important;
}
import React from 'react'
import Row from '@/packages/row'
import Col from '@/packages/col'
import './demo.scss'
const LayoutDemo = () => {
return (
<>
<div className="demo">
<h2>基础布局</h2>
<div className="box-item">
<Row>
<Col span="24">
<div className="flex-content">span:24</div>
</Col>
</Row>
<Row>
<Col span="12">
<div className="flex-content">span:12</div>
</Col>
<Col span="12">
<div className="flex-content flex-content-light">span:12</div>
</Col>
</Row>
<Row>
<Col span="8">
<div className="flex-content">span:8</div>
</Col>
<Col span="8">
<div className="flex-content flex-content-light">span:8</div>
</Col>
<Col span="8">
<div className="flex-content">span:8</div>
</Col>
</Row>
<Row>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content flex-content-light">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content flex-content-light">span:6</div>
</Col>
</Row>
</div>
<h2>分栏间隔</h2>
<div className="box-item">
<Row gutter="10">
<Col span="8">
<div className="flex-content">span:8</div>
</Col>
<Col span="8">
<div className="flex-content flex-content-light">span:8</div>
</Col>
<Col span="8">
<div className="flex-content">span:8</div>
</Col>
</Row>
</div>
<h2>Flex布局</h2>
<div className="box-item">
<Row type="flex" wrap="nowrap">
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content flex-content-light">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
</Row>
<Row type="flex" justify="center">
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content flex-content-light">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
</Row>
<Row type="flex" justify="end">
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content flex-content-light">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
</Row>
<Row type="flex" justify="space-between">
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content flex-content-light">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
</Row>
<Row type="flex" justify="space-around">
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content flex-content-light">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
</Row>
</div>
</div>
</>
)
}
export default LayoutDemo
# Layout布局
### 介绍
用于快速进行布局
### 安装
```javascript
import { createApp } from 'vue'
import { Layout,Row,Col } from '@nutui/nutui'
const app = createApp()
app.use(Layout);
app.use(Row);
app.use(Col);
```
## 代码演示
### 基础布局
```tsx
<Row>
<Col span="24">
<div className="flex-content">span:24</div>
</Col>
</Row>
<Row>
<Col span="12">
<div className="flex-content">span:12</div>
</Col>
<Col span="12">
<div className="flex-content flex-content-light">span:12</div>
</Col>
</Row>
<Row>
<Col span="8">
<div className="flex-content">span:8</div>
</Col>
<Col span="8">
<div className="flex-content flex-content-light">span:8</div>
</Col>
<Col span="8">
<div className="flex-content flex-content-light">span:8</div>
</Col>
</Row>
<Row>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
</Row>
```
### 分栏间隔
```tsx
<Row gutter="10">
<Col span="8">
<div className="flex-content">span:8</div>
</Col>
<Col span="8">
<div className="flex-content flex-content-light">span:8</div>
</Col>
<Col span="8">
<div className="flex-content flex-content-light">span:8</div>
</Col>
</Row>
```
### Flex布局
```tsx
<Row type="flex" wrap="nowrap">
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
</Row>
<Row ttype="flex" justify="center">
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
</Row>
<Row ttype="flex" justify="end">
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
</Row>
<Row ttype="flex" justify="space-between">
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
</Row>
<Row ttype="flex" justify="space-around">
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
<Col span="6">
<div className="flex-content">span:6</div>
</Col>
</Row>
```
## API
### row
| 字段 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| type | 布局方式,可选值为flex | String | - |
| gutter | 列元素之间的间距(单位为px) | String、Number | 0 |
| justify | Flex 主轴对齐方式,可选值为 start end center space-around space-between| String | start |
| align | Flex 交叉轴对齐方式,可选值为 flex-start center flex-end | String | flex-start |
| wrap | Flex是否换行,可选值为 nowrap wrap reverse | String | nowrap |
### col
| 字段 | 说明 | 类型 | 默认值 |
|--------|----------------|--------------| ------------------|
| span | 列元素宽度(共分为24份,例如设置一行3个,那么span值为8) | String、Number| 24|
| offset | 列元素偏移距离 | String、Number| 0|
import { Layout } from './layout'
export default Layout
import React, { FunctionComponent } from 'react'
import './layout.scss'
export interface LayoutProps {}
const defaultProps = {} as LayoutProps
export const Layout: FunctionComponent<
Partial<LayoutProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => {
const { children } = { ...defaultProps, ...props }
return <div className="nut-layout">Layout</div>
}
Layout.defaultProps = defaultProps
Layout.displayName = 'NutLayout'
......@@ -5,6 +5,9 @@ import Icon from './icon'
import Price from './price'
import Overlay from './overlay'
import Divider from './divider'
import Layout from './layout'
import Row from './row'
import Col from './col'
import Drag from './drag'
import Collapse from './collapse'
import Toast from './toast'
......@@ -13,16 +16,20 @@ import Range from './range'
import Infiniteloading from './infiniteloading'
import Steps from './steps'
import CircleProgress from './circleprogress'
import Popup from './popup'
import NavBar from './navbar'
import Tabbar from './tabbar'
import InputNumber from './inputnumber'
import Elevator from './elevator'
import FixedNav from './fixednav'
import Rate from './rate'
import Uploader from './uploader'
import Input from './input'
import TextArea from './textarea'
import CheckBox from './checkbox'
import AnimatingNumbers from './animatingnumbers'
import Tag from './tag'
import Badge from './badge'
import Signature from './signature'
export {
......@@ -33,6 +40,9 @@ export {
Price,
Overlay,
Divider,
Layout,
Row,
Col,
Drag,
Collapse,
Toast,
......@@ -41,15 +51,19 @@ export {
Infiniteloading,
Steps,
CircleProgress,
Popup,
NavBar,
Tabbar,
InputNumber,
Elevator,
FixedNav,
Rate,
Uploader,
Input,
TextArea,
CheckBox,
AnimatingNumbers,
Tag,
Badge,
Signature,
}
......@@ -38,8 +38,8 @@ import { OverLay } from '@nutui/nutui-react';
| visible | 当前组件是否显示 | Boolean | `false` |
| zIndex | 遮罩层级 | Number | 2000 |
| duration | 动画时长,单位秒 | Number | 0.3 |
| className | 自定义遮罩类名 | String | - |
| style | 自定义遮罩样式 | CSSProperties | - |
| overlayClass | 自定义遮罩类名 | String | - |
| overlayStyle | 自定义遮罩样式 | CSSProperties | - |
| lockScroll | 背景是否锁定 | Boolean | `false` |
| closeOnClickOverlay | 是否点击遮罩关闭 | Boolean | `true` |
......
......@@ -6,20 +6,20 @@ import './overlay.scss'
export interface OverlayProps {
zIndex: number
duration: number
className: string
style: React.CSSProperties
overlayClass: string
overlayStyle: React.CSSProperties
closeOnClickOverlay: boolean
visible: boolean
lockScroll: boolean
}
const defaultProps = {
export const defaultOverlayProps = {
zIndex: 2000,
duration: 0.3,
className: '',
overlayClass: '',
closeOnClickOverlay: true,
visible: false,
lockScroll: true,
style: {},
overlayStyle: {},
} as OverlayProps
export const Overlay: FunctionComponent<
Partial<OverlayProps> & React.HTMLAttributes<HTMLDivElement>
......@@ -31,14 +31,14 @@ export const Overlay: FunctionComponent<
children,
zIndex,
duration,
className,
overlayClass,
closeOnClickOverlay,
visible,
lockScroll,
style,
overlayStyle,
...rest
} = {
...defaultProps,
...defaultOverlayProps,
...props,
}
......@@ -63,14 +63,14 @@ export const Overlay: FunctionComponent<
'first-render': renderRef.current && !visible,
'hidden-render': show,
},
className,
overlayClass,
b('')
)
const styles = {
zIndex: zIndex,
animationDuration: `${props.duration}s`,
...style,
...overlayStyle,
}
const lock = () => {
......@@ -101,5 +101,5 @@ export const Overlay: FunctionComponent<
)
}
Overlay.defaultProps = defaultProps
Overlay.defaultProps = defaultOverlayProps
Overlay.displayName = 'NutOverlay'
.demo-popup {
color: #fff;
}
import React, { useState } from 'react'
import Cell from '@/packages/cell'
import Popup from '@/packages/popup'
import './demo.scss'
const PopupDemo = () => {
const [showBasic, setShowBasic] = useState(false)
const [showTop, setShowTop] = useState(false)
const [showBottom, setShowBottom] = useState(false)
const [showLeft, setShowLeft] = useState(false)
const [showRight, setShowRight] = useState(false)
const [showIcon, setShowIcon] = useState(false)
const [showIconPosition, setShowIconPosition] = useState(false)
const [showIconDefine, setShowIconDefine] = useState(false)
const [showBottomRound, setShowBottomRound] = useState(false)
return (
<>
<div className="demo">
<h2>基础用法</h2>
<Cell
title="展示弹出层"
isLink
onClick={() => {
setShowBasic(true)
}}
></Cell>
<Popup
visible={showBasic}
style={{ padding: '30px 50px' }}
onClose={() => {
setShowBasic(false)
}}
>
正文
</Popup>
<h2>弹出位置</h2>
<Cell
title="顶部弹出"
isLink
onClick={() => {
setShowTop(true)
}}
></Cell>
<Popup
visible={showTop}
style={{ height: '20%' }}
position="top"
onClose={() => {
setShowTop(false)
}}
></Popup>
<Cell
title="底部弹出"
isLink
onClick={() => {
setShowBottom(true)
}}
></Cell>
<Popup
visible={showBottom}
style={{ height: '20%' }}
position="bottom"
onClose={() => {
setShowBottom(false)
}}
></Popup>
<Cell
title="左侧弹出"
isLink
onClick={() => {
setShowLeft(true)
}}
></Cell>
<Popup
visible={showLeft}
style={{ width: '20%', height: '100%' }}
position="left"
onClose={() => {
setShowLeft(false)
}}
></Popup>
<Cell
title="右侧弹出"
isLink
onClick={() => {
setShowRight(true)
}}
></Cell>
<Popup
visible={showRight}
style={{ width: '20%', height: '100%' }}
position="right"
onClose={() => {
setShowRight(false)
}}
></Popup>
<h2>关闭图标</h2>
<Cell
title="关闭图标"
isLink
onClick={() => {
setShowIcon(true)
}}
></Cell>
<Popup
closeable
visible={showIcon}
style={{ height: '20%' }}
position="bottom"
onClose={() => {
setShowIcon(false)
}}
></Popup>
<Cell
title="图标位置"
isLink
onClick={() => {
setShowIconPosition(true)
}}
></Cell>
<Popup
closeable
visible={showIconPosition}
style={{ height: '20%' }}
closeIconPosition="top-left"
position="bottom"
onClose={() => {
setShowIconPosition(false)
}}
></Popup>
<Cell
title="自定义图标"
isLink
onClick={() => {
setShowIconDefine(true)
}}
></Cell>
<Popup
closeable
visible={showIconDefine}
style={{ height: '20%' }}
closeIcon="heart"
position="bottom"
onClose={() => {
setShowIconDefine(false)
}}
></Popup>
<h2>圆角弹框</h2>
<Cell
title="圆角弹框"
isLink
onClick={() => {
setShowBottomRound(true)
}}
></Cell>
<Popup
closeable
visible={showBottomRound}
style={{ height: '20%' }}
position="bottom"
round
onClose={() => {
setShowBottomRound(false)
}}
></Popup>
</div>
</>
)
}
export default PopupDemo
# Popup 弹出层
### 介绍
弹出层容器,用于展示弹窗、信息提示等内容
### 安装
``` javascript
import { Popup } from '@nutui/nutui-react';
```
## 代码演示
### 基础用法
`visible` 控制显示/隐藏
```html
<Popup visible={ showBasic } style={{ padding: '30px 50px' }} onClose={ () => { setShowBasic(false) } }>正文</Popup>
```
### 弹出位置
```html
<Popup visible={ showTop } style={{ height: '20%' }} position="top" onClose={ () => { setShowTop(false) } }></Popup>
<Popup visible={ showBottom } style={{ height: '20%' }} position="bottom" onClose={ () => { setShowBottom(false) } }></Popup>
<Popup visible={ showLeft } style={{ width: '20%', height: '100%' }} position="left" onClose={ () => { setShowLeft(false) } }></Popup>
<Popup visible={ showRight } style={{ width: '20%', height: '100%' }} position="right" onClose={ () => { setShowRight(false) } }></Popup>
```
### 图标
```html
<Popup closeable visible={ showIcon } style={{ height: '20%' }} position="bottom" onClose={ () => { setShowIcon(false) } }></Popup>
<Popup closeable visible={ showIconPosition } style={{ height: '20%' }} closeIconPosition="top-left" position="bottom" onClose={ () => { setShowIconPosition(false) } }></Popup>
<Popup closeable visible={ showIconDefine } style={{ height: '20%' }} closeIcon="heart" position="bottom" onClose={ () => { setShowIconDefine(false) } }></Popup>
```
### 圆角弹框
```html
<Popup closeable visible={ showBottomRound } style={{ height: '20%' }} position="bottom" round onClose={ () => { setShowBottomRound(false) } }></Popup>
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
|------------------------|-------------------------------------------------------------|----------------|---------------|
| visible | 当前组件是否显示 | Boolean | `false` |
| z-index | 遮罩层级 | String、Number | `2000` |
| duration | 遮罩动画时长,单位秒 | Number | `0.3` |
| overlay-class | 自定义遮罩类名 | String | - |
| overlay-style | 自定义遮罩样式 | CSSProperties | - |
| lock-scroll | 背景是否锁定 | Boolean | `true` |
| overlay | 是否显示遮罩 | Boolean | `true` |
| close-on-click-overlay | 是否点击遮罩关闭 | Boolean | `true` |
| position | 弹出位置(top,bottom,left,right,center) | String | `"center"` |
| transition | 动画名 | String | - |
| style | 自定义弹框样式 | CSSProperties | - |
| pop-class | 自定义弹框类名 | String | - |
| closeable | 是否显示关闭按钮 | Boolean | `false` |
| close-icon-position | 关闭按钮位置(top-left,top-right,bottom-left,bottom-right) | String | `"top-right"` |
| close-icon | 自定义 Icon | String | `"close"` |
| destroy-on-close | 组件销毁后是否关闭 | Boolean | `true` |
| round | 是否显示圆角 | Boolean | `false` |
### Events
| 事件名 | 说明 | 回调参数 |
|------------------|------------------------|----------------|
| click | 点击弹框时触发 | `event: MouseEvent` |
| click-close-icon | 点击关闭图标时触发 | `event: MouseEvent` |
| open | 打开弹框时触发 | - |
| close | 关闭弹框时触发 | - |
| opend | 遮罩打开动画结束时触发 | `event: HTMLElement` |
| closed | 遮罩关闭动画结束时触发 | `event: HTMLElement` |
| click-overlay | 点击遮罩触发 | `event: MouseEvent` |
import { Popup } from './popup'
export default Popup
.popup-slide {
&-center-enter,
&-center-exit {
opacity: 0;
}
&-center-enter-active {
opacity: 1;
transition: opacity 0.5s ease;
}
&-top-enter,
&-top-exit {
transform: translate(0, -100%);
}
&-top-enter-active {
transform: translate(0, 0);
}
&-right-enter,
&-right-exit {
transform: translate(100%, 0);
}
&-right-enter-active {
transform: translate(0, 0);
}
&-bottom-enter,
&-bottom-exit {
transform: translate(0, 100%);
}
&-bottom-enter-active {
transform: translate(0, 0);
}
&-left-enter,
&-left-exit {
transform: translate(-100%, 0);
}
&-left-enter-active {
transform: translate(0, 0);
}
}
.popup-center {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
&.round {
border-radius: $popup-border-radius;
}
}
.popup-bottom {
bottom: 0;
left: 0;
width: 100%;
&.round {
border-radius: $popup-border-radius $popup-border-radius 0 0;
}
}
.popup-right {
top: 0;
right: 0;
&.round {
border-radius: $popup-border-radius 0 0 $popup-border-radius;
}
}
.popup-left {
top: 0;
left: 0;
&.round {
border-radius: 0 $popup-border-radius $popup-border-radius 0;
}
}
.popup-top {
top: 0;
left: 0;
width: 100%;
&.round {
border-radius: 0 0 $popup-border-radius $popup-border-radius;
}
}
.nut-popup {
position: fixed;
max-height: 100%;
overflow-y: auto;
background-color: $white;
transition: transform 0.2s;
-webkit-overflow-scrolling: touch;
}
.nut-overflow-hidden {
overflow: hidden !important;
}
.nutui-popup {
&__close-icon {
position: absolute !important;
z-index: 1;
color: #969799;
font-size: 18px;
cursor: pointer;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
&:active {
opacity: 0.7;
}
&--top-left {
top: $popup-close-icon-margin;
left: $popup-close-icon-margin;
}
&--top-right {
top: $popup-close-icon-margin;
right: $popup-close-icon-margin;
}
&--bottom-left {
bottom: $popup-close-icon-margin;
left: $popup-close-icon-margin;
}
&--bottom-right {
right: $popup-close-icon-margin;
bottom: $popup-close-icon-margin;
}
}
}
.fade-enter {
transform: translate(0, -100%);
}
.fade-enter-active {
transform: translate(0, 0);
}
.fade-exit {
transform: translate(0, -100%);
}
import React, {
FunctionComponent,
useState,
useEffect,
MouseEventHandler,
MouseEvent,
EventHandler,
} from 'react'
import { CSSTransition } from 'react-transition-group'
import { OverlayProps, defaultOverlayProps } from '@/packages/overlay/overlay'
import Icon from '@/packages/icon'
import Overlay from '@/packages/overlay'
import classNames from 'classnames'
import bem from '@/utils/bem'
import './popup.scss'
import { EnterHandler, ExitHandler } from 'react-transition-group/Transition'
interface PopupProps extends OverlayProps {
position: string
transition: string
style: React.CSSProperties
popClass: string
closeable: boolean
closeIconPosition: string
closeIcon: string
destroyOnClose: boolean
// teleport: string | HTMLElement
overlay: boolean
round: boolean
onOpen: () => void
onClose: () => void
onClick: (e: MouseEvent) => void
onOpened: (e: HTMLElement) => void
onClosed: (e: HTMLElement) => void
onClickOverlay: (e: MouseEvent) => void
onClickCloseIcon: (e: MouseEvent) => void
}
const defaultProps = {
position: 'center',
transition: '',
style: {},
popClass: '',
closeable: false,
closeIconPosition: 'top-right',
closeIcon: 'close',
destroyOnClose: true,
// teleport: 'body',
overlay: true,
round: false,
onOpen: () => {},
onClose: () => {},
onClick: (e: MouseEvent) => {},
onOpened: (e: HTMLElement) => {},
onClosed: (e: HTMLElement) => {},
onClickOverlay: (e: MouseEvent) => {},
onClickCloseIcon: (e: MouseEvent) => {},
...defaultOverlayProps,
} as PopupProps
let _zIndex = 2000
export const Popup: FunctionComponent<Partial<PopupProps> & React.HTMLAttributes<HTMLDivElement>> =
(props) => {
const {
children,
visible,
closeOnClickOverlay,
overlayStyle,
zIndex,
lockScroll,
duration,
closeable,
closeIconPosition,
closeIcon,
style,
transition,
round,
position,
popClass,
destroyOnClose,
onOpen,
onClose,
onClickOverlay,
onClickCloseIcon,
onOpened,
onClosed,
onClick,
} = props
const [index, setIndex] = useState(zIndex ? zIndex : _zIndex)
const [innerVisible, setInnerVisible] = useState(visible)
const [showChildren, setShowChildren] = useState(true)
const [transitionName, setTransitionName] = useState('')
const b = bem('popup')
const baseStyle = {
zIndex: index,
animationDuration: `${duration}s`,
}
const overlayStyles = {
...overlayStyle,
...baseStyle,
}
const popStyles = {
...style,
...baseStyle,
}
const classes = classNames(
{
['round']: round,
[`popup-${position}`]: true,
[`${popClass}`]: true,
},
b('')
)
const closeClasses = classNames({
'nutui-popup__close-icon': true,
[`nutui-popup__close-icon--${closeIconPosition}`]: true,
})
const open = () => {
if (!innerVisible) {
// if(zIndex !== undefined) {
// _zIndex = +zIndex;
// }
setInnerVisible(visible)
setIndex(++_zIndex)
}
if (destroyOnClose) {
setShowChildren(true)
}
onOpen && onOpen()
}
const close = () => {
if (innerVisible) {
setInnerVisible(!visible)
if (destroyOnClose) {
setTimeout(() => {
setShowChildren(false)
onClose && onClose()
}, Number(duration) * 1000)
}
}
}
const onHandleClickOverlay = (e: MouseEvent) => {
if (closeOnClickOverlay) {
onClickOverlay && onClickOverlay(e)
close()
}
}
const onHandleClick: MouseEventHandler<HTMLDivElement> = (e: MouseEvent) => {
onClick && onClick(e)
}
const onHandleClickCloseIcon: MouseEventHandler<HTMLDivElement> = (e: MouseEvent) => {
onClickCloseIcon && onClickCloseIcon(e)
close()
}
const onHandleOpened: EnterHandler<HTMLElement | undefined> | undefined = (e: HTMLElement) => {
onOpened && onOpened(e)
}
const onHandleClosed: ExitHandler<HTMLElement | undefined> | undefined = (e: HTMLElement) => {
onClosed && onClosed(e)
}
useEffect(() => {
visible && open()
}, [visible])
useEffect(() => {
setTransitionName(transition ? transition : `popup-slide-${position}`)
}, [position])
return (
<React.Fragment>
<Overlay
style={overlayStyles}
visible={innerVisible}
closeOnClickOverlay={closeOnClickOverlay}
zIndex={zIndex}
lockScroll={lockScroll}
duration={duration}
onClick={onHandleClickOverlay}
></Overlay>
<CSSTransition
classNames={transitionName}
unmountOnExit
timeout={500}
in={innerVisible}
onEntered={onHandleOpened}
onExited={onHandleClosed}
>
<div style={popStyles} className={classes} onClick={onHandleClick}>
{showChildren ? children : ''}
{closeable ? (
<div className={closeClasses} onClick={onHandleClickCloseIcon}>
<Icon name={closeIcon} size="12px"></Icon>
</div>
) : (
''
)}
</div>
</CSSTransition>
</React.Fragment>
)
}
Popup.defaultProps = defaultProps
Popup.displayName = 'NutPopup'
import { createContext } from 'react'
export const DataContext = createContext({})
import { Row } from './row'
export default Row
.nut-row {
width: 100%;
&:after {
display: block;
height: 0;
clear: both;
visibility: hidden;
content: '';
}
&-flex {
display: flex;
&:after {
display: none;
}
.nut-col {
float: none;
}
}
&-justify-center {
justify-content: center;
}
&-justify-end {
justify-content: flex-end;
}
&-justify-space-between {
justify-content: space-between;
align-items: center;
}
&-justify-space-around {
justify-content: space-around;
}
&-align-flex-start {
align-items: flex-start;
}
&-align-center {
align-items: center;
}
&-align-flex-end {
align-items: flex-end;
}
&-flex-wrap {
flex-wrap: wrap;
}
&-flex-nowrap {
flex-wrap: nowrap;
}
&-flex-reverse {
flex-wrap: wrap-reverse;
}
}
import React, { FunctionComponent } from 'react'
import { DataContext } from './UserContext'
import './row.scss'
export interface RowProps {
type: string
justify: string
align: string
wrap: string
gutter: string | number
}
const defaultProps = {
type: '',
justify: 'start',
align: 'flex-start',
wrap: 'nowrap',
gutter: '0',
} as RowProps
export const Row: FunctionComponent<Partial<RowProps> & React.HTMLAttributes<HTMLDivElement>> = (
props
) => {
const { children, type, justify, align, wrap, gutter } = { ...defaultProps, ...props }
const prefixCls = 'nut-row'
const getClass = (prefix: string, type: string) => {
return prefix ? (type ? `nut-row-${prefix}-${type}` : '') : `nut-row-${type}`
}
const getClasses = () => {
return `
${getClass('', type)}
${getClass('justify', justify)}
${getClass('align', align)}
${getClass('flex', wrap)}
${prefixCls}
`
}
const parentRow = {
gutter: gutter,
}
return (
<DataContext.Provider value={parentRow}>
{/* <div className={`${getClasses()}`}>
{children}
</div> */}
{React.createElement(
'div',
{
className: getClasses(),
},
children
)}
</DataContext.Provider>
)
}
Row.defaultProps = defaultProps
Row.displayName = 'NutRow'
import React, { useState } from 'react'
import { Tag } from './tag'
import Cell from '@/packages/cell'
import CellGroup from '@/packages/cellgroup'
const TagDemo = () => {
const [isShow, setIsShow] = useState(true)
const close = () => {
setIsShow(false)
}
return (
<>
<div className="demo">
<CellGroup title="基础用法">
<Cell title="primary类型" extra={<Tag type="primary">标签</Tag>}></Cell>
<Cell title="success类型" extra={<Tag type="success">标签</Tag>}></Cell>
<Cell title="danger类型" extra={<Tag type="danger">标签</Tag>}></Cell>
<Cell title="warning类型" extra={<Tag type="warning">标签</Tag>}></Cell>
</CellGroup>
<CellGroup title="样式风格">
<Cell title="空心样式" extra={<Tag plain>标签</Tag>}></Cell>
<Cell
title="圆角样式"
extra={
<Tag round type="primary">
标签
</Tag>
}
></Cell>
<Cell
title="标记样式"
extra={
<Tag mark type="primary">
标签
</Tag>
}
></Cell>
<Cell
title="可关闭标签"
extra={
<Tag isShow={isShow} closeable onClick={close} type="primary">
标签
</Tag>
}
></Cell>
</CellGroup>
<CellGroup title="自定义">
<Cell title="背景颜色" extra={<Tag color="#FA685D">标签</Tag>}></Cell>
<Cell
title="文字颜色"
extra={
<Tag color="#E9E9E9" textColor="#999999">
标签
</Tag>
}
></Cell>
<Cell
title="空心颜色"
extra={
<Tag color="#FA2400" plain>
标签
</Tag>
}
></Cell>
</CellGroup>
</div>
</>
)
}
export default TagDemo
# Tag 标签
### 介绍
用于标记和分类的标签。
### 安装
``` javascript
import { Tag } from '@nutui/nutui';
```
## 代码实例
### 基础用法
```tsx
<Tag type="primary">标签</Tag>
<Tag type="success">标签</Tag>
<Tag type="danger">标签</Tag>
<Tag type="warning">标签</Tag>
```
### 空心样式
```tsx
<Tag plain>标签</Tag>
```
### 圆角样式
```tsx
<Tag round type="primary">标签</Tag>
```
### 标记样式
```tsx
<Tag mark type="primary">标签</Tag>
```
### 可关闭标签
```tsx
<Tag isShow={isShow} closeable onClick={close} type="primary">标签</Tag>
```
### 自定义颜色
```tsx
<Tag color="#FA685D">标签</Tag>
<Tag color="#E9E9E9" text-color="#999999">标签</Tag>
<Tag color="#FA2400" plain>标签</Tag>
```
## API
### Props
| 字段 | 说明 | 类型 | 默认值 |
|------------|--------------------------------------------------|---------|-----------|
| type | 标签类型,可选值为primary success danger warning | String | `default` |
| color | 标签颜色 | String | - |
| text-color | 文本颜色,优先级高于color属性 | String | `white` |
| plain | 是否为空心样式 | Boolean | `false` |
| round | 是否为圆角样式 | Boolean | `false` |
| mark | 是否为标记样式 | Boolean | `false` |
| closeable | 是否为可关闭标签 | Boolean | `false` |
### Event
| 事件名称 | 说明 | 回调参数 |
|----------|----------|----------|
| close | 关闭事件 | event |
import { Tag } from './tag'
export default Tag
.nut-tag {
padding: 0 4px;
border-radius: 4px;
._icon {
vertical-align: middle;
margin-left: 4px;
}
&--default {
color: $tag-default-color;
background: $tag-default-background-color;
border: $tag-border-width solid transparent;
}
&--primary {
color: $tag-default-color;
background: $tag-primary-background-color;
border: $tag-border-width solid transparent;
}
&--success {
color: $tag-default-color;
background: $tag-success-background-color;
border: $tag-border-width solid transparent;
}
&--danger {
color: $tag-default-color;
background: $tag-danger-background-color;
border: $tag-border-width solid transparent;
}
&--warning {
color: $tag-default-color;
background: $tag-warning-background-color;
border: $tag-border-width solid transparent;
}
&--plain {
color: $tag-plain-color;
background: $tag-plain-background-color;
border: $tag-border-width solid $tag-plain-color;
}
&--round {
border-radius: 8px;
}
&--mark {
border-radius: 0 12px 12px 0;
}
&--close {
margin-left: 4px;
cursor: pointer;
}
}
import React, {
CSSProperties,
FunctionComponent,
MouseEventHandler,
useEffect,
useState,
} from 'react'
import './tag.scss'
import classNames from 'classnames'
import Icon from '@/packages/icon'
export interface TagProps {
type: TagType
color: string
textColor: string
plain: boolean
round: boolean
mark: boolean
closeable: boolean
isShow: boolean
prefixCls: string
onClick: (e: MouseEvent) => void
}
export type TagType = 'default' | 'primary' | 'success' | 'warning' | 'danger'
const defaultProps = {
type: 'default',
color: '',
textColor: '',
plain: false,
round: false,
mark: false,
closeable: false,
isShow: true,
prefixCls: 'nut-tag',
onClick: (e: MouseEvent) => {},
} as TagProps
export const Tag: FunctionComponent<Partial<TagProps>> = (props) => {
const {
color,
plain,
type,
round,
prefixCls,
children,
mark,
closeable,
textColor,
isShow,
onClick,
} = {
...defaultProps,
...props,
}
const [btnName, setBtnName] = useState('')
useEffect(() => {
setBtnName(classes())
}, [type, color, textColor, plain, round, mark, closeable, prefixCls, isShow, onClick])
const classes = () => {
const prefixCls = 'nut-tag'
return `${prefixCls}
${type ? `${prefixCls}--${type}` : ''}
${plain ? `${prefixCls}--plain` : ''}
${round ? `${prefixCls}--round` : ''}
${mark ? `${prefixCls}--mark` : ''}
${closeable ? `${prefixCls}--close` : ''}`
}
const handleClick = (e: any) => {
if (props.onClick) {
props.onClick(e)
}
}
const getStyle = () => {
const style: CSSProperties = {}
if (textColor) {
style.color = textColor
if (plain) {
style.background = '#fff'
} else {
if (color) {
style.background = color
}
}
} else {
if (color) {
style.color = '#fff'
style.background = color
}
}
return style
}
return (
<div>
{closeable ? (
isShow ? (
<div className={`${btnName}`} style={getStyle()} onClick={(e) => handleClick(e)}>
{children && <span className="text">{children}</span>}
{closeable && (
<Icon className="_icon" name="close" size="12" onClick={(e) => handleClick(e)}></Icon>
)}
</div>
) : (
''
)
) : (
<div className={`${btnName}`} style={getStyle()} onClick={(e) => handleClick(e)}>
{children && <span className="text">{children}</span>}
</div>
)}
</div>
)
}
Tag.defaultProps = defaultProps
Tag.displayName = 'NutTag'
......@@ -267,5 +267,32 @@ $fixednav-font-color: $black;
$fixednav-index: 201;
$fixednav-btn-bg: linear-gradient(135deg, rgba(250, 25, 44, 1) 0%, rgba(250, 63, 25, 1) 100%);
// Tag
$tag-font-size: 12px !default;
$tag-default-background-color: #000000 !default;
$tag-primary-background-color: #3460fa !default;
$tag-success-background-color: #4fc08d !default;
$tag-danger-background-color: linear-gradient(
135deg,
rgba(242, 20, 12, 1) 0%,
rgba(232, 34, 14, 1) 69.83950099728881%,
rgba(242, 77, 12, 1) 100%
) !default;
$tag-warning-background-color: #f3812e !default;
$tag-default-color: #ffffff !default;
$tag-border-width: 1px !default;
$tag-plain-background-color: #fff !default;
$tag-plain-color: rgb(250, 36, 0) !default;
//badge
$badge-background-color: linear-gradient(
135deg,
$primary-color 0%,
$primary-color-end 100%
) !default;
$badge-font-size: $font-size-1 !default;
$badge-default-background-color: rgba(255, 255, 255, 1) !default;
@import './mixins/index';
@import './animation/index';
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册