提交 d6e45d3a 编写于 作者: L liuyijun

fix: 解决冲突合并

......@@ -276,6 +276,16 @@
"sort": 3,
"show": true,
"author": "swag~jun"
},
{
"version": "1.0.0",
"name": "Elevator",
"type": "component",
"cName": "电梯楼层",
"desc": "用于列表快速定位以及索引的显示",
"sort": 4,
"show": true,
"author": "songsong"
}
]
},
......@@ -336,7 +346,18 @@
},
{
"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 {
bgColor: string
prefixCls: string
src: string
className: string
style: React.CSSProperties
}
export type AvatarSize = 'large' | 'normal' | 'small'
export type AvatarShape = 'round' | 'square'
const defaultProps: AvatarProps = {
const defaultProps = {
size: 'normal',
icon: '',
shape: 'round',
bgColor: '#eee',
prefixCls: 'nut-avatar',
src: '',
}
} as AvatarProps
export const Avatar: FunctionComponent<
Partial<AvatarProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => {
const { children, prefixCls, size, shape, bgColor, src, icon, className, ...rest } = {
const { children, prefixCls, size, shape, bgColor, src, icon, className, style, ...rest } = {
...defaultProps,
...props,
}
......@@ -41,6 +43,7 @@ export const Avatar: FunctionComponent<
height: sizeValue.indexOf(size) > -1 ? '' : `${size}px`,
backgroundImage: src ? `url(${src})` : '',
backgroundColor: `${bgColor}`,
...style,
}
const iconStyles = !!icon && !src ? icon : ''
const handleClick: MouseEventHandler<HTMLDivElement> = (e) => {
......
......@@ -6,7 +6,7 @@
### 安装
``` javascript
import { Avatar } from '@nutui/nutui';
import { Avatar } from '@nutui/nutui-react';
```
## 代码示例
......
.nut-cell {
position: relative;
display: flex;
align-items: center;
width: 100%;
line-height: 20px;
padding: 13px 16px;
......@@ -57,10 +58,11 @@
flex: 1;
&--icon {
flex-direction: row;
.icon {
margin-right: 10px;
overflow: hidden;
}
}
&__icon {
margin-right: 10px;
}
&__subtitle {
font-size: $cell-title-desc-font;
......
......@@ -14,7 +14,7 @@ export interface CellProps {
replace: boolean
url: string
icon: string
classPrefix: string
className: string
extra: ReactNode
click: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
}
......@@ -28,7 +28,7 @@ const defaultProps = {
replace: false,
url: '',
icon: '',
classPrefix: 'nutui-cell',
className: '',
extra: '',
click: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {},
} as CellProps
......@@ -42,7 +42,7 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H
to,
url,
replace,
classPrefix,
className,
descTextAlign,
desc,
icon,
......@@ -65,12 +65,16 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H
}
}
const styles = {
const styles =
title || subTitle || icon
? {}
: {
textAlign: descTextAlign,
flex: 1,
}
return (
<div
className={`${b({ clickable: isLink || to ? true : false }, [classPrefix])} `}
className={`${b({ clickable: isLink || to ? true : false }, [className])} `}
onClick={(event) => handleClick(event)}
{...rest}
>
......@@ -80,12 +84,8 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H
<>
{title || subTitle || icon ? (
<>
{icon ? (
<div className={`${b('icon')}`}>
<Icon name={icon} />
</div>
) : null}
<div className={`${b('title', { icon: icon ? true : false })}`}>
{icon ? <Icon name={icon} className={`${b('icon')}`} /> : null}
{subTitle ? (
<>
<div className={b('maintitle')}>{title}</div>
......@@ -98,18 +98,14 @@ export const Cell: FunctionComponent<Partial<CellProps> & React.HTMLAttributes<H
</>
) : null}
{desc ? (
<div className={b('desc')} style={styles as CSSProperties}>
<div className={b('desc')} style={styles as React.CSSProperties}>
{desc}
</div>
) : null}
</>
)}
{extra ? extra : null}
{!extra && (isLink || to) ? (
<div className={b('link')}>
<Icon name="right"></Icon>
</div>
) : null}
{!extra && (isLink || to) ? <Icon name="right" className={b('link')}></Icon> : null}
</div>
)
}
......
import React, { FunctionComponent } from 'react'
import bem from '@/utils/bem'
import classNames from 'classnames'
import './circleprogress.scss'
export interface CircleProgressProps {
......@@ -7,6 +8,7 @@ export interface CircleProgressProps {
progress: string | number
isAuto: boolean
progressOption: object
className: string
}
const defaultProps = {
strokeInnerWidth: 10,
......@@ -17,11 +19,21 @@ const defaultProps = {
export const CircleProgress: FunctionComponent<
Partial<CircleProgressProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => {
const { children, strokeInnerWidth, progress, isAuto, progressOption } = {
const {
children,
strokeInnerWidth,
progress,
isAuto,
progressOption,
className,
style,
...restProps
} = {
...defaultProps,
...props,
}
const b = bem('circleprogress')
const classes = classNames(className, b(''))
const option = () => {
// 所有进度条的可配置项
let baseOption = {
......@@ -41,13 +53,18 @@ export const CircleProgress: FunctionComponent<
baseOption.startPosition = 'rotate(-90,' + baseOption.cx + ',' + baseOption.cy + ')'
return baseOption
}
const styles: React.CSSProperties = {
height: `${option().size}px`,
width: `${option().size}px`,
...style,
}
const arcLength = () => {
let circleLength = Math.floor(2 * Math.PI * option().radius)
let progressLength = ((progress as number) / 100) * circleLength
return `${progressLength},${circleLength}`
}
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">
<circle
r={option().radius}
......
......@@ -7,7 +7,7 @@
### 安装
``` javascript
import { CirecleProgress } from '@nutui/nutui';
import { CirecleProgress } from '@nutui/nutui-react';
```
## 代码演示
......
......@@ -4,4 +4,5 @@
z-index: 9997 !important;
width: fit-content;
height: fit-content;
touch-action: none;
}
import React, { FunctionComponent, useState, useEffect, useRef } from 'react'
import './drag.scss'
import bem from '@/utils/bem'
export interface DragProps {
attract: boolean
......@@ -10,7 +11,8 @@ export interface DragProps {
right: number
bottom: number
}
style: any
className: string
style: React.CSSProperties
}
const defaultProps = {
attract: false,
......@@ -21,12 +23,16 @@ const defaultProps = {
right: 0,
bottom: 0,
},
style: {},
className: '',
} as DragProps
export const Drag: FunctionComponent<Partial<DragProps> & React.HTMLAttributes<HTMLDivElement>> = (
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 elHeight = useRef(0)
const screenWidth = useRef(0)
......@@ -164,8 +170,8 @@ export const Drag: FunctionComponent<Partial<DragProps> & React.HTMLAttributes<H
return (
<div
className="nut-drag"
style={style}
className={`${b()} ${className}`}
{...reset}
ref={myDrag}
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 @@
### 安装
```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 './infiniteloading.scss'
......@@ -14,6 +16,8 @@ export interface InfiniteloadingProps {
loadIcon: string
loadTxt: string
loadMoreTxt: string
className: string
style: React.CSSProperties
refresh: (param: () => void) => void
loadMore: (param: () => void) => void
scrollChange: (param: number) => void
......@@ -52,9 +56,11 @@ export const Infiniteloading: FunctionComponent<
loadIcon,
loadTxt,
loadMoreTxt,
className,
refresh,
loadMore,
scrollChange,
...restProps
} = {
...defaultProps,
...props,
......@@ -66,10 +72,12 @@ export const Infiniteloading: FunctionComponent<
const isTouching = useRef(false)
const beforeScrollTop = useRef(0)
const refreshMaxH = useRef(0)
const x = useRef(0)
const y = useRef(0)
const distance = useRef(0)
const b = bem('infiniteloading')
const classes = classNames(className, b())
useEffect(() => {
const parentElement = getParentElement(scroller.current as HTMLDivElement) as Node & ParentNode
scrollEl.current = useWindow ? window : parentElement
......@@ -213,11 +221,12 @@ export const Infiniteloading: FunctionComponent<
return (
<div
className="nut-infiniteloading"
className={classes}
ref={scroller}
onTouchStart={(event) => touchStart(event)}
onTouchMove={(event) => touchMove(event)}
onTouchEnd={() => touchEnd()}
{...restProps}
>
<div className="nut-infinite-top" ref={refreshTop} style={getStyle()}>
<div className="top-box">
......
......@@ -7,7 +7,7 @@
### 安装
``` javascript
import { InputNumber } from '@nutui/nutui';
import { InputNumber } from '@nutui/nutui-react';
```
## 代码演示
......
......@@ -15,6 +15,8 @@ export interface InputNumberProps {
step: string | number
decimalPlaces: string | number
isAsync: boolean
className: string
style: React.CSSProperties
add: (e: MouseEvent) => void
reduce: (e: MouseEvent) => void
overlimit: (e: MouseEvent) => void
......@@ -52,12 +54,15 @@ export const InputNumber: FunctionComponent<
decimalPlaces,
step,
isAsync,
className,
style,
add,
reduce,
change,
overlimit,
blur,
focus,
...restProps
} = {
...defaultProps,
...props,
......@@ -68,7 +73,17 @@ export const InputNumber: FunctionComponent<
}, [modelValue])
const b = bem('inputnumber')
const classes = classNames(
{
[`${b('')}--disabled`]: disabled,
},
className,
b('')
)
const styles = {
height: pxCheck(buttonSize),
...style,
}
const addAllow = (value = Number(inputValue)) => {
return value < Number(max) && !disabled
}
......@@ -77,10 +92,6 @@ export const InputNumber: FunctionComponent<
return value > Number(min) && !disabled
}
const classes = classNames(b(''), {
[`${b('')}--disabled`]: disabled,
})
const iconMinusClasses = classNames('nut-inputnumber__icon', {
'nut-inputnumber__icon--disabled': !reduceAllow(),
})
......@@ -97,9 +108,15 @@ export const InputNumber: FunctionComponent<
const output_value: number | string = fixedDecimalPlaces(value)
change && change(output_value, e)
if (!isAsync) {
if (Number(output_value) < Number(min)) {
setInputValue(Number(min))
} else if (Number(output_value) > Number(max)) {
setInputValue(Number(max))
} else {
setInputValue(output_value)
}
}
}
const reduceNumber = (e: MouseEvent) => {
reduce && reduce(e)
......@@ -153,7 +170,7 @@ export const InputNumber: FunctionComponent<
blur && blur(e)
}
return (
<div className={classes} style={{ height: pxCheck(buttonSize) }}>
<div className={classes} style={styles} {...restProps}>
<Icon className={iconMinusClasses} size={buttonSize} name="minus" click={reduceNumber} />
<input
type="number"
......
......@@ -16,11 +16,13 @@ import CircleProgress from './circleprogress'
import NavBar from './navbar'
import Tabbar from './tabbar'
import InputNumber from './inputnumber'
import Elevator from './elevator'
import Rate from './rate'
import Uploader from './uploader'
import Input from './input'
import TextArea from './textarea'
import CheckBox from './checkbox'
import Signature from './signature'
export {
Avatar,
......@@ -41,9 +43,11 @@ export {
NavBar,
Tabbar,
InputNumber,
Elevator,
Rate,
Uploader,
Input,
TextArea,
CheckBox,
Signature,
}
......@@ -8,7 +8,7 @@
``` javascript
import { OverLay } from '@nutui/nutui';
import { OverLay } from '@nutui/nutui-react';
```
## 代码演示
......
......@@ -8,6 +8,8 @@ export interface PriceProps {
symbol: string
decimalDigits: number
thousands: boolean
className: string
style: React.CSSProperties
}
const defaultProps = {
price: 0,
......@@ -15,9 +17,13 @@ const defaultProps = {
symbol: '&yen;',
decimalDigits: 2,
thousands: false,
className: '',
} as PriceProps
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 showSymbol = () => {
return { __html: (needSymbol ? symbol : '') || '' }
......@@ -45,18 +51,19 @@ export const Price: FunctionComponent<Partial<PriceProps>> = (props) => {
if (Number(decimalNum) == 0) {
decimalNum = 0
}
if (checkPoint(decimalNum)) {
decimalNum = Number(decimalNum).toFixed(decimalDigits)
decimalNum = typeof decimalNum.split('.') === 'string' ? 0 : decimalNum.split('.')[1]
} else {
decimalNum = decimalNum.toString()
decimalNum = 0
}
const result = '0.' + decimalNum
const resultFixed = Number(result).toFixed(decimalDigits)
return String(resultFixed).substring(2, resultFixed.length)
}
return (
<div className="nut-price">
<div className={`${b()} ${className}`} {...rest}>
{needSymbol ? (
<div className={`${b('symbol')}`} dangerouslySetInnerHTML={showSymbol()}></div>
) : 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 {
activeIndex: number
icon: string
size: string
className: string
style: React.CSSProperties
renderContent: () => React.ReactNode
}
const defaultProps = {
......@@ -23,7 +25,17 @@ const defaultProps = {
export const Step: FunctionComponent<Partial<StepProps> & React.HTMLAttributes<HTMLDivElement>> = (
props
) => {
const { children, title, content, activeIndex, icon, size, renderContent } = {
const {
children,
title,
content,
activeIndex,
icon,
size,
className,
renderContent,
...restProps
} = {
...defaultProps,
...props,
}
......@@ -41,10 +53,11 @@ export const Step: FunctionComponent<Partial<StepProps> & React.HTMLAttributes<H
{
[`${b('')}-${getCurrentStatus()}`]: true,
},
className,
b('')
)
return (
<div className={classes}>
<div className={classes} {...restProps}>
<div className="nut-step-head">
<div className="nut-step-line"></div>
<div className={`nut-step-icon ${!dot ? (icon ? 'is-icon' : 'is-text') : ''}`}>
......
......@@ -7,7 +7,7 @@
### 安装
```javascript
import { Steps } from '@nutui/nutui';
import { Steps } from '@nutui/nutui-react';
```
## 代码演示
......
......@@ -8,6 +8,8 @@ export interface StepsProps {
current: number
direction: string
progressDot: boolean
className: string
style: React.CSSProperties
}
const defaultProps = {
current: 0,
......@@ -18,7 +20,7 @@ const defaultProps = {
export const Steps: FunctionComponent<Partial<StepsProps> & React.HTMLAttributes<HTMLDivElement>> =
(props) => {
const propSteps = { ...defaultProps, ...props }
const { children, direction } = propSteps
const { children, direction, className, ...restProps } = propSteps
const parentSteps = {
propSteps,
......@@ -30,6 +32,7 @@ export const Steps: FunctionComponent<Partial<StepsProps> & React.HTMLAttributes
[`${b('')}-${direction}`]: true,
[`${b('')}-dot`]: !!props.progressDot,
},
className,
b('')
)
return (
......@@ -38,6 +41,7 @@ export const Steps: FunctionComponent<Partial<StepsProps> & React.HTMLAttributes
'div',
{
className: classes,
...restProps,
},
children
)}
......
......@@ -7,7 +7,7 @@
### 安装
``` javascript
import { Uploader } from '@nutui/nutui';
import { Uploader } from '@nutui/nutui-react';
```
## 代码示例
......
......@@ -258,6 +258,9 @@ $checkbox-label-disable-color: #999;
$radio-label-color: #1d1e1e;
$radio-label-disable-color: #999;
// signature
$signature-border-color: #dadada;
//fixednav
$fixednav-bg-color: $white;
$fixednav-font-color: $black;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册