未验证 提交 f3323394 编写于 作者: D Drjingfubo 提交者: GitHub

feat(numberkeyboard): new components (#192)

上级 f32858c0
......@@ -413,6 +413,16 @@
"sort": 1,
"show": true,
"author": "swag~jun"
},
{
"version": "1.0.0",
"name": "NumberKeyboard",
"type": "component",
"cName": "数字键盘",
"desc": "",
"sort": 16,
"show": true,
"author": "Drjingfubo"
}
]
},
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should match customKey snapshot 1`] = `
<div>
<div>
<div
class="overlay-fade-enter-active nut-overlay"
style="background-color: rgba(0, 0, 0, 0); z-index: 2000; animation-duration: 0.3s;"
/>
<div
class="popup-bottom undefined nut-popup"
style="z-index: 2000; animation-duration: 0.3s;"
>
<div
class="nut-numberkeyboard "
>
<div
class="nut-numberkeyboard__body"
>
<div
class="nut-numberkeyboard__body__keys"
>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
1
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
2
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
3
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
4
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
5
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
6
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
7
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
8
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
9
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
.
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
0
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
x
</div>
</div>
</div>
</div>
<div
class="nut-numberkeyboard__sidebar"
>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<img
alt=""
src="https://img11.360buyimg.com/imagetools/jfs/t1/129395/8/12735/2030/5f61ac37E70cab338/fb477dc11f46056c.png"
/>
</div>
</div>
<div
class="key-board-wrapper key-board-finish"
>
<div
class="key finish"
>
完成
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`should match snapshot 1`] = `
<div>
<div>
<div
class="overlay-fade-enter-active nut-overlay"
style="background-color: rgba(0, 0, 0, 0); z-index: 2000; animation-duration: 0.3s;"
/>
<div
class="popup-bottom undefined nut-popup"
style="z-index: 2000; animation-duration: 0.3s;"
>
<div
class="nut-numberkeyboard "
>
<div
class="nut-numberkeyboard__body"
>
<div
class="nut-numberkeyboard__body__keys"
>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
1
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
2
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
3
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
4
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
5
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
6
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
7
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
8
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
9
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key lock"
>
<img
alt=""
src="https://img11.360buyimg.com/imagetools/jfs/t1/146371/38/8485/738/5f606425Eca239740/14f4b4f5f20d8a68.png"
/>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key"
>
<div>
0
</div>
</div>
</div>
<div
class="key-board-wrapper"
>
<div
class="key delete"
>
<img
alt=""
src="https://img11.360buyimg.com/imagetools/jfs/t1/129395/8/12735/2030/5f61ac37E70cab338/fb477dc11f46056c.png"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
import * as React from 'react'
import { render } from '@testing-library/react'
import { trigger } from '@/utils/test/event'
import '@testing-library/jest-dom'
import { NumberKeyboard } from '../numberkeyboard'
function clickKey(key: Parameters<typeof trigger>[0]) {
trigger(key, 'touchstart')
trigger(key, 'touchend')
}
test('should match snapshot', async () => {
const { container } = render(<NumberKeyboard visible />)
expect(container).toMatchSnapshot()
})
test('should match customKey snapshot', async () => {
const { container } = render(
<NumberKeyboard visible type="rightColumn" customKey={['.', 'x', 'r']} />
)
expect(container.querySelector('.finish')).toHaveTextContent('完成')
expect(container).toMatchSnapshot()
})
test('default keyboard custom keyboard', async () => {
const { container } = render(
<NumberKeyboard visible customKey={['.', 'x', 'r']} />
)
expect(container.querySelectorAll('.key')[10]).toHaveTextContent('0')
})
test('Has title and custom keyboard length greater than 1', async () => {
const { container } = render(
<NumberKeyboard visible title="标题" customKey={['.', 'x', 'r']} />
)
expect(container.querySelectorAll('.key')[10]).toHaveTextContent('0')
})
test('Has title and custom keyboard length equal 1', async () => {
const { container } = render(
<NumberKeyboard visible title="标题" customKey={['.']} />
)
expect(container.querySelectorAll('.key')[9]).toHaveTextContent('.')
})
test('should shuffle key order when using random-key prop', async () => {
let value = 0
const onChange = (v: string) => {
value = Number(v)
}
const { container } = render(
<NumberKeyboard visible randomKeys onChange={onChange} />
)
const keys: number[] = []
const clickKeys: number[] = []
for (let i = 0; i < 9; i++) {
keys.push(i + 1)
clickKey(container.querySelectorAll('.key')[i])
clickKeys.push(value)
}
expect(keys.every((v, k) => keys[k] === clickKeys[k])).toEqual(false)
})
test('should emit delete event after clicking delete key', () => {
const onDelete = jest.fn()
const { container } = render(<NumberKeyboard visible onDelete={onDelete} />)
clickKey(container.querySelectorAll('.key')[11])
expect(onDelete).toBeTruthy()
})
test('should emit close event after clicking collapse key', () => {
const onClose = jest.fn()
const { container } = render(<NumberKeyboard visible onClose={onClose} />)
clickKey(container.querySelectorAll('.key')[9])
expect(onClose).toBeTruthy()
})
test('should render title and close button correctly', () => {
const { container } = render(<NumberKeyboard visible title="默认键盘" />)
const title = container.querySelector('.nut-numberkeyboard__header__tit')
expect(title).toHaveTextContent('默认键盘')
})
test('should render finish button correctly', () => {
const { container } = render(
<NumberKeyboard visible type="rightColumn" confirmText="支付" />
)
const title = container.querySelector('.finish')
expect(title).toHaveTextContent('支付')
})
import React, { useState } from 'react'
import { useTranslate } from '@/sites/assets/locale'
import Cell from '../cell'
import Toast from '../toast'
import { NumberKeyboard } from './numberkeyboard'
const NumberKeyboardDemo = () => {
const [translated] = useTranslate({
'zh-CN': {
basic: '默认键盘',
sidebar: '带右侧栏键盘',
confirmText: '支付',
randomKeyOrder: '随机数键盘',
title: '标题',
withTitle: '带标题栏键盘',
idNumberKeyboard: '身份证键盘',
},
'zh-TW': {
basic: '默認鍵盤',
sidebar: '帶右側欄鍵盤',
confirmText: '支付',
randomKeyOrder: '隨機數鍵盤',
title: '標題',
withTitle: '帶標題欄鍵盤',
idNumberKeyboard: '身份證鍵盤',
},
'en-US': {
basic: 'Default Keyboard',
sidebar: 'Keyboard With Sidebar',
confirmText: 'pay',
randomKeyOrder: 'Random Key Order',
title: 'title',
withTitle: 'Show Keyboard With Title',
idNumberKeyboard: 'Show IdNumber Keyboard',
},
})
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 onChange = (number: string) => {
Toast.text(`输入:${number}`)
}
const onDelete = () => {
Toast.text('删除')
}
return (
<div className="demo">
<h2>{translated.basic}</h2>
<Cell
title={translated.basic}
isLink
onClick={() => {
setVisible1(true)
}}
/>
<NumberKeyboard
visible={visible1}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible1(false)}
/>
<h2>{translated.sidebar}</h2>
<Cell
title={translated.sidebar}
isLink
onClick={() => {
setVisible2(true)
}}
/>
<NumberKeyboard
visible={visible2}
type="rightColumn"
customKey={['.', 'x']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible2(false)}
/>
<h2>{translated.randomKeyOrder}</h2>
<Cell
title={translated.randomKeyOrder}
isLink
onClick={() => {
setVisible3(true)
}}
/>
<NumberKeyboard
visible={visible3}
randomKeys
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible3(false)}
/>
<h2>{translated.withTitle}</h2>
<Cell
title={translated.withTitle}
isLink
onClick={() => {
setVisible4(true)
}}
/>
<NumberKeyboard
visible={visible4}
title={translated.title}
customKey={['.']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible4(false)}
/>
<h2>{translated.idNumberKeyboard}</h2>
<Cell
title={translated.idNumberKeyboard}
isLink
onClick={() => {
setVisible5(true)
}}
/>
<NumberKeyboard
visible={visible5}
customKey={['X']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible5(false)}
/>
</div>
)
}
export default NumberKeyboardDemo
# NumberKeyboard
### Introduce
Virtual numeric keypad, used for scenarios where payment passwords are entered.
### Install
```js
import { NumberKeyboard } from '@nutui/nutui-react';
```
## Demo
### Default Keyboard
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`enter:${number}`)
}
const onDelete = () => {
Toast.text('delete')
}
return (
<>
<Cell
title="Default Keyboard"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### Keyboard With Sidebar
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`enter:${number}`)
}
const onDelete = () => {
Toast.text('delete')
}
return (
<>
<Cell
title="Keyboard With Sidebar"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
type="rightColumn"
customKey={['.', 'x']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### Random Key Order
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`enter:${number}`)
}
const onDelete = () => {
Toast.text('delete')
}
return (
<>
<Cell
title=" Random Key Order"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
randomKeys
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### Show Keyboard With Title
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`enter:${number}`)
}
const onDelete = () => {
Toast.text('delete')
}
return (
<>
<Cell
title="Show Keyboard With Title"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
title="title"
customKey={['.']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### Show IdNumber Keyboard
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`enter:${number}`)
}
const onDelete = () => {
Toast.text('delete')
}
return (
<>
<Cell
title="Show IdNumber Keyboard"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
customKey={['X']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
## API
### Props
| Attribute | Description | Type | Default |
|----- | ----- | ----- | ----- |
| visible | Whether to show keyboard | Boolean | false |
| title | Keyboard title | String | - |
| type | Keyboard type | String | `default`, `rightColumn` |
| randomKeys | Whether to shuffle the order of keys | Boolean | false |
| customKey | Content of bottom left key | String [] | Array form supports adding up to two |
| confirmText | Custom done button text,Such as "pay", "next", "submit" | String | done |
| pop-class | Custom bullet box classname | String | - |
### Event
| Event | Description | Arguments
|----- | ----- | -----
| onChange | Emitted when a key is pressed | value: string |
| onDelete | Emitted when the delete key is pressed | - |
| onClose | Emitted when the close button or non-keyboard area is clicked is clicked | - |
# NumberKeyboard 数字键盘
### 介绍
虚拟数字键盘,用于输入支付密码的场景。
### 安装
```js
import { NumberKeyboard } from '@nutui/nutui-react';
```
## 代码演示
### 基础用法
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`输入:${number}`)
}
const onDelete = () => {
Toast.text('删除')
}
return (
<>
<Cell
title="基础用法"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### 带右侧栏键盘
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`输入:${number}`)
}
const onDelete = () => {
Toast.text('删除')
}
return (
<>
<Cell
title="带右侧栏键盘"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
type="rightColumn"
customKey={['.', 'x']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### 随机数键盘
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`输入:${number}`)
}
const onDelete = () => {
Toast.text('删除')
}
return (
<>
<Cell
title="随机数键盘"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
randomKeys
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### 带标题栏键盘
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`输入:${number}`)
}
const onDelete = () => {
Toast.text('删除')
}
return (
<>
<Cell
title="带标题栏键盘"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
title="标题"
customKey={['.']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### 身份证键盘
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`输入:${number}`)
}
const onDelete = () => {
Toast.text('删除')
}
return (
<>
<Cell
title="身份证键盘"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
customKey={['X']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
## API
### Props
| 字段 | 说明 | 类型 | 默认值 |
|----- | ----- | ----- | ----- |
| visible | 是否显示键盘 | Boolean | false |
| title | 键盘标题 | String | - |
| type | 键盘模式 | String | `default`:默认样式 `rightColumn`:带右侧栏 |
| randomKeys | 随机数 | Boolean | false |
| customKey | 自定义键盘额外的键 | String [] | 数组形式最多支持添加2个,超出默认取前2项 |
| confirmText | 自定义完成按钮文字,如"支付","下一步","提交"等 | String | 完成 |
| pop-class | 自定义弹框类名 | String | - |
### Event
| 字段 | 说明 | 回调参数
|----- | ----- | -----
| onChange | 点击按键时触发 | value: string |
| onDelete | 点击删除键时触发 | - |
| onClose | 点击关闭按钮或非键盘区域时触发 | - |
# NumberKeyboard
### 介紹
虛擬數字鍵盤,用於輸入支付密碼的場景。
### 安裝
```js
import { NumberKeyboard } from '@nutui/nutui-react';
```
## 代碼演示
### 基礎用法
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`輸入:${number}`)
}
const onDelete = () => {
Toast.text('刪除')
}
return (
<>
<Cell
title="基礎用法"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### 帶右側欄鍵盤
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`輸入:${number}`)
}
const onDelete = () => {
Toast.text('刪除')
}
return (
<>
<Cell
title="帶右側欄鍵盤"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
type="rightColumn"
customKey={['.', 'x']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### 隨機數鍵盤
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`輸入:${number}`)
}
const onDelete = () => {
Toast.text('刪除')
}
return (
<>
<Cell
title="隨機數鍵盤"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
randomKeys
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### 帶標題欄鍵盤
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`輸入:${number}`)
}
const onDelete = () => {
Toast.text('刪除')
}
return (
<>
<Cell
title="帶標題欄鍵盤"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
title="標題"
customKey={['.']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
### 身份證鍵盤
:::demo
```tsx
import React, { useState } from "react";
import { Cell,NumberKeyboard,Toast } from '@nutui/nutui-react';
const App = () => {
const [visible,setVisible] = useState(false)
const onChange = (number: string) => {
Toast.text(`輸入:${number}`)
}
const onDelete = () => {
Toast.text('刪除')
}
return (
<>
<Cell
title="身份證鍵盤"
isLink
onClick={() => {
setVisible(true)
}}
/>
<NumberKeyboard
visible={visible}
customKey={['X']}
onChange={onChange}
onDelete={onDelete}
onClose={() => setVisible(false)}
/>
</>
)
}
export default App;
```
:::
## API
### Props
| 字段 | 說明 | 類型 | 預設值 |
|----- | ----- | ----- | ----- |
| visible | 是否顯示鍵盤 | Boolean | false |
| title | 鍵盤標題 | String | - |
| type | 鍵盤模式 | String | `default`:默認樣式 `rightColumn`:帶右側欄 |
| randomKeys | 隨機數 | Boolean | false |
| customKey | 自定義鍵盤額外的鍵 | String [] | 數組形式最多支持添加2個,超出默認取前2項 |
| confirmText | 自定義完成按鈕文字,如"支付","下一步","提交"等 | String | 完成 |
| pop-class | 自定義彈框類名 | String | - |
### Event
| 字段 | 說明 | 回調參數
|----- | ----- | -----
| onChange | 點擊按鍵時觸發 | value: string |
| onDelete | 點擊刪除鍵時觸發 | - |
| onClose | 點擊關閉按鈕或非鍵盤區域時觸發 | - |
import { NumberKeyboard } from './numberkeyboard'
export default NumberKeyboard
.nut-numberkeyboard {
width: $numberkeyboard-width;
padding: $numberkeyboard-padding;
background-color: $numberkeyboard-background-color;
user-select: none;
.nut-numberkeyboard__header {
position: relative;
display: flex;
align-items: center;
justify-content: center;
box-sizing: content-box;
height: $numberkeyboard-header-height;
padding: $numberkeyboard-header-padding;
color: $numberkeyboard-header-color;
font-size: $numberkeyboard-header-font-size;
.nut-numberkeyboard__header__tit {
display: inline-block;
}
.nut-numberkeyboard__header__close {
position: absolute;
display: block;
right: 0;
padding: $numberkeyboard-header-close-padding;
color: $numberkeyboard-header-close-color;
font-size: $numberkeyboard-header-close-font-size;
background-color: $numberkeyboard-header-close-background-color;
border: none;
cursor: pointer;
}
}
.nut-numberkeyboard__body {
display: flex;
padding: 6px 0 0 6px;
.nut-numberkeyboard__body__keys {
display: flex;
flex: 3;
flex-wrap: wrap;
}
.key-board-wrapper {
position: relative;
flex: 1;
flex-basis: 33%;
box-sizing: border-box;
padding: 0 6px 6px 0;
.key {
display: flex;
align-items: center;
justify-content: center;
height: $numberkeyboard-key-height;
font-size: $numberkeyboard-key-font-size;
line-height: $numberkeyboard-key-line-height;
background-color: $numberkeyboard-key-background-color;
color: $numberkeyboard-key-font-size-color;
border-radius: $numberkeyboard-key-border-radius;
cursor: pointer;
}
.active {
background-color: $numberkeyboard-key-active-background-color;
}
img {
width: 30px;
height: 24px;
}
}
.key-board-wrapper-large {
flex-basis: 66%;
}
.nut-numberkeyboard__sidebar {
display: flex;
flex: 1;
flex-direction: column;
.key-board-wrapper {
.key {
position: absolute;
top: 0;
right: 6px;
bottom: 6px;
left: 0;
height: auto;
}
.finish {
font-size: $numberkeyboard-key-finish-font-size;
color: $numberkeyboard-key-finish-font-size-color;
background-color: $numberkeyboard-key-finish-background-color;
}
.activeFinsh {
background-color: $numberkeyboard-key-activeFinsh-background-color;
}
}
}
}
}
import React, {
CSSProperties,
FunctionComponent,
useEffect,
useState,
} from 'react'
import classNames from 'classnames'
import bem from '@/utils/bem'
import Popup from '@/packages/popup'
import { useConfig } from '@/packages/configprovider'
export interface NextListObj {
type: string
id: string
}
export interface NumberKeyboardProps {
confirmText: string
title: string
visible: boolean
type: string
customKey: Array<string>
randomKeys: boolean
popClass: string
className: string
style?: CSSProperties
onChange?: (value: string) => void
onDelete?: () => void
onClose: () => void
}
const defaultProps = {
confirmText: '',
title: '',
visible: false,
type: 'default',
customKey: [],
className: '',
randomKeys: false,
popClass: '',
onClose: () => {},
} as NumberKeyboardProps
export const NumberKeyboard: FunctionComponent<
Partial<NumberKeyboardProps> &
Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>
> = (props) => {
const { locale } = useConfig()
const {
title,
confirmText,
visible,
type,
customKey,
randomKeys,
popClass,
style,
className,
onChange,
onDelete,
onClose,
} = props
const b = bem('numberkeyboard')
const [show, setShow] = useState<boolean | undefined>(visible)
const [clickKeyIndex, setClickKeyIndex] =
useState<string | undefined>(undefined)
const [keysList, setKeysList] = useState<Array<any> | undefined>([])
useEffect(() => {
setShow(visible)
}, [visible])
const defaultKey = () => {
let leftBottomKey = {
id: 'lock',
type: 'lock',
}
if (customKey) {
const customKeys: Array<string> = Array.isArray(customKey)
? customKey
: [customKey]
if (customKeys.length > 0) {
const newCustomKey = customKeys[0]
leftBottomKey = {
id: newCustomKey,
type: 'custom',
}
}
}
return [
...getBasicKeys(),
leftBottomKey,
{ id: 0, type: 'number' },
{ id: 'delete', type: 'delete' },
]
}
const getBasicKeys = () => {
const keys: Array<unknown> = []
for (let i = 1; i <= 9; i++) {
keys.push({ id: i, type: 'number' })
}
if (randomKeys) {
return keys.sort(() => (Math.random() > 0.5 ? 1 : -1))
}
return keys
}
const genCustomKeys = () => {
const keys = getBasicKeys()
if (!customKey) return []
let customKeys = Array.isArray(customKey) ? customKey : [customKey]
if (customKeys.length > 2) {
customKeys = [customKeys[0], customKeys[1]]
}
if (
customKeys.length === 2 &&
props.title &&
props.type !== 'rightColumn'
) {
customKeys = [customKeys[0]]
}
if (customKeys.length === 1) {
if (props.title && props.type !== 'rightColumn') {
keys.push(
{ id: customKeys[0], type: 'custom' },
{ id: 0, type: 'number' },
{ id: 'delete', type: 'delete' }
)
} else {
keys.push(
{ id: 0, type: 'number' },
{ id: customKeys[0], type: 'custom' }
)
}
} else if (customKeys.length === 2) {
keys.push(
{ id: customKeys[0], type: 'custom' },
{ id: 0, type: 'number' },
{ id: customKeys[1], type: 'custom' }
)
}
return keys
}
useEffect(() => {
if (props.type === 'rightColumn' || props.title !== '') {
setKeysList(genCustomKeys())
} else {
setKeysList(defaultKey())
}
}, [])
const onTouchstart = (
item: { id: string; type: string },
event: React.TouchEvent<HTMLElement>
) => {
event.stopPropagation()
setClickKeyIndex(item.id)
if (item.type === 'number' || item.type === 'custom') {
onChange && onChange(item.id)
}
if (item.type === 'lock') {
onClose && onClose()
}
if (item.type === 'delete') {
onDelete && onDelete()
}
}
const onTouchMove = (event: React.TouchEvent<HTMLElement>) => {
event.stopPropagation()
}
const onTouchEnd = (event: React.TouchEvent<HTMLElement>) => {
event.preventDefault()
setClickKeyIndex(undefined)
}
return (
<div>
<Popup
visible={show}
position="bottom"
popClass={popClass}
onClickOverlay={onClose}
onClickCloseIcon={onClose}
overlayStyle={{ backgroundColor: 'rgba(0, 0, 0, 0)' }}
>
<div className={`${b()} ${className}`} style={{ ...style }}>
{title ? (
<div className={b('header')}>
<h3 className={b('header__tit')}>{title}</h3>
{type === 'default' ? (
<span className={b('header__close')} onClick={onClose}>
{locale.done}
</span>
) : null}
</div>
) : null}
<div className={b('body')}>
<div className={b('body__keys')}>
{keysList?.map((item: any, index: number) => {
return (
<div
key={index}
className={classNames({
'key-board-wrapper': true,
'key-board-wrapper-large':
item.id === 0 &&
type === 'rightColumn' &&
Array.isArray(customKey) &&
customKey.length === 1,
})}
>
<div
className={classNames({
key: true,
active: item.id === clickKeyIndex,
lock: item.type === 'lock',
delete: item.type === 'delete',
})}
onTouchStart={(event) => onTouchstart(item, event)}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
>
{item.type === 'number' || item.type === 'custom' ? (
<div>{item.id}</div>
) : null}
{item.type === 'lock' ? (
<img
src="https://img11.360buyimg.com/imagetools/jfs/t1/146371/38/8485/738/5f606425Eca239740/14f4b4f5f20d8a68.png"
alt=""
/>
) : null}
{item.type === 'delete' ? (
<img
src="https://img11.360buyimg.com/imagetools/jfs/t1/129395/8/12735/2030/5f61ac37E70cab338/fb477dc11f46056c.png"
alt=""
/>
) : null}
</div>
</div>
)
})}
</div>
{type === 'rightColumn' ? (
<div className={b('sidebar')}>
<div className="key-board-wrapper">
<div
className={classNames({
key: true,
active: clickKeyIndex === 'delete',
})}
onTouchStart={(event) =>
onTouchstart({ id: 'delete', type: 'delete' }, event)
}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
>
<img
src="https://img11.360buyimg.com/imagetools/jfs/t1/129395/8/12735/2030/5f61ac37E70cab338/fb477dc11f46056c.png"
alt=""
/>
</div>
</div>
<div
className="key-board-wrapper key-board-finish"
onClick={onClose}
>
<div
className={classNames({
key: true,
finish: true,
activeFinsh: clickKeyIndex === 'finish',
})}
>
{confirmText || locale.done}
</div>
</div>
</div>
) : null}
</div>
</div>
</Popup>
</div>
)
}
NumberKeyboard.defaultProps = defaultProps
NumberKeyboard.displayName = 'NutNumberKeyboard'
......@@ -486,6 +486,29 @@ $divider-vertical-top: 2px !default;
$divider-vertical-border-left: rgba(0, 0, 0, 0.06) !default;
$divider-vertical-margin: 0 8px !default;
//numberkeyboard
$numberkeyboard-width: 100% !default;
$numberkeyboard-padding: 0 0 22px 0 !default;
$numberkeyboard-background-color: #f2f3f5 !default;
$numberkeyboard-header-height: 34px !default;
$numberkeyboard-header-padding: 6px 0 0 0 !default;
$numberkeyboard-header-color: #646566 !default;
$numberkeyboard-header-font-size: 16px !default;
$numberkeyboard-header-close-padding: 0 16px !default;
$numberkeyboard-header-close-color: #576b95 !default;
$numberkeyboard-header-close-font-size: 14px !default;
$numberkeyboard-header-close-background-color: transparent !default;
$numberkeyboard-key-background-color: #fff !default;
$numberkeyboard-key-active-background-color: #ebedf0 !default;
$numberkeyboard-key-height: 48px !default;
$numberkeyboard-key-line-height: 1.5 !default;
$numberkeyboard-key-border-radius: 8px !default;
$numberkeyboard-key-font-size: 28px !default;
$numberkeyboard-key-font-size-color: #333 !default;
$numberkeyboard-key-finish-font-size: 16px !default;
$numberkeyboard-key-finish-font-size-color: #fff !default;
$numberkeyboard-key-finish-background-color: #1989fa !default;
$numberkeyboard-key-activeFinsh-background-color: #0570db !default;
// elevator
$elevator-list-item-highcolor: $primary-color !default;
$elevator-list-item-font-size: 12px !default;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册