提交 d6e45d3a 编写于 作者: L liuyijun

fix: 解决冲突合并

...@@ -232,15 +232,15 @@ ...@@ -232,15 +232,15 @@
"show": true, "show": true,
"author": "swag~jun" "author": "swag~jun"
}, },
{ {
"version": "1.0.0", "version": "1.0.0",
"name": "CircleProgress", "name": "CircleProgress",
"type": "component", "type": "component",
"cName": "进度条", "cName": "进度条",
"desc": "展示操作或任务的当前进度。", "desc": "展示操作或任务的当前进度。",
"sort": 7, "sort": 7,
"show": true, "show": true,
"author": "swag~jun" "author": "swag~jun"
} }
] ]
}, },
...@@ -276,6 +276,16 @@ ...@@ -276,6 +276,16 @@
"sort": 3, "sort": 3,
"show": true, "show": true,
"author": "swag~jun" "author": "swag~jun"
},
{
"version": "1.0.0",
"name": "Elevator",
"type": "component",
"cName": "电梯楼层",
"desc": "用于列表快速定位以及索引的显示",
"sort": 4,
"show": true,
"author": "songsong"
} }
] ]
}, },
...@@ -336,7 +346,18 @@ ...@@ -336,7 +346,18 @@
}, },
{ {
"name": "特色组件", "name": "特色组件",
"packages": [] "packages": [
{
"version": "1.0.0",
"name": "Signature",
"type": "component",
"cName": "签字",
"desc": "基于Canvas的签名组件",
"sort": 1,
"show": true,
"author": "songsong"
}
]
} }
] ]
} }
\ No newline at end of file
...@@ -10,22 +10,24 @@ export interface AvatarProps { ...@@ -10,22 +10,24 @@ export interface AvatarProps {
bgColor: string bgColor: string
prefixCls: string prefixCls: string
src: string src: string
className: string
style: React.CSSProperties
} }
export type AvatarSize = 'large' | 'normal' | 'small' export type AvatarSize = 'large' | 'normal' | 'small'
export type AvatarShape = 'round' | 'square' export type AvatarShape = 'round' | 'square'
const defaultProps: AvatarProps = { const defaultProps = {
size: 'normal', size: 'normal',
icon: '', icon: '',
shape: 'round', shape: 'round',
bgColor: '#eee', bgColor: '#eee',
prefixCls: 'nut-avatar', prefixCls: 'nut-avatar',
src: '', src: '',
} } as AvatarProps
export const Avatar: FunctionComponent< export const Avatar: FunctionComponent<
Partial<AvatarProps> & React.HTMLAttributes<HTMLDivElement> Partial<AvatarProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => { > = (props) => {
const { children, prefixCls, size, shape, bgColor, src, icon, className, ...rest } = { const { children, prefixCls, size, shape, bgColor, src, icon, className, style, ...rest } = {
...defaultProps, ...defaultProps,
...props, ...props,
} }
...@@ -41,6 +43,7 @@ export const Avatar: FunctionComponent< ...@@ -41,6 +43,7 @@ export const Avatar: FunctionComponent<
height: sizeValue.indexOf(size) > -1 ? '' : `${size}px`, height: sizeValue.indexOf(size) > -1 ? '' : `${size}px`,
backgroundImage: src ? `url(${src})` : '', backgroundImage: src ? `url(${src})` : '',
backgroundColor: `${bgColor}`, backgroundColor: `${bgColor}`,
...style,
} }
const iconStyles = !!icon && !src ? icon : '' const iconStyles = !!icon && !src ? icon : ''
const handleClick: MouseEventHandler<HTMLDivElement> = (e) => { const handleClick: MouseEventHandler<HTMLDivElement> = (e) => {
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
### 安装 ### 安装
``` javascript ``` javascript
import { Avatar } from '@nutui/nutui'; import { Avatar } from '@nutui/nutui-react';
``` ```
## 代码示例 ## 代码示例
......
.nut-cell { .nut-cell {
position: relative; position: relative;
display: flex; display: flex;
align-items: center;
width: 100%; width: 100%;
line-height: 20px; line-height: 20px;
padding: 13px 16px; padding: 13px 16px;
...@@ -57,11 +58,12 @@ ...@@ -57,11 +58,12 @@
flex: 1; flex: 1;
&--icon { &--icon {
flex-direction: row; flex-direction: row;
.icon { overflow: hidden;
margin-right: 10px;
}
} }
} }
&__icon {
margin-right: 10px;
}
&__subtitle { &__subtitle {
font-size: $cell-title-desc-font; font-size: $cell-title-desc-font;
} }
......
...@@ -14,7 +14,7 @@ export interface CellProps { ...@@ -14,7 +14,7 @@ export interface CellProps {
replace: boolean replace: boolean
url: string url: string
icon: string icon: string
classPrefix: string className: string
extra: ReactNode extra: ReactNode
click: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void click: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
} }
...@@ -28,7 +28,7 @@ const defaultProps = { ...@@ -28,7 +28,7 @@ const defaultProps = {
replace: false, replace: false,
url: '', url: '',
icon: '', icon: '',
classPrefix: 'nutui-cell', className: '',
extra: '', extra: '',
click: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {}, click: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {},
} as CellProps } as CellProps
...@@ -42,7 +42,7 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H ...@@ -42,7 +42,7 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H
to, to,
url, url,
replace, replace,
classPrefix, className,
descTextAlign, descTextAlign,
desc, desc,
icon, icon,
...@@ -65,12 +65,16 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H ...@@ -65,12 +65,16 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H
} }
} }
const styles = { const styles =
textAlign: descTextAlign, title || subTitle || icon
} ? {}
: {
textAlign: descTextAlign,
flex: 1,
}
return ( return (
<div <div
className={`${b({ clickable: isLink || to ? true : false }, [classPrefix])} `} className={`${b({ clickable: isLink || to ? true : false }, [className])} `}
onClick={(event) => handleClick(event)} onClick={(event) => handleClick(event)}
{...rest} {...rest}
> >
...@@ -80,12 +84,8 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H ...@@ -80,12 +84,8 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H
<> <>
{title || subTitle || icon ? ( {title || subTitle || icon ? (
<> <>
{icon ? (
<div className={`${b('icon')}`}>
<Icon name={icon} />
</div>
) : null}
<div className={`${b('title', { icon: icon ? true : false })}`}> <div className={`${b('title', { icon: icon ? true : false })}`}>
{icon ? <Icon name={icon} className={`${b('icon')}`} /> : null}
{subTitle ? ( {subTitle ? (
<> <>
<div className={b('maintitle')}>{title}</div> <div className={b('maintitle')}>{title}</div>
...@@ -98,18 +98,14 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H ...@@ -98,18 +98,14 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H
</> </>
) : null} ) : null}
{desc ? ( {desc ? (
<div className={b('desc')} style={styles as CSSProperties}> <div className={b('desc')} style={styles as React.CSSProperties}>
{desc} {desc}
</div> </div>
) : null} ) : null}
</> </>
)} )}
{extra ? extra : null} {extra ? extra : null}
{!extra && (isLink || to) ? ( {!extra && (isLink || to) ? <Icon name="right" className={b('link')}></Icon> : null}
<div className={b('link')}>
<Icon name="right"></Icon>
</div>
) : null}
</div> </div>
) )
} }
......
import React, { FunctionComponent } from 'react' import React, { FunctionComponent } from 'react'
import bem from '@/utils/bem' import bem from '@/utils/bem'
import classNames from 'classnames'
import './circleprogress.scss' import './circleprogress.scss'
export interface CircleProgressProps { export interface CircleProgressProps {
...@@ -7,6 +8,7 @@ export interface CircleProgressProps { ...@@ -7,6 +8,7 @@ export interface CircleProgressProps {
progress: string | number progress: string | number
isAuto: boolean isAuto: boolean
progressOption: object progressOption: object
className: string
} }
const defaultProps = { const defaultProps = {
strokeInnerWidth: 10, strokeInnerWidth: 10,
...@@ -17,11 +19,21 @@ const defaultProps = { ...@@ -17,11 +19,21 @@ const defaultProps = {
export const CircleProgress: FunctionComponent< export const CircleProgress: FunctionComponent<
Partial<CircleProgressProps> & React.HTMLAttributes<HTMLDivElement> Partial<CircleProgressProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => { > = (props) => {
const { children, strokeInnerWidth, progress, isAuto, progressOption } = { const {
children,
strokeInnerWidth,
progress,
isAuto,
progressOption,
className,
style,
...restProps
} = {
...defaultProps, ...defaultProps,
...props, ...props,
} }
const b = bem('circleprogress') const b = bem('circleprogress')
const classes = classNames(className, b(''))
const option = () => { const option = () => {
// 所有进度条的可配置项 // 所有进度条的可配置项
let baseOption = { let baseOption = {
...@@ -41,13 +53,18 @@ export const CircleProgress: FunctionComponent< ...@@ -41,13 +53,18 @@ export const CircleProgress: FunctionComponent<
baseOption.startPosition = 'rotate(-90,' + baseOption.cx + ',' + baseOption.cy + ')' baseOption.startPosition = 'rotate(-90,' + baseOption.cx + ',' + baseOption.cy + ')'
return baseOption return baseOption
} }
const styles: React.CSSProperties = {
height: `${option().size}px`,
width: `${option().size}px`,
...style,
}
const arcLength = () => { const arcLength = () => {
let circleLength = Math.floor(2 * Math.PI * option().radius) let circleLength = Math.floor(2 * Math.PI * option().radius)
let progressLength = ((progress as number) / 100) * circleLength let progressLength = ((progress as number) / 100) * circleLength
return `${progressLength},${circleLength}` return `${progressLength},${circleLength}`
} }
return ( return (
<div className={`${b()}`} style={{ height: `${option().size}px`, width: `${option().size}px` }}> <div className={classes} style={styles} {...restProps}>
<svg height={option().size} width={option().size} x-mlns="http://www.w3.org/200/svg"> <svg height={option().size} width={option().size} x-mlns="http://www.w3.org/200/svg">
<circle <circle
r={option().radius} r={option().radius}
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
### 安装 ### 安装
``` javascript ``` javascript
import { CirecleProgress } from '@nutui/nutui'; import { CirecleProgress } from '@nutui/nutui-react';
``` ```
## 代码演示 ## 代码演示
......
...@@ -4,4 +4,5 @@ ...@@ -4,4 +4,5 @@
z-index: 9997 !important; z-index: 9997 !important;
width: fit-content; width: fit-content;
height: fit-content; height: fit-content;
touch-action: none;
} }
import React, { FunctionComponent, useState, useEffect, useRef } from 'react' import React, { FunctionComponent, useState, useEffect, useRef } from 'react'
import './drag.scss' import './drag.scss'
import bem from '@/utils/bem'
export interface DragProps { export interface DragProps {
attract: boolean attract: boolean
...@@ -10,7 +11,8 @@ export interface DragProps { ...@@ -10,7 +11,8 @@ export interface DragProps {
right: number right: number
bottom: number bottom: number
} }
style: any className: string
style: React.CSSProperties
} }
const defaultProps = { const defaultProps = {
attract: false, attract: false,
...@@ -21,12 +23,16 @@ const defaultProps = { ...@@ -21,12 +23,16 @@ const defaultProps = {
right: 0, right: 0,
bottom: 0, bottom: 0,
}, },
style: {}, className: '',
} as DragProps } as DragProps
export const Drag: FunctionComponent<Partial<DragProps> & React.HTMLAttributes<HTMLDivElement>> = ( export const Drag: FunctionComponent<Partial<DragProps> & React.HTMLAttributes<HTMLDivElement>> = (
props props
) => { ) => {
const { attract, direction, boundary, style, children } = { ...defaultProps, ...props } const { attract, direction, boundary, children, className, ...reset } = {
...defaultProps,
...props,
}
const b = bem('drag')
const elWidth = useRef(0) const elWidth = useRef(0)
const elHeight = useRef(0) const elHeight = useRef(0)
const screenWidth = useRef(0) const screenWidth = useRef(0)
...@@ -164,8 +170,8 @@ export const Drag: FunctionComponent<Partial<DragProps> & React.HTMLAttributes<H ...@@ -164,8 +170,8 @@ export const Drag: FunctionComponent<Partial<DragProps> & React.HTMLAttributes<H
return ( return (
<div <div
className="nut-drag" className={`${b()} ${className}`}
style={style} {...reset}
ref={myDrag} ref={myDrag}
onTouchStart={(event) => touchStart(event)} onTouchStart={(event) => touchStart(event)}
> >
......
import React from 'react'
import { Elevator } from './elevator'
const ElevatorDemo = () => {
const dataList = [
{
title: 'A',
list: [
{
name: '安徽',
id: 1,
},
],
},
{
title: 'B',
list: [
{
name: '北京',
id: 2,
},
],
},
{
title: 'G',
list: [
{
name: '广西',
id: 3,
},
{
name: '广东',
id: 4,
},
],
},
{
title: 'H',
list: [
{
name: '湖南',
id: 5,
},
{
name: '湖北',
id: 6,
},
,
{
name: '琥珀',
id: 7,
},
],
},
]
const dataList2 = [
{
num: '',
list: [
{
name: '北京',
id: 1,
},
{
name: '上海',
id: 2,
},
{
name: '深圳',
id: 3,
},
{
name: '广州',
id: 4,
},
{
name: '杭州',
id: 5,
},
],
},
{
num: '',
list: [
{
name: '成都',
id: 6,
},
{
name: '西安',
id: 7,
},
{
name: '天津',
id: 8,
},
{
name: '武汉',
id: 9,
},
{
name: '长沙',
id: 10,
},
{
name: '重庆',
id: 11,
},
{
name: '苏州',
id: 12,
},
{
name: '南京',
id: 13,
},
],
},
{
num: '',
list: [
{
name: '西宁',
id: 14,
},
{
name: '兰州',
id: 15,
},
{
name: '石家庄',
id: 16,
},
{
name: '秦皇岛',
id: 17,
},
{
name: '大连',
id: 18,
},
{
name: '哈尔滨',
id: 19,
},
{
name: '长春',
id: 20,
},
{
name: '太原',
id: 21,
},
],
},
]
const clickItem = (key: string, item: any) => {
console.log(key, JSON.stringify(item))
}
const clickIndex = (key: string) => {
console.log(key)
}
return (
<>
<div className="demo">
<h2>基础用法</h2>
<Elevator
indexList={dataList}
height="260"
clickItem={(key: string, item: any) => clickItem(key, item)}
clickIndex={(key: string) => clickIndex(key)}
></Elevator>
<h2>自定义索引key</h2>
<Elevator
indexList={dataList2}
height="220"
acceptKey="num"
clickItem={(key: string, item: any) => clickItem(key, item)}
clickIndex={(key: string) => clickIndex(key)}
></Elevator>
</div>
</>
)
}
export default ElevatorDemo
# Elevator 组件
### 介绍
用于列表快速定位以及索引的显示
### 安装
## 代码演示
### 基础用法
```tsx
const dataList = [
{
title: 'A',
list: [
{
name: '安徽',
id: 1,
},
],
},
{
title: 'B',
list: [
{
name: '北京',
id: 2,
},
],
},
{
title: 'G',
list: [
{
name: '广西',
id: 3,
},
{
name: '广东',
id: 4,
},
],
},
{
title: 'H',
list: [
{
name: '湖南',
id: 5,
},
{
name: '湖北',
id: 6,
},
,
{
name: '琥珀',
id: 7,
},
],
},
]
const clickItem = (key: string, item: any) => {
console.log(key, JSON.stringify(item))
}
const clickIndex = (key: string) => {
console.log(key)
}
```
```tsx
<Elevator
indexList={dataList}
height="260"
clickItem={(key: string, item: any) => clickItem(key, item)}
clickIndex={(key: string) => clickIndex(key)}
></Elevator>
```
### 自定义索引
## API
```tsx
const dataList = [
{
num: '',
list: [
{
name: '北京',
id: 1,
},
{
name: '上海',
id: 2,
},
{
name: '深圳',
id: 3,
},
{
name: '广州',
id: 4,
},
{
name: '杭州',
id: 5,
},
],
},
{
num: '',
list: [
{
name: '成都',
id: 6,
},
{
name: '西安',
id: 7,
},
{
name: '天津',
id: 8,
},
{
name: '武汉',
id: 9,
},
{
name: '长沙',
id: 10,
},
{
name: '重庆',
id: 11,
},
{
name: '苏州',
id: 12,
},
{
name: '南京',
id: 13,
},
],
},
{
num: '',
list: [
{
name: '西宁',
id: 14,
},
{
name: '兰州',
id: 15,
},
{
name: '石家庄',
id: 16,
},
{
name: '秦皇岛',
id: 17,
},
{
name: '大连',
id: 18,
},
{
name: '哈尔滨',
id: 19,
},
{
name: '长春',
id: 20,
},
{
name: '太原',
id: 21,
},
],
},
]
const clickItem = (key: string, item: any) => {
console.log(key, JSON.stringify(item))
}
const clickIndex = (key: string) => {
console.log(key)
}
```
```tsx
<Elevator
indexList={dataList2}
height="220"
acceptKey="num"
clickItem={(key: string, item: any) => clickItem(key, item)}
clickIndex={(key: string) => clickIndex(key)}
></Elevator>
```
### Props
| 字段 | 说明 | 类型 | 默认值 |
| --------- | -------------- | ----------------------------------------------------------- | --------------------- |
| height | 电梯区域的高度 | Number、String | `200px` |
| acceptKey | 索引 key 值 | String | `title` |
| indexList | 索引列表 | Array(item 需包含 id、name 属性, name 支持传入 html 结构) | `[{id: 0, name: ''}]` |
### Event
| 名称 | 说明 | 回调参数 |
| ---------- | -------- | -------------------------------------- |
| clickItem | 点击内容 | key: string, item: { id: 0, name: '' } |
| clickIndex | 点击索引 | key: string |
.nut-elevator {
width: 100%;
display: block;
position: relative;
&__list {
display: block;
overflow: auto;
&__item {
display: block;
font-size: 12px;
color: #333;
&__code {
display: inline-flex;
position: relative;
height: 35px;
line-height: 35px;
font-size: 14px;
color: #1a1a1a;
padding: 0 20px;
font-weight: 500;
&::after {
content: ' ';
width: 100%;
height: 1px;
position: absolute;
left: 0;
bottom: 7px;
background-color: #eeeff2;
}
}
&__name {
display: flex;
align-items: center;
padding: 0 20px;
height: 30px;
line-height: 30px;
}
}
}
&__code--current {
position: absolute;
right: 60px;
top: 50%;
transform: translateY(-50%);
width: 45px;
height: 45px;
line-height: 45px;
border-radius: 50%;
background: $white;
box-shadow: 0 3px 3px 1px rgba(240, 240, 240, 1);
text-align: center;
}
&__bars {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
padding: 15px 0;
background-color: #eeeff2;
border-radius: 6px;
text-align: center;
z-index: 10;
&__inner {
&__item {
display: block;
padding: 3px;
font-size: 10px;
}
}
}
}
import React, { FunctionComponent, useRef, useEffect, useState } from 'react'
import './elevator.scss'
import bem from '@/utils/bem'
export interface ElevatorProps {
height: number | string
acceptKey: string
indexList: any
className: string
style: React.CSSProperties
clickItem: (key: string, item: ElevatorData) => void
clickIndex: (key: string) => void
}
const defaultProps = {
height: '200px',
acceptKey: 'title',
indexList: [],
className: '',
} as ElevatorProps
interface ElevatorData {
name: string
id: number | string
[key: string]: string | number
}
export const Elevator: FunctionComponent<
Partial<ElevatorProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => {
const { height, acceptKey, indexList, className, clickItem, clickIndex, ...rest } = {
...defaultProps,
...props,
}
const b = bem('elevator')
const spaceHeight = 23
const listview = useRef<HTMLDivElement>(null)
const initData = {
anchorIndex: 0,
listHeight: [] as number[],
listGroup: [] as Element[],
}
const touchState = useRef({
y1: 0,
y2: 0,
})
const [currentIndex, setCurrentIndex] = useState<number>(0)
const [scrollStart, setScrollStart] = useState<boolean>(false)
const state = useRef(initData)
//重置滚动参数
const resetScrollState = () => {
state.current.anchorIndex = 0
setCurrentIndex(0)
setScrollStart(false)
touchState.current = {
y1: 0,
y2: 0,
}
}
const getData = (el: HTMLElement, name: string): string | void => {
const prefix = 'data-'
return el.getAttribute(prefix + name) as string
}
const calculateHeight = () => {
let height = 0
state.current.listHeight.push(height)
for (let i = 0; i < state.current.listGroup.length; i++) {
let item = state.current.listGroup[i]
height += item.clientHeight
state.current.listHeight.push(height)
}
}
const scrollTo = (index: number) => {
if (!index && index !== 0) {
return
}
if (!state.current.listHeight.length) {
calculateHeight()
}
if (index < 0) index = 0
if (index > state.current.listHeight.length - 2) index = state.current.listHeight.length - 2
setCurrentIndex(index)
if (listview.current) {
listview.current.scrollTo(0, state.current.listHeight[index])
}
}
const touchMove = (e: React.TouchEvent<HTMLDivElement>) => {
let firstTouch = e.touches[0]
touchState.current.y2 = firstTouch.pageY
let delta = ((touchState.current.y2 - touchState.current.y1) / spaceHeight) | 0
const cacheIndex = state.current.anchorIndex + delta
setCurrentIndex(cacheIndex)
scrollTo(cacheIndex)
}
const touchEnd = () => {
resetScrollState()
}
const touchStart = (e: React.TouchEvent<HTMLDivElement>) => {
setScrollStart(true)
let index = Number(getData(e.target as HTMLElement, 'index'))
let firstTouch = e.touches[0]
touchState.current.y1 = firstTouch.pageY
state.current.anchorIndex = +index
setCurrentIndex((currentIndex) => currentIndex + index)
scrollTo(index)
const target = e.currentTarget as HTMLElement
target.removeEventListener('touchmove', () => touchMove(e), false)
target.removeEventListener('touchend', touchEnd, false)
target.addEventListener('touchmove', () => touchMove(e), false)
target.addEventListener('touchend', touchEnd, false)
}
const handleClickItem = (key: string, item: ElevatorData) => {
clickItem && clickItem(key, item)
}
const handleClickIndex = (key: string) => {
clickIndex && clickIndex(key)
}
const setListGroup = () => {
if (listview.current) {
const els = listview.current.querySelectorAll('.nut-elevator__list__item')
els.forEach((el: Element) => {
if (el != null && !state.current.listGroup.includes(el)) {
state.current.listGroup.push(el)
}
})
}
}
useEffect(() => {
if (listview.current) {
setListGroup()
}
}, [listview.current])
return (
<div className={`${b()} ${className}`} {...rest}>
<div
className={b('list')}
ref={listview}
style={{ height: isNaN(+height) ? height : `${height}px` }}
>
{indexList.map((item: any) => {
return (
<div className={b('list__item')} key={item[acceptKey]}>
<div className={b('list__item__code')}>{item[acceptKey]}</div>
<>
{item.list.map((subitem: any) => {
return (
<div
className={b('list__item__name')}
key={subitem['id']}
onClick={() => handleClickItem(item[acceptKey], subitem)}
>
{subitem.name}
</div>
)
})}
</>
</div>
)
})}
</div>
{indexList.length && scrollStart ? (
<div className={b('code', { current: true })}> {indexList[currentIndex][acceptKey]}</div>
) : null}
<div className={b('bars')} onTouchStart={(event) => touchStart(event)}>
<div className={b('bars__inner')}>
{indexList.map((item: any, index: number) => {
return (
<div
className={b('bars__inner__item')}
data-index={index}
key={item[acceptKey]}
onClick={() => handleClickIndex(item[acceptKey])}
>
{item[acceptKey]}
</div>
)
})}
</div>
</div>
</div>
)
}
Elevator.defaultProps = defaultProps
Elevator.displayName = 'NutElevator'
import { Elevator } from './elevator'
export default Elevator
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
### 安装 ### 安装
```javascript ```javascript
import { InfiniteLoading } from '@nutui/nutui'; import { InfiniteLoading } from '@nutui/nutui-react';
``` ```
## 代码演示 ## 代码演示
......
import React, { useState, useEffect, useRef, FunctionComponent, useReducer } from 'react' import React, { useState, useEffect, useRef, FunctionComponent } from 'react'
import bem from '@/utils/bem'
import classNames from 'classnames'
import Icon from '@/packages/icon' import Icon from '@/packages/icon'
import './infiniteloading.scss' import './infiniteloading.scss'
...@@ -14,6 +16,8 @@ export interface InfiniteloadingProps { ...@@ -14,6 +16,8 @@ export interface InfiniteloadingProps {
loadIcon: string loadIcon: string
loadTxt: string loadTxt: string
loadMoreTxt: string loadMoreTxt: string
className: string
style: React.CSSProperties
refresh: (param: () => void) => void refresh: (param: () => void) => void
loadMore: (param: () => void) => void loadMore: (param: () => void) => void
scrollChange: (param: number) => void scrollChange: (param: number) => void
...@@ -52,9 +56,11 @@ export const Infiniteloading: FunctionComponent< ...@@ -52,9 +56,11 @@ export const Infiniteloading: FunctionComponent<
loadIcon, loadIcon,
loadTxt, loadTxt,
loadMoreTxt, loadMoreTxt,
className,
refresh, refresh,
loadMore, loadMore,
scrollChange, scrollChange,
...restProps
} = { } = {
...defaultProps, ...defaultProps,
...props, ...props,
...@@ -66,10 +72,12 @@ export const Infiniteloading: FunctionComponent< ...@@ -66,10 +72,12 @@ export const Infiniteloading: FunctionComponent<
const isTouching = useRef(false) const isTouching = useRef(false)
const beforeScrollTop = useRef(0) const beforeScrollTop = useRef(0)
const refreshMaxH = useRef(0) const refreshMaxH = useRef(0)
const x = useRef(0)
const y = useRef(0) const y = useRef(0)
const distance = useRef(0) const distance = useRef(0)
const b = bem('infiniteloading')
const classes = classNames(className, b())
useEffect(() => { useEffect(() => {
const parentElement = getParentElement(scroller.current as HTMLDivElement) as Node & ParentNode const parentElement = getParentElement(scroller.current as HTMLDivElement) as Node & ParentNode
scrollEl.current = useWindow ? window : parentElement scrollEl.current = useWindow ? window : parentElement
...@@ -213,11 +221,12 @@ export const Infiniteloading: FunctionComponent< ...@@ -213,11 +221,12 @@ export const Infiniteloading: FunctionComponent<
return ( return (
<div <div
className="nut-infiniteloading" className={classes}
ref={scroller} ref={scroller}
onTouchStart={(event) => touchStart(event)} onTouchStart={(event) => touchStart(event)}
onTouchMove={(event) => touchMove(event)} onTouchMove={(event) => touchMove(event)}
onTouchEnd={() => touchEnd()} onTouchEnd={() => touchEnd()}
{...restProps}
> >
<div className="nut-infinite-top" ref={refreshTop} style={getStyle()}> <div className="nut-infinite-top" ref={refreshTop} style={getStyle()}>
<div className="top-box"> <div className="top-box">
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
### 安装 ### 安装
``` javascript ``` javascript
import { InputNumber } from '@nutui/nutui'; import { InputNumber } from '@nutui/nutui-react';
``` ```
## 代码演示 ## 代码演示
......
...@@ -15,6 +15,8 @@ export interface InputNumberProps { ...@@ -15,6 +15,8 @@ export interface InputNumberProps {
step: string | number step: string | number
decimalPlaces: string | number decimalPlaces: string | number
isAsync: boolean isAsync: boolean
className: string
style: React.CSSProperties
add: (e: MouseEvent) => void add: (e: MouseEvent) => void
reduce: (e: MouseEvent) => void reduce: (e: MouseEvent) => void
overlimit: (e: MouseEvent) => void overlimit: (e: MouseEvent) => void
...@@ -52,12 +54,15 @@ export const InputNumber: FunctionComponent< ...@@ -52,12 +54,15 @@ export const InputNumber: FunctionComponent<
decimalPlaces, decimalPlaces,
step, step,
isAsync, isAsync,
className,
style,
add, add,
reduce, reduce,
change, change,
overlimit, overlimit,
blur, blur,
focus, focus,
...restProps
} = { } = {
...defaultProps, ...defaultProps,
...props, ...props,
...@@ -68,7 +73,17 @@ export const InputNumber: FunctionComponent< ...@@ -68,7 +73,17 @@ export const InputNumber: FunctionComponent<
}, [modelValue]) }, [modelValue])
const b = bem('inputnumber') const b = bem('inputnumber')
const classes = classNames(
{
[`${b('')}--disabled`]: disabled,
},
className,
b('')
)
const styles = {
height: pxCheck(buttonSize),
...style,
}
const addAllow = (value = Number(inputValue)) => { const addAllow = (value = Number(inputValue)) => {
return value < Number(max) && !disabled return value < Number(max) && !disabled
} }
...@@ -77,10 +92,6 @@ export const InputNumber: FunctionComponent< ...@@ -77,10 +92,6 @@ export const InputNumber: FunctionComponent<
return value > Number(min) && !disabled return value > Number(min) && !disabled
} }
const classes = classNames(b(''), {
[`${b('')}--disabled`]: disabled,
})
const iconMinusClasses = classNames('nut-inputnumber__icon', { const iconMinusClasses = classNames('nut-inputnumber__icon', {
'nut-inputnumber__icon--disabled': !reduceAllow(), 'nut-inputnumber__icon--disabled': !reduceAllow(),
}) })
...@@ -97,7 +108,13 @@ export const InputNumber: FunctionComponent< ...@@ -97,7 +108,13 @@ export const InputNumber: FunctionComponent<
const output_value: number | string = fixedDecimalPlaces(value) const output_value: number | string = fixedDecimalPlaces(value)
change && change(output_value, e) change && change(output_value, e)
if (!isAsync) { if (!isAsync) {
setInputValue(output_value) if (Number(output_value) < Number(min)) {
setInputValue(Number(min))
} else if (Number(output_value) > Number(max)) {
setInputValue(Number(max))
} else {
setInputValue(output_value)
}
} }
} }
...@@ -153,7 +170,7 @@ export const InputNumber: FunctionComponent< ...@@ -153,7 +170,7 @@ export const InputNumber: FunctionComponent<
blur && blur(e) blur && blur(e)
} }
return ( return (
<div className={classes} style={{ height: pxCheck(buttonSize) }}> <div className={classes} style={styles} {...restProps}>
<Icon className={iconMinusClasses} size={buttonSize} name="minus" click={reduceNumber} /> <Icon className={iconMinusClasses} size={buttonSize} name="minus" click={reduceNumber} />
<input <input
type="number" type="number"
......
...@@ -16,11 +16,13 @@ import CircleProgress from './circleprogress' ...@@ -16,11 +16,13 @@ import CircleProgress from './circleprogress'
import NavBar from './navbar' import NavBar from './navbar'
import Tabbar from './tabbar' import Tabbar from './tabbar'
import InputNumber from './inputnumber' import InputNumber from './inputnumber'
import Elevator from './elevator'
import Rate from './rate' import Rate from './rate'
import Uploader from './uploader' import Uploader from './uploader'
import Input from './input' import Input from './input'
import TextArea from './textarea' import TextArea from './textarea'
import CheckBox from './checkbox' import CheckBox from './checkbox'
import Signature from './signature'
export { export {
Avatar, Avatar,
...@@ -41,9 +43,11 @@ export { ...@@ -41,9 +43,11 @@ export {
NavBar, NavBar,
Tabbar, Tabbar,
InputNumber, InputNumber,
Elevator,
Rate, Rate,
Uploader, Uploader,
Input, Input,
TextArea, TextArea,
CheckBox, CheckBox,
Signature,
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
``` javascript ``` javascript
import { OverLay } from '@nutui/nutui'; import { OverLay } from '@nutui/nutui-react';
``` ```
## 代码演示 ## 代码演示
......
...@@ -8,6 +8,8 @@ export interface PriceProps { ...@@ -8,6 +8,8 @@ export interface PriceProps {
symbol: string symbol: string
decimalDigits: number decimalDigits: number
thousands: boolean thousands: boolean
className: string
style: React.CSSProperties
} }
const defaultProps = { const defaultProps = {
price: 0, price: 0,
...@@ -15,9 +17,13 @@ const defaultProps = { ...@@ -15,9 +17,13 @@ const defaultProps = {
symbol: '&yen;', symbol: '&yen;',
decimalDigits: 2, decimalDigits: 2,
thousands: false, thousands: false,
className: '',
} as PriceProps } as PriceProps
export const Price: FunctionComponent<Partial<PriceProps>> = (props) => { export const Price: FunctionComponent<Partial<PriceProps>> = (props) => {
const { price, needSymbol, symbol, decimalDigits, thousands } = { ...defaultProps, ...props } const { price, needSymbol, symbol, decimalDigits, thousands, className, ...rest } = {
...defaultProps,
...props,
}
const b = bem('price') const b = bem('price')
const showSymbol = () => { const showSymbol = () => {
return { __html: (needSymbol ? symbol : '') || '' } return { __html: (needSymbol ? symbol : '') || '' }
...@@ -45,18 +51,19 @@ export const Price: FunctionComponent<Partial<PriceProps>> = (props) => { ...@@ -45,18 +51,19 @@ export const Price: FunctionComponent<Partial<PriceProps>> = (props) => {
if (Number(decimalNum) == 0) { if (Number(decimalNum) == 0) {
decimalNum = 0 decimalNum = 0
} }
if (checkPoint(decimalNum)) { if (checkPoint(decimalNum)) {
decimalNum = Number(decimalNum).toFixed(decimalDigits) decimalNum = Number(decimalNum).toFixed(decimalDigits)
decimalNum = typeof decimalNum.split('.') === 'string' ? 0 : decimalNum.split('.')[1] decimalNum = typeof decimalNum.split('.') === 'string' ? 0 : decimalNum.split('.')[1]
} else { } else {
decimalNum = decimalNum.toString() decimalNum = 0
} }
const result = '0.' + decimalNum const result = '0.' + decimalNum
const resultFixed = Number(result).toFixed(decimalDigits) const resultFixed = Number(result).toFixed(decimalDigits)
return String(resultFixed).substring(2, resultFixed.length) return String(resultFixed).substring(2, resultFixed.length)
} }
return ( return (
<div className="nut-price"> <div className={`${b()} ${className}`} {...rest}>
{needSymbol ? ( {needSymbol ? (
<div className={`${b('symbol')}`} dangerouslySetInnerHTML={showSymbol()}></div> <div className={`${b('symbol')}`} dangerouslySetInnerHTML={showSymbol()}></div>
) : null} ) : null}
......
import React from 'react'
import { Signature } from './signature'
const SignatureDemo = () => {
const confirm = (canvas: HTMLCanvasElement, data: string) => {
let img = document.createElement('img')
img.src = data
const demo = document.querySelector('.demo1') as HTMLElement
demo.appendChild(img)
}
const clear = () => {
let img = document.querySelector('.demo1 img')
if (img) {
img.remove()
}
}
const confirm1 = (canvas: HTMLCanvasElement, data: string) => {
let img = document.createElement('img')
img.src = data
const demo = document.querySelector('.demo2') as HTMLElement
demo.appendChild(img)
}
const clear1 = () => {
let img = document.querySelector('.demo2 img')
if (img) {
img.remove()
}
}
const demoStyles: React.CSSProperties = { margin: '1em 0' }
return (
<>
<div className="demo">
<h2>基础用法</h2>
<Signature confirm={confirm} clear={clear}></Signature>
<p className="demo-tips demo1" style={demoStyles}>
Tips: 点击确认按钮,下方显示签名图片
</p>
<h2>修改颜色和签字粗细</h2>
<Signature
lineWidth={4}
strokeStyle={'green'}
confirm={confirm1}
clear={clear1}
></Signature>
<p className="demo-tips demo2" style={demoStyles}>
Tips: 点击确认按钮,下方显示签名图片
</p>
</div>
</>
)
}
export default SignatureDemo
# Signature 组件
### 介绍
基于 Canvas 的签名组件
### 安装
## 代码演示
### 基础用法
```tsx
const confirm = (canvas: HTMLCanvasElement, data: string) => {
let img = document.createElement('img')
img.src = data
const demo = document.querySelector('.demo1') as HTMLElement
demo.appendChild(img)
}
const clear = () => {
let img = document.querySelector('.demo1 img')
if (img) {
img.remove()
}
}
Signature confirm={confirm} clear={clear}></Signature>
<p className="demo-tips demo1" style={demoStyles}>
Tips: 点击确认按钮,下方显示签名图片
</p>
```
### 修改颜色和签字粗细
```tsx
const confirm = (canvas: HTMLCanvasElement, data: string) => {
let img = document.createElement('img')
img.src = data
const demo = document.querySelector('.demo1') as HTMLElement
demo.appendChild(img)
}
const clear = () => {
let img = document.querySelector('.demo1 img')
if (img) {
img.remove()
}
}
<Signature
lineWidth={4}
strokeStyle={'green'}
confirm={confirm1}
clear={clear1}
></Signature>
<p className="demo-tips demo2" style={demoStyles}>
Tips: 点击确认按钮,下方显示签名图片
</p>
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| -------------- | ------------------------------ | ------ | --------------------------------------------------- |
| custom-class | 自定义 class | String | - |
| line-width | 线条的宽度 | Number | 3 |
| stroke-style | 绘图笔触颜色 | String | '#000' |
| type | 图片格式 | String | 'png' |
| un-support-tpl | 不支持 Canvas 情况下的展示文案 | String | '对不起,当前浏览器不支持 Canvas,无法使用本控件!' |
## Event
| 字段 | 说明 | 回调参数 |
| ------- | ---------------------------- | -------------------------------- |
| confirm | 点击确认按钮触发事件回调函数 | canvas 和签名图片展示的 data URI |
| clear | 点击重签按钮触发事件回调函数 | 无 |
import { Signature } from './signature'
export default Signature
.nut-signature {
&__inner {
height: 10rem;
margin-bottom: 1rem;
border: 1px solid $signature-border-color;
display: flex;
justify-content: center;
align-items: center;
}
&__unsopport {
font-size: $font-size-base;
}
&__btn {
margin-right: 15px !important;
}
}
import React, { FunctionComponent, useRef, useState, useEffect } from 'react'
import './signature.scss'
import { Button } from '../button/button'
import bem from '@/utils/bem'
export interface SignatureProps {
type: String
lineWidth: Number
strokeStyle: String
unSupportTpl: String
className: string
confirm?: (canvas: HTMLCanvasElement, dataurl: string) => void
clear?: () => void
}
const defaultProps = {
type: 'png',
lineWidth: 2,
strokeStyle: '#000',
unSupportTpl: '对不起,当前浏览器不支持Canvas,无法使用本控件!',
className: '',
} as SignatureProps
export const Signature: FunctionComponent<
Partial<SignatureProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => {
const { type, lineWidth, strokeStyle, unSupportTpl, className, ...rest } = {
...defaultProps,
...props,
}
const b = bem('signature')
const canvasRef = useRef<HTMLCanvasElement>(null)
const wrapRef = useRef<HTMLDivElement>(null)
const [canvasHeight, setCanvasHeight] = useState(0)
const [canvasWidth, setCanvasWidth] = useState(0)
const ctx = useRef<CanvasRenderingContext2D | null>(null)
const isCanvasSupported = () => {
let elem = document.createElement('canvas')
return !!(elem.getContext && elem.getContext('2d'))
}
const isSupportTouch = 'ontouchstart' in window
const events = isSupportTouch
? ['touchstart', 'touchmove', 'touchend', 'touchleave']
: ['mousedown', 'mousemove', 'mouseup', 'mouseleave']
useEffect(() => {
if (isCanvasSupported() && canvasRef.current && wrapRef.current) {
ctx.current = canvasRef.current.getContext('2d')
setCanvasWidth(wrapRef.current.offsetWidth)
setCanvasHeight(wrapRef.current.offsetHeight)
addEvent()
}
}, [])
const startEventHandler = (event: any) => {
event.preventDefault()
if (ctx.current && canvasRef.current) {
ctx.current.beginPath()
ctx.current.lineWidth = lineWidth as number
ctx.current.strokeStyle = strokeStyle as string
canvasRef.current.addEventListener(events[1], moveEventHandler, false)
canvasRef.current.addEventListener(events[2], endEventHandler, false)
canvasRef.current.addEventListener(events[3], leaveEventHandler, false)
}
}
const addEvent = () => {
if (canvasRef.current) {
canvasRef.current.addEventListener(events[0], startEventHandler, false)
}
}
const moveEventHandler = (event: any) => {
event.preventDefault()
let evt = isSupportTouch ? event.touches[0] : event
if (canvasRef.current && ctx.current) {
let coverPos = canvasRef.current.getBoundingClientRect()
let mouseX = evt.clientX - coverPos.left
let mouseY = evt.clientY - coverPos.top
ctx.current.lineTo(mouseX, mouseY)
ctx.current.stroke()
}
}
const endEventHandler = (event: any) => {
event.preventDefault()
if (canvasRef.current) {
canvasRef.current.removeEventListener(events[1], moveEventHandler, false)
canvasRef.current.removeEventListener(events[2], endEventHandler, false)
}
}
const leaveEventHandler = (event: any) => {
event.preventDefault()
if (canvasRef.current) {
canvasRef.current.removeEventListener(events[1], moveEventHandler, false)
canvasRef.current.removeEventListener(events[2], endEventHandler, false)
}
}
const clear = () => {
if (canvasRef.current && ctx.current) {
canvasRef.current.addEventListener(events[2], endEventHandler, false)
ctx.current.clearRect(0, 0, canvasWidth, canvasHeight)
ctx.current.closePath()
}
props.clear && props.clear()
}
const confirm = () => {
onSave(canvasRef.current as HTMLCanvasElement)
}
const onSave = (canvas: HTMLCanvasElement) => {
let dataurl
switch (type) {
case 'png':
dataurl = canvas.toDataURL('image/png')
break
case 'jpg':
dataurl = canvas.toDataURL('image/jpeg', 0.8)
break
}
clear()
props.confirm && props.confirm(canvas, dataurl as string)
}
return (
<div className={`${b()} ${className}`} {...rest}>
<div className={`${b('inner')}`} ref={wrapRef}>
{isCanvasSupported() ? (
<canvas ref={canvasRef} height={canvasHeight} width={canvasWidth}></canvas>
) : (
<p className={`${b('unsopport')}`}>{unSupportTpl}</p>
)}
</div>
<Button className={`${b('btn')}`} type="default" onClick={() => clear()}>
重签
</Button>
<Button className={`${b('btn')}`} type="primary" onClick={() => confirm()}>
确认
</Button>
</div>
)
}
Signature.defaultProps = defaultProps
Signature.displayName = 'NutSignature'
...@@ -11,6 +11,8 @@ export interface StepProps { ...@@ -11,6 +11,8 @@ export interface StepProps {
activeIndex: number activeIndex: number
icon: string icon: string
size: string size: string
className: string
style: React.CSSProperties
renderContent: () => React.ReactNode renderContent: () => React.ReactNode
} }
const defaultProps = { const defaultProps = {
...@@ -23,7 +25,17 @@ const defaultProps = { ...@@ -23,7 +25,17 @@ const defaultProps = {
export const Step: FunctionComponent<Partial<StepProps> & React.HTMLAttributes<HTMLDivElement>> = ( export const Step: FunctionComponent<Partial<StepProps> & React.HTMLAttributes<HTMLDivElement>> = (
props props
) => { ) => {
const { children, title, content, activeIndex, icon, size, renderContent } = { const {
children,
title,
content,
activeIndex,
icon,
size,
className,
renderContent,
...restProps
} = {
...defaultProps, ...defaultProps,
...props, ...props,
} }
...@@ -41,10 +53,11 @@ export const Step: FunctionComponent<Partial<StepProps> & React.HTMLAttributes<H ...@@ -41,10 +53,11 @@ export const Step: FunctionComponent<Partial<StepProps> & React.HTMLAttributes<H
{ {
[`${b('')}-${getCurrentStatus()}`]: true, [`${b('')}-${getCurrentStatus()}`]: true,
}, },
className,
b('') b('')
) )
return ( return (
<div className={classes}> <div className={classes} {...restProps}>
<div className="nut-step-head"> <div className="nut-step-head">
<div className="nut-step-line"></div> <div className="nut-step-line"></div>
<div className={`nut-step-icon ${!dot ? (icon ? 'is-icon' : 'is-text') : ''}`}> <div className={`nut-step-icon ${!dot ? (icon ? 'is-icon' : 'is-text') : ''}`}>
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
### 安装 ### 安装
```javascript ```javascript
import { Steps } from '@nutui/nutui'; import { Steps } from '@nutui/nutui-react';
``` ```
## 代码演示 ## 代码演示
......
...@@ -8,6 +8,8 @@ export interface StepsProps { ...@@ -8,6 +8,8 @@ export interface StepsProps {
current: number current: number
direction: string direction: string
progressDot: boolean progressDot: boolean
className: string
style: React.CSSProperties
} }
const defaultProps = { const defaultProps = {
current: 0, current: 0,
...@@ -18,7 +20,7 @@ const defaultProps = { ...@@ -18,7 +20,7 @@ const defaultProps = {
export const Steps: FunctionComponent<Partial<StepsProps> & React.HTMLAttributes<HTMLDivElement>> = export const Steps: FunctionComponent<Partial<StepsProps> & React.HTMLAttributes<HTMLDivElement>> =
(props) => { (props) => {
const propSteps = { ...defaultProps, ...props } const propSteps = { ...defaultProps, ...props }
const { children, direction } = propSteps const { children, direction, className, ...restProps } = propSteps
const parentSteps = { const parentSteps = {
propSteps, propSteps,
...@@ -30,6 +32,7 @@ export const Steps: FunctionComponent<Partial<StepsProps> & React.HTMLAttributes ...@@ -30,6 +32,7 @@ export const Steps: FunctionComponent<Partial<StepsProps> & React.HTMLAttributes
[`${b('')}-${direction}`]: true, [`${b('')}-${direction}`]: true,
[`${b('')}-dot`]: !!props.progressDot, [`${b('')}-dot`]: !!props.progressDot,
}, },
className,
b('') b('')
) )
return ( return (
...@@ -38,6 +41,7 @@ export const Steps: FunctionComponent<Partial<StepsProps> & React.HTMLAttributes ...@@ -38,6 +41,7 @@ export const Steps: FunctionComponent<Partial<StepsProps> & React.HTMLAttributes
'div', 'div',
{ {
className: classes, className: classes,
...restProps,
}, },
children children
)} )}
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
### 安装 ### 安装
``` javascript ``` javascript
import { Uploader } from '@nutui/nutui'; import { Uploader } from '@nutui/nutui-react';
``` ```
## 代码示例 ## 代码示例
......
...@@ -258,6 +258,9 @@ $checkbox-label-disable-color: #999; ...@@ -258,6 +258,9 @@ $checkbox-label-disable-color: #999;
$radio-label-color: #1d1e1e; $radio-label-color: #1d1e1e;
$radio-label-disable-color: #999; $radio-label-disable-color: #999;
// signature
$signature-border-color: #dadada;
//fixednav //fixednav
$fixednav-bg-color: $white; $fixednav-bg-color: $white;
$fixednav-font-color: $black; $fixednav-font-color: $black;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册