diff --git a/packages/nutui-taro-demo-rn/scripts/taro/adapted.js b/packages/nutui-taro-demo-rn/scripts/taro/adapted.js index 0829e85635..23dc22bb7e 100644 --- a/packages/nutui-taro-demo-rn/scripts/taro/adapted.js +++ b/packages/nutui-taro-demo-rn/scripts/taro/adapted.js @@ -26,6 +26,7 @@ exports = module.exports = [ 'hoverbutton', 'safearea', 'hoverbuttonitem', + 'range', 'avatar', 'avatargroup', 'inputnumber', diff --git a/packages/nutui-taro-demo-rn/src/app.config.ts b/packages/nutui-taro-demo-rn/src/app.config.ts index 8c9e8edcfd..c1eadf265b 100644 --- a/packages/nutui-taro-demo-rn/src/app.config.ts +++ b/packages/nutui-taro-demo-rn/src/app.config.ts @@ -124,7 +124,7 @@ const subPackages = [ } ]; -export default defineAppConfig ({ +export default defineAppConfig({ pages: ['pages/index/index'], subPackages, window: { diff --git a/packages/nutui-taro-demo-rn/src/dentry1/pages/range/index.config.ts b/packages/nutui-taro-demo-rn/src/dentry1/pages/range/index.config.ts index 6cdf97238b..780d4febe6 100644 --- a/packages/nutui-taro-demo-rn/src/dentry1/pages/range/index.config.ts +++ b/packages/nutui-taro-demo-rn/src/dentry1/pages/range/index.config.ts @@ -1,3 +1,3 @@ export default { - navigationBarTitleText: 'Range', + navigationBarTitleText: 'Range' } \ No newline at end of file diff --git a/packages/nutui-taro-demo-rn/src/dentry1/pages/range/index.tsx b/packages/nutui-taro-demo-rn/src/dentry1/pages/range/index.tsx index 039e05e8ec..c90590d54f 100644 --- a/packages/nutui-taro-demo-rn/src/dentry1/pages/range/index.tsx +++ b/packages/nutui-taro-demo-rn/src/dentry1/pages/range/index.tsx @@ -1 +1,2 @@ -export default <>button; \ No newline at end of file +import Demo from '@/packages/range/demo.taro'; +export default Demo; \ No newline at end of file diff --git a/src/config.json b/src/config.json index eea840b99e..f2478c0ade 100644 --- a/src/config.json +++ b/src/config.json @@ -610,7 +610,7 @@ "author": "oasis" }, { - "version": "2.0.0", + "version": "3.0.0", "name": "Range", "type": "component", "cName": "区间选择器", diff --git a/src/packages/range/demos/h5/demo11.tsx b/src/packages/range/demos/h5/demo11.tsx index c8c55228d2..07adbe4284 100644 --- a/src/packages/range/demos/h5/demo11.tsx +++ b/src/packages/range/demos/h5/demo11.tsx @@ -13,6 +13,7 @@ const Demo11 = () => { button={
{ textAlign: 'center', backgroundColor: 'red', borderRadius: '10px', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', }} > {value} diff --git a/src/packages/range/demos/taro/demo1.tsx b/src/packages/range/demos/taro/demo1.tsx index f926c615d7..65544fd703 100644 --- a/src/packages/range/demos/taro/demo1.tsx +++ b/src/packages/range/demos/taro/demo1.tsx @@ -1,10 +1,22 @@ -import React, { useState } from 'react' -import { Range, Cell, Toast } from '@nutui/nutui-react-taro' +import React, { useMemo, useState } from 'react' +import { View } from '@tarojs/components' +import { Range, Cell /* , Toast */ } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo1 = () => { - const cellStyle = { - padding: '40px 18px', - } + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) const [show, setShow] = useState(false) const [msg, setMsg] = useState('') const showToast = (msg: string) => { @@ -12,7 +24,7 @@ const Demo1 = () => { setShow(true) } return ( - <> + showToast(`${val}`)} /> @@ -26,15 +38,15 @@ const Demo1 = () => { onEnd={(val) => showToast(`${val}`)} /> - { setShow(false) }} - /> - + /> */} + ) } export default Demo1 diff --git a/src/packages/range/demos/taro/demo10.tsx b/src/packages/range/demos/taro/demo10.tsx index edec554041..0de7a26c85 100644 --- a/src/packages/range/demos/taro/demo10.tsx +++ b/src/packages/range/demos/taro/demo10.tsx @@ -1,10 +1,22 @@ -import React from 'react' +import React, { useMemo } from 'react' import { Range, ConfigProvider, Cell } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo10 = () => { - const cellStyle = { - padding: '40px 18px', - } + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) + return ( { + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) + + const buttonNativeStyle = useMemo(() => { + if (rn()) { + return { + transform: [ + { translateX: pxTransform(-13) }, + { translateY: pxTransform(3) }, + ], + } + } + return {} + }, []) const [value, setValue] = useState(60) - const cellStyle = { - padding: '40px 18px', - } const [show, setShow] = useState(false) const [msg, setMsg] = useState('') const showToast = (msg: string) => { @@ -14,38 +37,63 @@ const Demo11 = () => { setShow(true) } return ( - <> + - {value} + + {value} + } onChange={(val: any) => setValue(val)} onEnd={(val) => showToast(`${val}`)} /> - { setShow(false) }} - /> - + /> */} + ) } export default Demo11 diff --git a/src/packages/range/demos/taro/demo12.tsx b/src/packages/range/demos/taro/demo12.tsx index 4bb7c4ab08..8c7f5e939a 100644 --- a/src/packages/range/demos/taro/demo12.tsx +++ b/src/packages/range/demos/taro/demo12.tsx @@ -1,12 +1,28 @@ -import React, { useState } from 'react' +import React, { useMemo, useState } from 'react' import { View } from '@tarojs/components' -import { Range, Cell, Toast } from '@nutui/nutui-react-taro' +import { Range, Cell /* , Toast */ } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo12 = () => { - const verticalStyle = { - height: '180px', - padding: '10px', - } + const verticalStyle = useMemo(() => { + return harmonyAndRn() + ? { + height: pxTransform(180), + paddingTop: pxTransform(10), + paddingBottom: pxTransform(10), + paddingLeft: pxTransform(10), + paddingRight: pxTransform(10), + } + : { + height: '180px', + padding: '10px', + } + }, []) + const viewStyle = useMemo( + () => ({ width: pxTransform(150), height: '100%' }), + [] + ) const [show, setShow] = useState(false) const [msg, setMsg] = useState('') const showToast = (msg: string) => { @@ -14,16 +30,16 @@ const Demo12 = () => { setShow(true) } return ( - <> + - + showToast(`${val}`)} /> - + { /> - { setShow(false) }} - /> - + /> */} + ) } export default Demo12 diff --git a/src/packages/range/demos/taro/demo13.tsx b/src/packages/range/demos/taro/demo13.tsx index 0f852ee94f..35ccd90766 100644 --- a/src/packages/range/demos/taro/demo13.tsx +++ b/src/packages/range/demos/taro/demo13.tsx @@ -1,7 +1,36 @@ -import React, { useState } from 'react' -import { Range, Cell, Toast } from '@nutui/nutui-react-taro' +import React, { useMemo, useState } from 'react' +import { View } from '@tarojs/components' +import { Range, Cell /* , Toast */ } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo13 = () => { + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) + const verticalStyle = useMemo(() => { + return harmonyAndRn() + ? { + height: pxTransform(180), + paddingTop: pxTransform(10), + paddingBottom: pxTransform(10), + paddingLeft: pxTransform(10), + paddingRight: pxTransform(10), + } + : { + height: '180px', + padding: '10px', + } + }, []) const [marks] = useState({ 0: 'Start', 20: 20, @@ -10,13 +39,6 @@ const Demo13 = () => { 80: 80, 100: 'End', }) - const cellStyle = { - padding: '40px 18px', - } - const verticalStyle = { - height: '180px', - padding: '10px', - } const [show, setShow] = useState(false) const [msg, setMsg] = useState('') const showToast = (msg: string) => { @@ -24,7 +46,7 @@ const Demo13 = () => { setShow(true) } return ( - <> + { minDescription={null} marks={marks} onEnd={(val) => showToast(`${val}`)} + style={{ flex: 1 }} /> { marks={marks} range onEnd={(val) => showToast(`${val}`)} + style={{ flex: 1 }} /> - { setShow(false) }} - /> - + /> */} + ) } export default Demo13 diff --git a/src/packages/range/demos/taro/demo2.tsx b/src/packages/range/demos/taro/demo2.tsx index 39b43f0626..cf7b1b1090 100644 --- a/src/packages/range/demos/taro/demo2.tsx +++ b/src/packages/range/demos/taro/demo2.tsx @@ -1,11 +1,22 @@ -import React, { useState } from 'react' +import React, { useMemo, useState } from 'react' import { Range, Cell } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo2 = () => { + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) const [value, setValue] = useState(40) - const cellStyle = { - padding: '40px 18px', - } return ( setValue(val)} /> diff --git a/src/packages/range/demos/taro/demo3.tsx b/src/packages/range/demos/taro/demo3.tsx index e693325781..f9dc0eb7f3 100644 --- a/src/packages/range/demos/taro/demo3.tsx +++ b/src/packages/range/demos/taro/demo3.tsx @@ -1,10 +1,22 @@ -import React, { useState } from 'react' -import { Range, Cell, Toast } from '@nutui/nutui-react-taro' +import React, { useMemo, useState } from 'react' +import { View } from '@tarojs/components' +import { Range, Cell /* , Toast */ } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo3 = () => { - const cellStyle = { - padding: '40px 18px', - } + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) const [show, setShow] = useState(false) const [msg, setMsg] = useState('') const showToast = (msg: string) => { @@ -12,7 +24,7 @@ const Demo3 = () => { setShow(true) } return ( - <> + { onEnd={(val) => showToast(`${val}`)} /> - { setShow(false) }} - /> - + /> */} + ) } export default Demo3 diff --git a/src/packages/range/demos/taro/demo4.tsx b/src/packages/range/demos/taro/demo4.tsx index 3a76b2e934..8a153f9423 100644 --- a/src/packages/range/demos/taro/demo4.tsx +++ b/src/packages/range/demos/taro/demo4.tsx @@ -1,10 +1,22 @@ -import React, { useState } from 'react' -import { Range, Cell, Toast } from '@nutui/nutui-react-taro' +import React, { useState, useMemo } from 'react' +import { View } from '@tarojs/components' +import { Range, Cell /* , Toast */ } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo4 = () => { - const cellStyle = { - padding: '40px 18px', - } + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) const [show, setShow] = useState(false) const [msg, setMsg] = useState('') const showToast = (msg: string) => { @@ -12,7 +24,7 @@ const Demo4 = () => { setShow(true) } return ( - <> + { onEnd={(val) => showToast(`${val}`)} /> - { setShow(false) }} - /> - + /> */} + ) } export default Demo4 diff --git a/src/packages/range/demos/taro/demo5.tsx b/src/packages/range/demos/taro/demo5.tsx index c8c2d62920..5537ebf2e2 100644 --- a/src/packages/range/demos/taro/demo5.tsx +++ b/src/packages/range/demos/taro/demo5.tsx @@ -1,10 +1,22 @@ -import React, { useState } from 'react' -import { Range, Cell, Toast } from '@nutui/nutui-react-taro' +import React, { useMemo, useState } from 'react' +import { View } from '@tarojs/components' +import { Range, Cell /* , Toast */ } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo5 = () => { - const cellStyle = { - padding: '40px 18px', - } + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) const [show, setShow] = useState(false) const [msg, setMsg] = useState('') const showToast = (msg: string) => { @@ -12,7 +24,7 @@ const Demo5 = () => { setShow(true) } return ( - <> + { onEnd={(val) => showToast(`${val}`)} /> - { setShow(false) }} - /> - + /> */} + ) } export default Demo5 diff --git a/src/packages/range/demos/taro/demo6.tsx b/src/packages/range/demos/taro/demo6.tsx index d27cd52b36..dae93f0ff1 100644 --- a/src/packages/range/demos/taro/demo6.tsx +++ b/src/packages/range/demos/taro/demo6.tsx @@ -1,10 +1,22 @@ -import React, { useState } from 'react' -import { Range, Cell, Toast } from '@nutui/nutui-react-taro' +import React, { useMemo, useState } from 'react' +import { View } from '@tarojs/components' +import { Range, Cell /* , Toast */ } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo6 = () => { - const cellStyle = { - padding: '40px 18px', - } + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) const [show, setShow] = useState(false) const [msg, setMsg] = useState('') const showToast = (msg: string) => { @@ -12,7 +24,7 @@ const Demo6 = () => { setShow(true) } return ( - <> + { onEnd={(val) => showToast(`${val}`)} /> - { setShow(false) }} - /> - + /> */} + ) } export default Demo6 diff --git a/src/packages/range/demos/taro/demo7.tsx b/src/packages/range/demos/taro/demo7.tsx index eb5de5905d..70cc958525 100644 --- a/src/packages/range/demos/taro/demo7.tsx +++ b/src/packages/range/demos/taro/demo7.tsx @@ -1,10 +1,22 @@ -import React, { useState } from 'react' -import { Range, Cell, Toast } from '@nutui/nutui-react-taro' +import React, { useMemo, useState } from 'react' +import { View } from '@tarojs/components' +import { Range, Cell /* , Toast */ } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo7 = () => { - const cellStyle = { - padding: '40px 18px', - } + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) const [show, setShow] = useState(false) const [msg, setMsg] = useState('') const showToast = (msg: string) => { @@ -12,7 +24,7 @@ const Demo7 = () => { setShow(true) } return ( - <> + { onEnd={(val) => showToast(`${val}`)} /> - { setShow(false) }} - /> - + /> */} + ) } export default Demo7 diff --git a/src/packages/range/demos/taro/demo8.tsx b/src/packages/range/demos/taro/demo8.tsx index 69fdf767d6..40d7a2ed9d 100644 --- a/src/packages/range/demos/taro/demo8.tsx +++ b/src/packages/range/demos/taro/demo8.tsx @@ -1,10 +1,22 @@ -import React, { useState } from 'react' -import { Range, Cell, Toast } from '@nutui/nutui-react-taro' +import React, { useMemo, useState } from 'react' +import { View } from '@tarojs/components' +import { Range, Cell /* , Toast */ } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo8 = () => { - const cellStyle = { - padding: '40px 18px', - } + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) const [show, setShow] = useState(false) const [msg, setMsg] = useState('') const showToast = (msg: string) => { @@ -12,7 +24,7 @@ const Demo8 = () => { setShow(true) } return ( - <> + { onEnd={(val) => showToast(`${val}`)} /> - { setShow(false) }} - /> - + /> */} + ) } export default Demo8 diff --git a/src/packages/range/demos/taro/demo9.tsx b/src/packages/range/demos/taro/demo9.tsx index 69cf9574ce..19bab8cb58 100644 --- a/src/packages/range/demos/taro/demo9.tsx +++ b/src/packages/range/demos/taro/demo9.tsx @@ -1,10 +1,22 @@ -import React from 'react' +import React, { useMemo } from 'react' import { Range, Cell } from '@nutui/nutui-react-taro' +import pxTransform from '@/utils/px-transform' +import { harmonyAndRn } from '@/utils/platform-taro' const Demo9 = () => { - const cellStyle = { - padding: '40px 18px', - } + const cellStyle = useMemo(() => { + return harmonyAndRn() + ? { + paddingTop: pxTransform(40), + paddingBottom: pxTransform(40), + paddingLeft: pxTransform(18), + paddingRight: pxTransform(18), + } + : { + padding: '40px 18px', + } + }, []) + return ( diff --git a/src/packages/range/range.harmony.css b/src/packages/range/range.harmony.css index d82abcd79e..7c15129047 100644 --- a/src/packages/range/range.harmony.css +++ b/src/packages/range/range.harmony.css @@ -1,240 +1,265 @@ .nut-range-container { display: flex; + flex-direction: row; position: relative; width: 100%; height: 4px; align-items: center; + justify-content: space-between; } -.nut-range-container .min { - font-size: 12px; - color: #1A1A1A; - user-select: none; +.nut-range-container-native { + height: auto; } -.nut-range-container .max { - font-size: 12px; - color: #1A1A1A; - user-select: none; -} -.nut-range-container .nut-range { + +.nut-range { display: block; position: relative; - width: 100%; height: 4px; margin: 0 15px; - background-color: #FFEBF1; + background-color: #ffebf1; border-radius: 2px; + flex: 1; cursor: pointer; } -.nut-range-container .nut-range::before { +.nut-range::before { position: absolute; inset-block: -8px; inset-inline: 0; content: ""; } -.nut-range-container .nut-range-bar { +.nut-range-min { + font-size: 12px; + color: #1a1a1a; + user-select: none; +} +.nut-range-max { + font-size: 12px; + color: #1a1a1a; + user-select: none; +} +.nut-range-bar { display: block; position: relative; width: 100%; height: 100%; - background: #FF0F23; - border-radius: inherit; + background: #ff0f23; + border-radius: 2px; transition: all 0.2s; } -.nut-range-container .nut-range-button { - display: block; +.nut-range-button { + position: absolute; + display: flex; width: 24px; height: 24px; background: #ffffff; border-radius: 50%; box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.15); - border: 1px solid #FF0F23; + border: 1px solid #ff0f23; outline: none; + align-items: center; + top: 50%; + left: 50%; +} +.nut-range-button-wrapper { + width: 24px; + height: 24px; +} +.nut-range-button-wrapper-right { + width: 24px; + height: 24px; } -.nut-range-container .nut-range-button-wrapper, .nut-range-container .nut-range-button-wrapper-right { +.nut-range-button-wrapper-left { + width: 24px; + height: 24px; +} +.nut-range-button-wrapper, .nut-range-button-wrapper-right { touch-action: none; position: absolute; top: 50%; - right: 0; - transform: translate3d(50%, -50%, 0); + left: 100%; cursor: grab; outline: none; } -.nut-range-container .nut-range-button-wrapper-left { +.nut-range-button-wrapper-left { position: absolute; top: 50%; left: 0; - transform: translate3d(-50%, -50%, 0); cursor: grab; outline: none; touch-action: none; } -.nut-range-container .nut-range-button .number { - width: 100%; - height: 100%; +.nut-range-button-number { + position: relative; + width: 200%; + height: 24px; + line-height: 14px; + padding: 5px 0; + left: 50%; display: flex; align-items: center; justify-content: center; user-select: none; font-size: 12px; - color: #1A1A1A; - transform: translate3d(0, -100%, 0); + color: #1a1a1a; + text-align: center; + vertical-align: center; + box-sizing: border-box; } -.nut-range-container .nut-range-disabled { +.nut-range-disabled { cursor: not-allowed; opacity: 0.54; } -.nut-range-container .nut-range-disabled .nut-range-button-wrapper, -.nut-range-container .nut-range-disabled .nut-range-button-wrapper-left, -.nut-range-container .nut-range-disabled .nut-range-button-wrapper-right { +.nut-range-disabled .nut-range-button-wrapper, +.nut-range-disabled .nut-range-button-wrapper-left, +.nut-range-disabled .nut-range-button-wrapper-right { cursor: not-allowed; } -.nut-range-container .nut-range-mark { +.nut-range-mark { position: absolute; width: 100%; + height: 14px; overflow: visible; top: 50%; - font-size: 12px; - padding-top: 14px; } -.nut-range-container .nut-range-mark-text { +.nut-range-mark-text-wrapper { position: absolute; + height: 100%; + top: 14px; display: inline-block; + transform: translateX(-10px); +} +.nut-range-mark-text { + position: absolute; line-height: 16px; + font-size: 12px; color: #999; text-align: center; word-break: keep-all; user-select: none; - transform: translateX(-10px); } -.nut-range-container .nut-range-tick { +.nut-range-tick { position: absolute; top: -20px; width: 11px; height: 11px; left: 0px; - border-radius: 50%; - background: #FFEBF1; + border-radius: 6px; + background: #ffebf1; } -.nut-range-container .nut-range-tick.active { - background: #FF0F23; +.nut-range-tick-active { + background: #ff0f23; } -.nut-range-container-vertical { + +.nut-range-vertical-container { height: 100%; flex-direction: column; padding: 0px 15px; } -.nut-range-container-vertical .nut-range { + +.nut-range-vertical { width: 4px; - height: 100%; + margin: 15px 0px; } -.nut-range-container-vertical .nut-range-button-wrapper, .nut-range-container-vertical .nut-range-button-wrapper-right { +.nut-range-vertical-button-wrapper, .nut-range-vertical-button-wrapper-right { position: absolute; top: initial; - bottom: 0px; + top: 100%; left: 50%; right: initial; - transform: translate3d(-50%, 50%, 0); } -.nut-range-container-vertical .nut-range-button-wrapper-left { +.nut-range-vertical-button-wrapper-left { top: 0px; left: 50%; right: initial; - transform: translate3d(-50%, -50%, 0); } -.nut-range-container-vertical .nut-range .number { - transform: translate3d(100%, 0, 0); -} -.nut-range-container-vertical .nut-range-vertical { - margin: 15px 0px; +.nut-range-vertical-button-number { + left: 0px; + top: 50%; } -.nut-range-container-vertical .nut-range-mark { +.nut-range-vertical-mark { position: absolute; - width: 100%; + width: 36px; + height: 100%; + top: initial; right: 50%; overflow: visible; font-size: 12px; - height: 100%; - top: initial; - width: 36px; padding: 0px; } -.nut-range-container-vertical .nut-range-mark-text { - width: 20px; +.nut-range-vertical-mark-hm { + left: -34px; +} +.nut-range-vertical-mark-text-wrapper { + height: 16px; position: absolute; display: inline-block; + user-select: none; + transform: translateY(-11px); +} +.nut-range-vertical-mark-text { + height: 100%; line-height: 16px; color: #999; text-align: center; word-break: keep-all; - user-select: none; - transform: translateY(-11px); } -.nut-range-container-vertical .nut-range-tick { +.nut-range-vertical-tick { position: absolute; - top: 0px; - left: 30px; - width: 11px; - height: 11px; - border-radius: 50%; - background: #FFEBF1; + top: 2px; + left: 31px; + width: 10px; + height: 10px; + border-radius: 5px; + background: #ffebf1; } -.nut-range-container-vertical .nut-range-tick.active { - background: #FF0F23; +.nut-range-vertical-tick-active { + background: #ff0f23; } -[dir=rtl] .nut-range-container-vertical .nut-range-button-wrapper, [dir=rtl] .nut-range-container-vertical .nut-range-button-wrapper-right, -.nut-rtl .nut-range-container-vertical .nut-range-button-wrapper, -.nut-rtl .nut-range-container-vertical .nut-range-button-wrapper-right { - right: 50%; +[dir=rtl] .nut-range-button-wrapper, [dir=rtl] .nut-range-button-wrapper-right, +.rtl-nut-range-button-wrapper, +.rtl-nut-range-button-wrapper-right { + left: 0; + right: initial; +} +[dir=rtl] .nut-range-button-wrapper-left, +.rtl-nut-range-button-wrapper-left { + right: 0; left: initial; - transform: translate3d(50%, 50%, 0); } -[dir=rtl] .nut-range-container-vertical .nut-range-button-wrapper-left, -.nut-rtl .nut-range-container-vertical .nut-range-button-wrapper-left { +[dir=rtl] .nut-range-tick, +.rtl-nut-range-tick { + right: 0px; + left: initial; +} +[dir=rtl] .nut-range-mark-text, +.rtl-nut-range-mark-text { + transform: translateX(10px); +} +[dir=rtl] .nut-range-vertical-button-wrapper, [dir=rtl] .nut-range-vertical-button-wrapper-right, +.rtl-nut-range-vertical-button-wrapper, +.rtl-nut-range-vertical-button-wrapper-right { right: 50%; left: initial; - transform: translate3d(50%, -50%, 0); } -[dir=rtl] .nut-range-container-vertical .nut-range .number, -.nut-rtl .nut-range-container-vertical .nut-range .number { - transform: translate3d(-100%, 0, 0); +[dir=rtl] .nut-range-vertical-button-wrapper-left, +.rtl-nut-range-vertical-button-wrapper-left { + right: 50%; + left: initial; } -[dir=rtl] .nut-range-container-vertical .nut-range-mark, -.nut-rtl .nut-range-container-vertical .nut-range-mark { +[dir=rtl] .nut-range-vertical-mark, +.rtl-nut-range-vertical-mark { right: auto; left: 50%; } -[dir=rtl] .nut-range-container-vertical .nut-range-tick, -.nut-rtl .nut-range-container-vertical .nut-range-tick { +[dir=rtl] .nut-range-vertical-tick, +.rtl-nut-range-vertical-tick { left: auto; right: 30px; margin-left: 0; margin-right: 0px; } -[dir=rtl] .nut-range-container-vertical .nut-range-mark-text, -.nut-rtl .nut-range-container-vertical .nut-range-mark-text { +[dir=rtl] .nut-range-vertical-mark-text-wrapper, +.rtl-nut-range-vertical-mark-text-wrapper { transform: translateY(-11px); -} - -[dir=rtl] .nut-range-button-wrapper, [dir=rtl] .nut-range-button-wrapper-right, -.nut-rtl .nut-range-button-wrapper, -.nut-rtl .nut-range-button-wrapper-right { - left: 0; - right: initial; - transform: translate3d(-50%, -50%, 0); -} -[dir=rtl] .nut-range-button-wrapper-left, -.nut-rtl .nut-range-button-wrapper-left { - right: 0; - left: initial; - transform: translate3d(50%, -50%, 0); -} -[dir=rtl] .nut-range-tick, -.nut-rtl .nut-range-tick { - right: 0px; - left: initial; -} -[dir=rtl] .nut-range-mark-text, -.nut-rtl .nut-range-mark-text { - transform: translateX(10px); } \ No newline at end of file diff --git a/src/packages/range/range.scss b/src/packages/range/range.scss index a338087494..d63484a13b 100644 --- a/src/packages/range/range.scss +++ b/src/packages/range/range.scss @@ -1,266 +1,263 @@ .nut-range-container { display: flex; + flex-direction: row; position: relative; width: 100%; height: $range-height; align-items: center; + justify-content: space-between; - .min, - .max { + &-native { + height: auto; + } +} + +.nut-range { + display: block; + position: relative; + // width: 100%; + height: $range-height; + margin: 0 $range-margin; + background-color: $range-inactive-color; + border-radius: 2px; + flex: 1; + cursor: pointer; + + &::before { + position: absolute; + inset-block: -8px; + inset-inline: 0; + content: ''; + } + + &-min, + &-max { font-size: $font-size-small; color: $range-color; user-select: none; } - .nut-range { + &-bar { display: block; position: relative; width: 100%; - height: $range-height; - margin: 0 $range-margin; - background-color: $range-inactive-color; + height: 100%; + background: $range-active-color; border-radius: 2px; - cursor: pointer; - - &::before { - position: absolute; - inset-block: -8px; - inset-inline: 0; - content: ''; - } + transition: all 0.2s; + } - &-bar { - display: block; - position: relative; - width: 100%; - height: 100%; - background: $range-active-color; - border-radius: inherit; - transition: all 0.2s; - } + &-button { + position: absolute; + display: flex; + width: $range-button-width; + height: $range-button-height; + background: $range-button-background; + border-radius: 50%; + box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.15); + border: $range-button-border; + outline: none; + align-items: center; + top: 50%; + left: 50%; - &-button { - display: block; + &-wrapper, + &-wrapper-right, + &-wrapper-left { width: $range-button-width; height: $range-button-height; - background: $range-button-background; - border-radius: 50%; - box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.15); - border: $range-button-border; - outline: none; - - &-wrapper, - &-wrapper-right { - touch-action: none; - position: absolute; - top: 50%; - right: 0; - transform: translate3d(50%, -50%, 0); - cursor: grab; - outline: none; - } - - &-wrapper-left { - position: absolute; - top: 50%; - left: 0; - transform: translate3d(-50%, -50%, 0); - cursor: grab; - outline: none; - touch-action: none; - } - - .number { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - user-select: none; - font-size: $font-size-small; - color: $range-color; - transform: translate3d(0, -100%, 0); - } } - &-disabled { - cursor: not-allowed; - opacity: 0.54; - - .nut-range-button-wrapper, - .nut-range-button-wrapper-left, - .nut-range-button-wrapper-right { - cursor: not-allowed; - } + &-wrapper, + &-wrapper-right { + touch-action: none; + position: absolute; + top: 50%; + left: 100%; + // right: 0; + // transform: translate3d(-50%, -50%, 0); + cursor: grab; + outline: none; } - &-mark { + &-wrapper-left { position: absolute; - width: 100%; - overflow: visible; top: 50%; - font-size: 12px; - padding-top: 14px; + left: 0; + // transform: translate3d(-50%, -50%, 0); + cursor: grab; + outline: none; + touch-action: none; } - &-mark-text { - position: absolute; - display: inline-block; - line-height: 16px; - color: #999; - text-align: center; - word-break: keep-all; + &-number { + position: relative; + width: 200%; + height: 24px; + line-height: 14px; + padding: 5px 0; + left: 50%; + display: flex; + align-items: center; + justify-content: center; user-select: none; - transform: translateX(-10px); + font-size: $font-size-small; + color: $range-color; + text-align: center; + vertical-align: center; + box-sizing: border-box; + // transform: translate3d(0, -100%, 0); } + } - &-tick { - position: absolute; - top: -20px; - width: 11px; - height: 11px; - left: 0px; - border-radius: 50%; - background: $range-inactive-color; + &-disabled { + cursor: not-allowed; + // 鸿蒙端在外层设置的透明度会分发到所有子图层上,导致样式与其他端不一致 + opacity: 0.54; - &.active { - background: $range-active-color; - } + .nut-range-button-wrapper, + .nut-range-button-wrapper-left, + .nut-range-button-wrapper-right { + cursor: not-allowed; } } - &-vertical { + &-mark { + position: absolute; + width: 100%; + height: 14px; + overflow: visible; + top: 50%; + } + + &-mark-text-wrapper { + position: absolute; height: 100%; - flex-direction: column; - padding: 0px 15px; - - .nut-range { - width: $range-height; - height: 100%; - - &-button { - &-wrapper, - &-wrapper-right { - position: absolute; - top: initial; - bottom: 0px; - left: 50%; - right: initial; - transform: translate3d(-50%, 50%, 0); - } - - &-wrapper-left { - top: 0px; - left: 50%; - right: initial; - transform: translate3d(-50%, -50%, 0); - } - } + top: 14px; + display: inline-block; + transform: translateX(-10px); + } - .number { - transform: translate3d(100%, 0, 0); - } + &-mark-text { + position: absolute; + line-height: 16px; + font-size: 12px; + color: #999; + text-align: center; + word-break: keep-all; + user-select: none; + } - &-vertical { - margin: $range-margin 0px; - } + &-tick { + position: absolute; + top: -20px; + width: 11px; + height: 11px; + left: 0px; + border-radius: 6px; + background: $range-inactive-color; + + &-active { + background: $range-active-color; + } + } +} - &-mark { - position: absolute; - width: 100%; - right: 50%; - overflow: visible; - font-size: 12px; - height: 100%; - top: initial; - width: 36px; - padding: 0px; - } +.nut-range-vertical-container { + height: 100%; + flex-direction: column; + padding: 0px 15px; +} - &-mark-text { - width: 20px; - position: absolute; - display: inline-block; - line-height: 16px; - color: #999; - text-align: center; - word-break: keep-all; - user-select: none; - transform: translateY(-11px); - } +.nut-range-vertical { + width: $range-height; + margin: $range-margin 0px; - &-tick { - position: absolute; - top: 0px; - left: 30px; - width: 11px; - height: 11px; - border-radius: 50%; - background: $range-inactive-color; - - &.active { - background: $range-active-color; - } - } + &-button { + &-wrapper, + &-wrapper-right { + position: absolute; + top: initial; + top: 100%; + left: 50%; + right: initial; + // transform: translate3d(-50%, -50%, 0); + } + + &-wrapper-left { + top: 0px; + left: 50%; + right: initial; + // transform: translate3d(-50%, -50%, 0); + } + + &-number { + left: 0px; + top: 50%; + // transform: translate3d(100%, 0, 0); } } -} -[dir='rtl'] .nut-range-container, -.nut-rtl .nut-range-container { - &-vertical { - .nut-range { - &-button { - &-wrapper, - &-wrapper-right { - right: 50%; - left: initial; - transform: translate3d(50%, 50%, 0); - } - - &-wrapper-left { - right: 50%; - left: initial; - transform: translate3d(50%, -50%, 0); - } - } + &-mark { + position: absolute; + width: 36px; + height: 100%; + top: initial; + right: 50%; + overflow: visible; + font-size: 12px; + padding: 0px; + } - .number { - transform: translate3d(-100%, 0, 0); - } + &-mark-hm { + left: -34px; + } - &-mark { - right: auto; - left: 50%; - } + &-mark-text-wrapper { + // width: 20px; + height: 16px; + position: absolute; + display: inline-block; + user-select: none; + transform: translateY(-11px); + } - &-tick { - left: auto; - right: 30px; - margin-left: 0; - margin-right: -0px; - } + &-mark-text { + height: 100%; + line-height: 16px; + color: #999; + text-align: center; + word-break: keep-all; + } - &-mark-text { - transform: translateY(-11px); - } + &-tick { + position: absolute; + top: 2px; + left: 31px; + width: 10px; + height: 10px; + border-radius: 5px; + background: $range-inactive-color; + + &-active { + background: $range-active-color; } } } [dir='rtl'] .nut-range, -.nut-rtl .nut-range { +.rtl-nut-range { &-button { &-wrapper, &-wrapper-right { left: 0; right: initial; - transform: translate3d(-50%, -50%, 0); + // transform: translate3d(-50%, -50%, 0); } &-wrapper-left { right: 0; left: initial; - transform: translate3d(50%, -50%, 0); + // transform: translate3d(50%, -50%, 0); } } @@ -272,4 +269,41 @@ &-mark-text { transform: translateX(10px); } + + &-vertical { + &-button { + &-wrapper, + &-wrapper-right { + right: 50%; + left: initial; + // transform: translate3d(50%, 50%, 0); + } + + &-wrapper-left { + right: 50%; + left: initial; + // transform: translate3d(50%, -50%, 0); + } + + &-number { + // transform: translate3d(-100%, 0, 0); + } + } + + &-mark { + right: auto; + left: 50%; + } + + &-tick { + left: auto; + right: 30px; + margin-left: 0; + margin-right: -0px; + } + + &-mark-text-wrapper { + transform: translateY(-11px); + } + } } diff --git a/src/packages/range/range.taro.tsx b/src/packages/range/range.taro.tsx index f5186f4ad0..7d5ef81324 100644 --- a/src/packages/range/range.taro.tsx +++ b/src/packages/range/range.taro.tsx @@ -5,15 +5,18 @@ import React, { useRef, useCallback, ReactNode, + useMemo, } from 'react' +import { pxTransform } from '@tarojs/taro' import classNames from 'classnames' -import { View } from '@tarojs/components' +import { View, Text } from '@tarojs/components' import { useTouch } from '@/utils/use-touch' import { BasicComponent, ComponentDefaults } from '@/utils/typings' import { usePropsValue } from '@/utils/use-props-value' import { getRectByTaro } from '@/utils/get-rect-by-taro' import { RangeMark, RangeValue } from './types' import { useRtl } from '../configprovider/index.taro' +import { harmony, harmonyAndRn, rn } from '@/utils/platform-taro' export interface RangeProps extends BasicComponent { value: RangeValue @@ -44,6 +47,23 @@ const defaultProps = { marks: {}, } as RangeProps +const isRn = rn() +const isHm = harmony() +const isNative = harmonyAndRn() +const classPrefix = 'nut-range' +const verticalClassPrefix = `${classPrefix}-vertical` + +const isSameValue = (newValue: RangeValue, oldValue: RangeValue) => { + return JSON.stringify(newValue) === JSON.stringify(oldValue) +} + +const handleOverlap = (value: number[]) => { + if (value[0] > value[1]) { + return value.slice(0).reverse() + } + return value +} + export const Range: FunctionComponent< Partial & Omit< @@ -54,6 +74,7 @@ export const Range: FunctionComponent< const rtl = useRtl() const { className, + style, range, disabled, button, @@ -72,7 +93,10 @@ export const Range: FunctionComponent< defaultValue, } = { ...defaultProps, ...props } - const classPrefix = 'nut-range' + const rtlClassPrefix = useMemo( + () => `rtl-${vertical ? verticalClassPrefix : classPrefix}`, + [vertical] + ) const [buttonIndex, setButtonIndex] = useState(0) const [dragStatus, setDragStatus] = useState('start' || 'draging' || '') const touch = useTouch() @@ -80,6 +104,7 @@ export const Range: FunctionComponent< const [marksList, setMarksList] = useState([]) const [startValue, setStartValue] = useState(0) + const scope = useMemo(() => max - min, [max, min]) const handleChange = (value: RangeValue) => { onChange && onChange(value) @@ -91,7 +116,7 @@ export const Range: FunctionComponent< onChange: handleChange, }) - const [exactValue, setEaxctValue] = useState( + const [exactValue, setExactValue] = useState( () => value || defaultValue || 0 ) const marksRef = useRef<{ [key: string]: any }>({}) @@ -116,28 +141,12 @@ export const Range: FunctionComponent< setMarksList(list) } } - }, [marks]) - - const scope = () => { - return max - min - } - - const classes = classNames(classPrefix, { - [`${classPrefix}-disabled`]: disabled, - [`${classPrefix}-vertical`]: vertical, - }) - - const containerClasses = classNames( - `${classPrefix}-container`, - { - [`${classPrefix}-container-vertical`]: vertical, - }, - className - ) + }, [marks, max, min]) const markClassName = useCallback( (mark: any) => { const classPrefix = 'nut-range-mark' + const verticalClassPrefix = 'nut-range-vertical-mark' let lowerBound = min let upperBound = max if (range && Array.isArray(current)) { @@ -147,35 +156,50 @@ export const Range: FunctionComponent< upperBound = current as number } const isActive = mark <= upperBound && mark >= lowerBound - return [ - `${classPrefix}-text`, - `${isActive ? `${classPrefix}-text-active` : ''}`, - ].join(' ') + const classNames = [ + `${classPrefix}-text-wrapper`, + `${isActive ? `${classPrefix}-text-wrapper-active` : ''}`, + ] + + if (vertical) { + classNames.push(`${verticalClassPrefix}-text-wrapper`) + isActive && + classNames.push(`${verticalClassPrefix}-text-wrapper-active`) + } + + if (rtl) { + classNames.push(`${rtlClassPrefix}-mark-text-wrapper`) + } + + return classNames.join(' ') }, - [range, current, min, max] + [min, max, range, current, vertical, rtl, rtlClassPrefix] ) - const isRange = (val: any) => { - return !!range && Array.isArray(val) - } + const isRange = useCallback( + (val: any) => { + return !!range && Array.isArray(val) + }, + [range] + ) - const calcMainAxis = () => { + const calcMainAxis = useCallback(() => { const modelVal = current as any if (isRange(modelVal)) { - return `${((modelVal[1] - modelVal[0]) * 100) / scope()}%` + return `${((modelVal[1] - modelVal[0]) * 100) / scope}%` } - return `${((modelVal - min) * 100) / scope()}%` - } + return `${((modelVal - min) * 100) / scope}%` + }, [current, isRange, min, scope]) - const calcOffset = () => { + const calcOffset = useCallback(() => { const modelVal = current as any if (isRange(modelVal)) { - return `${((modelVal[0] - min) * 100) / scope()}%` + return `${((modelVal[0] - min) * 100) / scope}%` } return `0%` - } + }, [current, isRange, min, scope]) - const barStyle = () => { + const barStyle = useCallback(() => { if (vertical) { return { height: calcMainAxis(), @@ -189,238 +213,449 @@ export const Range: FunctionComponent< [dir]: calcOffset(), transition: dragStatus ? 'none' : undefined, } - } + }, [calcMainAxis, calcOffset, dragStatus, rtl, vertical]) - const marksStyle = (mark: any) => { - const dir = rtl ? 'right' : 'left' - let style: any = { - [dir]: `${((mark - min) / scope()) * 100}%`, - } - if (vertical) { - style = { - top: `${((mark - min) / scope()) * 100}%`, + const marksStyle = useCallback( + (mark: any) => { + const dir = rtl ? 'right' : 'left' + let style: any = { + [dir]: `${((mark - min) / scope) * 100}%`, } - } - return style - } + if (vertical) { + style = { + top: `${((mark - min) / scope) * 100}%`, + } + } + return style + }, + [min, rtl, scope, vertical] + ) - const tickClass = (mark: any) => { - if (range && Array.isArray(current)) { - return mark <= current[1] && mark >= current[0] - } - return mark <= current - } + const tickClass = useCallback( + (mark: any) => { + if (range && Array.isArray(current)) { + return mark <= current[1] && mark >= current[0] + } + return mark <= current + }, + [current, range] + ) - const format = (value: number) => { - value = Math.max(+min, Math.min(value, +max)) - return Math.round(value / +step) * +step - } + const format = useCallback( + (value: number) => { + value = Math.max(+min, Math.min(value, +max)) + return Math.round(value / +step) * +step + }, + [max, min, step] + ) - const isSameValue = (newValue: RangeValue, oldValue: RangeValue) => { - return JSON.stringify(newValue) === JSON.stringify(oldValue) - } + const updateValue = useCallback( + (value: any, end?: boolean) => { + if (isRange(value)) { + value = handleOverlap(value).map(format) + } else { + value = format(value) + } + if (!isSameValue(value, current)) { + setCurrent(value) + } + end && onEnd && onEnd(value) + }, + [current, format, isRange, onEnd, setCurrent] + ) - const handleOverlap = (value: number[]) => { - if (value[0] > value[1]) { - return value.slice(0).reverse() - } - return value - } - const updateValue = (value: any, end?: boolean) => { - if (isRange(value)) { - value = handleOverlap(value).map(format) - } else { - value = format(value) - } - if (!isSameValue(value, current)) { - setCurrent(value) - } - end && onEnd && onEnd(value) - } + const click = useCallback( + async (event: any) => { + if (disabled || !root.current) { + return + } + setDragStatus('') + const rect = await getRectByTaro(root.current) + let x = + typeof event.detail?.x !== 'undefined' ? event.detail.x : event.clientX + if (isHm) x = parseFloat(pxTransform(event.windowX)) + let delta = x - rect.left + let total = rect.width + + if (vertical) { + let y = + typeof event.detail?.y !== 'undefined' + ? event.detail.y + : event.clientY + if (isHm) y = parseFloat(pxTransform(event.windowY)) + delta = y - rect.top + total = rect.height + } + const value = min + (delta / total) * scope + setExactValue(current) + if (isRange(current)) { + const [left, right] = current as any + const middle = (left + right) / 2 + if (value <= middle) { + updateValue([value, right], true) + } else { + updateValue([left, value], true) + } + } else { + updateValue(value, true) + } + }, + [current, disabled, isRange, min, scope, updateValue, vertical] + ) - const click = async (event: any) => { - if (disabled || !root.current) { - return - } - setDragStatus('') - const rect = await getRectByTaro(root.current) - let delta = (event.detail.x ? event.detail.x : event.clientX) - rect.left - let total = rect.width - if (vertical) { - delta = (event.detail.y ? event.detail.y : event.clientY) - rect.top - total = rect.height - } - const value = min + (delta / total) * scope() - setEaxctValue(current) - if (isRange(current)) { - const [left, right] = current as any - const middle = (left + right) / 2 - if (value <= middle) { - updateValue([value, right], true) + const onTouchStart = useCallback( + (event: any) => { + if (disabled) { + return + } + touch.start(event) + setExactValue(current) + if (isRange(current)) { + setStartValue((current as number[]).map(format)) } else { - updateValue([left, value], true) + setStartValue(format(current as number)) } - } else { - updateValue(value, true) - } - } - const onTouchStart = (event: any) => { + setDragStatus('start') + }, + [current, disabled, format, isRange, touch] + ) + + const onTouchMove = useCallback( + async (event: any) => { + // @TODO RN、鸿蒙端垂直滑动时,页面会一同滑动,待解决 + if (disabled || !root.current) { + return + } + if (dragStatus === 'start') { + onStart && onStart() + } + + touch.move(isRn ? event.nativeEvent : event) + // console.log(JSON.stringify(event.touches[0])) + + setDragStatus('draging') + + const rect = await getRectByTaro(root.current) + if (!rect) return + let delta = isHm + ? parseFloat(pxTransform(touch.deltaX.current)) + : touch.deltaX.current + let total = rect.width + // console.log(pxTransform(delta), total) + let diff = (delta / total) * scope + diff = rtl ? -diff : diff + if (vertical) { + delta = isHm + ? parseFloat(pxTransform(touch.deltaY.current)) + : touch.deltaY.current + total = rect.height + diff = (delta / total) * scope + } + + let newValue + if (isRange(startValue)) { + newValue = (exactValue as number[]).slice() + newValue[buttonIndex] = startValue[buttonIndex] + diff + } else { + newValue = startValue + diff + } + setExactValue(newValue) + updateValue(newValue) + }, + [ + buttonIndex, + disabled, + dragStatus, + exactValue, + isRange, + onStart, + rtl, + scope, + startValue, + touch, + updateValue, + vertical, + ] + ) + + const onTouchEnd = useCallback(() => { if (disabled) { return } - touch.start(event) - setEaxctValue(current) - if (isRange(current)) { - setStartValue((current as number[]).map(format)) - } else { - setStartValue(format(current as number)) + if (dragStatus === 'draging') { + updateValue(current, true) } + setDragStatus('') + }, [current, disabled, dragStatus, updateValue]) - setDragStatus('start') - } + const curValue = useCallback( + (idx?: number) => { + const modelVal = current as any + const value = typeof idx === 'number' ? modelVal[idx] : modelVal + return value + }, + [current] + ) - const onTouchMove = async (event: any) => { - if (disabled || !root.current) { - return - } - if (dragStatus === 'start') { - onStart && onStart() + const buttonTransform = useMemo(() => { + const borderRadis = { borderRadius: pxTransform(13) } + const transform = { + transform: 'translate(-50%, -50%)', } - touch.move(event) + if (isRn) { + // @TODO 支持变量 + return { + ...borderRadis, + transform: [{ translateX: pxTransform(-12) }], + } + } + if (isHm) { + return { + ...borderRadis, + ...transform, + } + } + return { + ...transform, + } + }, []) + const buttonNumberTransform = useMemo(() => { + if (isRn) { + // @TODO 支持变量 + return [ + { translateX: pxTransform(vertical ? 26 : -12) }, + { translateY: pxTransform(vertical ? -12 : -26) }, + ] + } - setDragStatus('draging') + return vertical ? 'translate(100%, -50%)' : 'translate(-50%, -100%)' + }, [vertical]) - const rect = await getRectByTaro(root.current) - if (!rect) return - let delta = touch.deltaX.current - let total = rect.width - let diff = (delta / total) * scope() - diff = rtl ? -diff : diff - if (vertical) { - delta = touch.deltaY.current - total = rect.height - diff = (delta / total) * scope() - } + const renderButton = useCallback( + (index?: number) => { + return ( + + {button || ( + + {currentDescription !== null && ( + + {currentDescription + ? currentDescription(curValue(index)) + : curValue(index)} + + )} + + )} + + ) + }, + [ + button, + buttonNumberTransform, + buttonTransform, + curValue, + currentDescription, + rtl, + rtlClassPrefix, + vertical, + ] + ) - let newValue - if (isRange(startValue)) { - newValue = (exactValue as number[]).slice() - newValue[buttonIndex] = startValue[buttonIndex] + diff - } else { - newValue = startValue + diff - } - setEaxctValue(newValue) - updateValue(newValue) - } + const renderMarks = useCallback(() => { + if (marksList.length <= 0) return null - const onTouchEnd = () => { - if (disabled) { - return - } - if (dragStatus === 'draging') { - updateValue(current, true) + return ( + + {marksList.map((mark: any) => { + return ( + + + {Array.isArray(marks) ? marksRef.current[mark] : marks[mark]} + + + + ) + })} + + ) + }, [ + markClassName, + marks, + marksList, + marksStyle, + rtl, + rtlClassPrefix, + tickClass, + vertical, + ]) + + const wrapperTransform = useMemo(() => { + // @TODO 支持变量 + const wrapperTransformRN = [ + { translateX: pxTransform(vertical ? -12 : -13) }, + { translateY: pxTransform(-12) }, + ] + const wrapperTransform = 'translate(-50%, -50%)' + + return isRn ? wrapperTransformRN : wrapperTransform + }, [vertical]) + const rangeWrapperTransform = useMemo(() => { + if (isRn) { + // @TODO 支持变量 + return [ + { + translateX: pxTransform(-12), + }, + { + translateY: pxTransform(-13), + }, + ] } - setDragStatus('') - } - const curValue = (idx?: number) => { - const modelVal = current as any - const value = typeof idx === 'number' ? modelVal[idx] : modelVal - return value - } + return 'translate(-50%, -50%)' + }, []) + + const renderButtonWrapper = useCallback(() => { + if (range) + return [0, 1].map((item, index) => { + const isLeft = index === 0 + const suffix = isLeft ? 'left' : 'right' + + return ( + { + if (typeof index === 'number') { + // 实时更新当前拖动的按钮索引 + setButtonIndex(index) + } + onTouchStart(e) + }} + onTouchMove={(e) => onTouchMove(e)} + onTouchEnd={onTouchEnd} + onTouchCancel={onTouchEnd} + onClick={(e) => !isRn && e.stopPropagation()} + > + {renderButton(index)} + + ) + }) - const renderButton = (index?: number) => { return ( - <> - {button || ( -
- {currentDescription !== null && ( -
- {currentDescription - ? currentDescription(curValue(index)) - : curValue(index)} -
- )} -
- )} - + onTouchStart(e)} + onTouchMove={(e) => onTouchMove(e)} + onTouchEnd={onTouchEnd} + onTouchCancel={onTouchEnd} + onClick={(e) => !isRn && e.stopPropagation()} + > + {renderButton()} + ) - } + }, [ + onTouchEnd, + onTouchMove, + onTouchStart, + range, + rangeWrapperTransform, + renderButton, + rtl, + rtlClassPrefix, + vertical, + wrapperTransform, + ]) return ( -
+ {minDescription !== null && ( -
{minDescription || min}
+ {minDescription || min} )} -
click(e)}> - {marksList.length > 0 && ( -
- {marksList.map((mark: any) => { - return ( - - {Array.isArray(marks) ? marksRef.current[mark] : marks[mark]} - - - ) - })} -
- )} - -
- {range ? ( - [0, 1].map((item, index) => { - return ( -
{ - if (typeof index === 'number') { - // 实时更新当前拖动的按钮索引 - setButtonIndex(index) - } - onTouchStart(e) - }} - onTouchMove={(e) => onTouchMove(e)} - onTouchEnd={onTouchEnd} - onTouchCancel={onTouchEnd} - onClick={(e) => e.stopPropagation()} - > - {renderButton(index)} -
- ) - }) - ) : ( - onTouchStart(e)} - onTouchMove={(e) => onTouchMove(e)} - onTouchEnd={onTouchEnd} - onTouchCancel={onTouchEnd} - onClick={(e) => e.stopPropagation()} - > - {renderButton()} - - )} -
-
+ click(e)} + > + {renderMarks()} + + + {renderButtonWrapper()} + + {maxDescription !== null && ( -
{maxDescription || max}
+ {maxDescription || max} )} -
+
) } diff --git a/src/packages/range/range.tsx b/src/packages/range/range.tsx index 328a2d6dc6..a118ebd273 100644 --- a/src/packages/range/range.tsx +++ b/src/packages/range/range.tsx @@ -4,6 +4,7 @@ import React, { useState, useRef, useCallback, + useMemo, ReactNode, } from 'react' import type { TouchEvent } from 'react' @@ -44,6 +45,20 @@ const defaultProps = { marks: {}, } as RangeProps +const classPrefix = 'nut-range' +const verticalClassPrefix = `${classPrefix}-vertical` + +const isSameValue = (newValue: RangeValue, oldValue: RangeValue) => { + return JSON.stringify(newValue) === JSON.stringify(oldValue) +} + +const handleOverlap = (value: number[]) => { + if (value[0] > value[1]) { + return value.slice(0).reverse() + } + return value +} + export const Range: FunctionComponent< Partial & Omit< @@ -54,6 +69,7 @@ export const Range: FunctionComponent< const rtl = useRtl() const { className, + style, range, disabled, button, @@ -72,7 +88,10 @@ export const Range: FunctionComponent< defaultValue, } = { ...defaultProps, ...props } - const classPrefix = 'nut-range' + const rtlClassPrefix = useMemo( + () => `rtl-${vertical ? verticalClassPrefix : classPrefix}`, + [vertical] + ) const [buttonIndex, setButtonIndex] = useState(0) const [dragStatus, setDragStatus] = useState('start' || 'draging' || '') const touch = useTouch() @@ -80,6 +99,7 @@ export const Range: FunctionComponent< const [marksList, setMarksList] = useState([]) const [startValue, setStartValue] = useState(0) + const scope = useMemo(() => max - min, [max, min]) const handleChange = (value: RangeValue) => { onChange && onChange(value) @@ -91,7 +111,7 @@ export const Range: FunctionComponent< onChange: handleChange, }) - const [exactValue, setEaxctValue] = useState( + const [exactValue, setExactValue] = useState( () => value || defaultValue || 0 ) const marksRef = useRef<{ [key: string]: any }>({}) @@ -116,28 +136,12 @@ export const Range: FunctionComponent< setMarksList(list) } } - }, [marks]) - - const scope = () => { - return max - min - } - - const classes = classNames(classPrefix, { - [`${classPrefix}-disabled`]: disabled, - [`${classPrefix}-vertical`]: vertical, - }) - - const containerClasses = classNames( - `${classPrefix}-container`, - { - [`${classPrefix}-container-vertical`]: vertical, - }, - className - ) + }, [marks, max, min]) const markClassName = useCallback( (mark: any) => { const classPrefix = 'nut-range-mark' + const verticalClassPrefix = 'nut-range-vertical-mark' let lowerBound = min let upperBound = max if (range && Array.isArray(current)) { @@ -147,35 +151,50 @@ export const Range: FunctionComponent< upperBound = current as number } const isActive = mark <= upperBound && mark >= lowerBound - return [ - `${classPrefix}-text`, - `${isActive ? `${classPrefix}-text-active` : ''}`, - ].join(' ') + const classNames = [ + `${classPrefix}-text-wrapper`, + `${isActive ? `${classPrefix}-text-wrapper-active` : ''}`, + ] + + if (vertical) { + classNames.push(`${verticalClassPrefix}-text-wrapper`) + isActive && + classNames.push(`${verticalClassPrefix}-text-active-wrapper`) + } + + if (rtl) { + classNames.push(`${rtlClassPrefix}-mark-text-wrapper`) + } + + return classNames.join(' ') }, - [range, current, min, max] + [min, max, range, current, vertical, rtl, rtlClassPrefix] ) - const isRange = (val: any) => { - return !!range && Array.isArray(val) - } + const isRange = useCallback( + (val: any) => { + return !!range && Array.isArray(val) + }, + [range] + ) - const calcMainAxis = () => { + const calcMainAxis = useCallback(() => { const modelVal = current as any if (isRange(modelVal)) { - return `${((modelVal[1] - modelVal[0]) * 100) / scope()}%` + return `${((modelVal[1] - modelVal[0]) * 100) / scope}%` } - return `${((modelVal - min) * 100) / scope()}%` - } + return `${((modelVal - min) * 100) / scope}%` + }, [current, isRange, min, scope]) - const calcOffset = () => { + const calcOffset = useCallback(() => { const modelVal = current as any if (isRange(modelVal)) { - return `${((modelVal[0] - min) * 100) / scope()}%` + return `${((modelVal[0] - min) * 100) / scope}%` } return `0%` - } + }, [current, isRange, min, scope]) - const barStyle = () => { + const barStyle = useCallback(() => { if (vertical) { return { height: calcMainAxis(), @@ -189,129 +208,153 @@ export const Range: FunctionComponent< [dir]: calcOffset(), transition: dragStatus ? 'none' : undefined, } - } + }, [calcMainAxis, calcOffset, dragStatus, rtl, vertical]) - const marksStyle = (mark: any) => { - const dir = rtl ? 'right' : 'left' - let style: any = { - [dir]: `${((mark - min) / scope()) * 100}%`, - } - if (vertical) { - style = { - top: `${((mark - min) / scope()) * 100}%`, + const marksStyle = useCallback( + (mark: any) => { + const dir = rtl ? 'right' : 'left' + let style: any = { + [dir]: `${((mark - min) / scope) * 100}%`, } - } - return style - } - - const tickClass = (mark: any) => { - if (range && Array.isArray(current)) { - return mark <= current[1] && mark >= current[0] - } - return mark <= current - } + if (vertical) { + style = { + top: `${((mark - min) / scope) * 100}%`, + } + } + return style + }, + [min, rtl, scope, vertical] + ) - const format = (value: number) => { - value = Math.max(+min, Math.min(value, +max)) - return Math.round(value / +step) * +step - } + const tickClass = useCallback( + (mark: any) => { + if (range && Array.isArray(current)) { + return mark <= current[1] && mark >= current[0] + } + return mark <= current + }, + [current, range] + ) - const isSameValue = (newValue: RangeValue, oldValue: RangeValue) => { - return JSON.stringify(newValue) === JSON.stringify(oldValue) - } + const format = useCallback( + (value: number) => { + value = Math.max(+min, Math.min(value, +max)) + return Math.round(value / +step) * +step + }, + [max, min, step] + ) - const handleOverlap = (value: number[]) => { - if (value[0] > value[1]) { - return value.slice(0).reverse() - } - return value - } - const updateValue = (value: any, end?: boolean) => { - if (isRange(value)) { - value = handleOverlap(value).map(format) - } else { - value = format(value) - } - if (!isSameValue(value, current)) { - setCurrent(value) - } - end && onEnd && onEnd(value) - } + const updateValue = useCallback( + (value: any, end?: boolean) => { + if (isRange(value)) { + value = handleOverlap(value).map(format) + } else { + value = format(value) + } + if (!isSameValue(value, current)) { + setCurrent(value) + } + end && onEnd && onEnd(value) + }, + [current, format, isRange, onEnd, setCurrent] + ) - const click = (event: any) => { - if (disabled || !root.current) { - return - } - setDragStatus('') - const rect = getRect(root.current) - let delta = event.clientX - rect.left - let total = rect.width - if (vertical) { - delta = event.clientY - rect.top - total = rect.height - } - const value = min + (delta / total) * scope() - setEaxctValue(current) - if (isRange(current)) { - const [left, right] = current as any - const middle = (left + right) / 2 - if (value <= middle) { - updateValue([value, right], true) + const click = useCallback( + (event: any) => { + if (disabled || !root.current) { + return + } + setDragStatus('') + const rect = getRect(root.current) + let delta = event.clientX - rect.left + let total = rect.width + if (vertical) { + delta = event.clientY - rect.top + total = rect.height + } + const value = min + (delta / total) * scope + setExactValue(current) + if (isRange(current)) { + const [left, right] = current as any + const middle = (left + right) / 2 + if (value <= middle) { + updateValue([value, right], true) + } else { + updateValue([left, value], true) + } } else { - updateValue([left, value], true) + updateValue(value, true) } - } else { - updateValue(value, true) - } - } + }, + [current, disabled, isRange, min, scope, updateValue, vertical] + ) - const onTouchStart = (event: TouchEvent) => { - if (disabled) { - return - } - touch.start(event) - setEaxctValue(current) - if (isRange(current)) { - setStartValue((current as number[]).map(format)) - } else { - setStartValue(format(current as number)) - } + const onTouchStart = useCallback( + (event: any) => { + if (disabled) { + return + } + touch.start(event) + setExactValue(current) + if (isRange(current)) { + setStartValue((current as number[]).map(format)) + } else { + setStartValue(format(current as number)) + } - setDragStatus('start') - } + setDragStatus('start') + }, + [current, disabled, format, isRange, touch] + ) - const onTouchMove = (event: TouchEvent) => { - event.stopPropagation() - if (disabled || !root.current) { - return - } - if (dragStatus === 'start') { - onStart && onStart() - } - touch.move(event) - setDragStatus('draging') - const rect = getRect(root.current) - let delta = touch.deltaX.current - let total = rect.width - let diff = (delta / total) * scope() - diff = rtl ? -diff : diff - if (vertical) { - delta = touch.deltaY.current - total = rect.height - diff = (delta / total) * scope() - } - let newValue - if (isRange(startValue)) { - newValue = (exactValue as number[]).slice() - newValue[buttonIndex] = startValue[buttonIndex] + diff - } else { - newValue = startValue + diff - } - setEaxctValue(newValue) - updateValue(newValue) - } + const onTouchMove = useCallback( + (event: TouchEvent) => { + event.stopPropagation() + if (disabled || !root.current) { + return + } + if (dragStatus === 'start') { + onStart && onStart() + } + touch.move(event) + setDragStatus('draging') + const rect = getRect(root.current) + let delta = touch.deltaX.current + let total = rect.width + let diff = (delta / total) * scope + diff = rtl ? -diff : diff + if (vertical) { + delta = touch.deltaY.current + total = rect.height + diff = (delta / total) * scope + } + let newValue + if (isRange(startValue)) { + newValue = (exactValue as number[]).slice() + newValue[buttonIndex] = startValue[buttonIndex] + diff + } else { + newValue = startValue + diff + } + setExactValue(newValue) + updateValue(newValue) + }, + [ + buttonIndex, + disabled, + dragStatus, + exactValue, + isRange, + onStart, + rtl, + scope, + startValue, + touch, + updateValue, + vertical, + ] + ) - const onTouchEnd = () => { + const onTouchEnd = useCallback(() => { if (disabled) { return } @@ -319,101 +362,209 @@ export const Range: FunctionComponent< updateValue(current, true) } setDragStatus('') - } + }, [current, disabled, dragStatus, updateValue]) - const curValue = (idx?: number) => { - const modelVal = current as any - const value = typeof idx === 'number' ? modelVal[idx] : modelVal - return value - } + const curValue = useCallback( + (idx?: number) => { + const modelVal = current as any + const value = typeof idx === 'number' ? modelVal[idx] : modelVal + return value + }, + [current] + ) + + const renderButton = useCallback( + (index?: number) => { + const buttonNumberTransform = vertical + ? 'translate(100%, -50%)' + : 'translate(-50%, -100%)' + + return ( + <> + {button || ( +
+ {currentDescription !== null && ( +
+ {currentDescription + ? currentDescription(curValue(index)) + : curValue(index)} +
+ )} +
+ )} + + ) + }, + [button, curValue, currentDescription, rtl, rtlClassPrefix, vertical] + ) + + const renderMarks = useCallback(() => { + if (marksList.length <= 0) return null - const renderButton = (index?: number) => { return ( - <> - {button || ( -
- {currentDescription !== null && ( -
- {currentDescription - ? currentDescription(curValue(index)) - : curValue(index)} -
- )} +
+ {marksList.map((mark: any) => { + return ( + + + {Array.isArray(marks) ? marksRef.current[mark] : marks[mark]} + + + + ) + })} +
+ ) + }, [ + markClassName, + marks, + marksList, + marksStyle, + rtl, + rtlClassPrefix, + tickClass, + vertical, + ]) + + const getWrapperTransform = useCallback(() => { + const wrapperTransform = 'translate(-50%, -50%)' + + return wrapperTransform + }, []) + + const renderButtonWrapper = useCallback(() => { + if (range) + return [0, 1].map((item, index) => { + const isLeft = index === 0 + const suffix = isLeft ? 'left' : 'right' + + const transform = 'translate(-50%, -50%)' + + return ( +
{ + if (typeof index === 'number') { + // 实时更新当前拖动的按钮索引 + setButtonIndex(index) + } + onTouchStart(e) + }} + onTouchMove={(e) => onTouchMove(e)} + onTouchEnd={onTouchEnd} + onTouchCancel={onTouchEnd} + onClick={(e) => e.stopPropagation()} + > + {renderButton(index)}
- )} - + ) + }) + + return ( +
onTouchStart(e)} + onTouchMove={(e) => onTouchMove(e)} + onTouchEnd={onTouchEnd} + onTouchCancel={onTouchEnd} + onClick={(e) => e.stopPropagation()} + > + {renderButton()} +
) - } + }, [ + getWrapperTransform, + onTouchEnd, + onTouchMove, + onTouchStart, + range, + renderButton, + rtl, + rtlClassPrefix, + vertical, + ]) return ( -
+
{minDescription !== null && ( -
{minDescription || min}
+
{minDescription || min}
)} -
click(e)}> - {marksList.length > 0 && ( -
- {marksList.map((mark: any) => { - return ( - - {Array.isArray(marks) ? marksRef.current[mark] : marks[mark]} - - - ) - })} -
- )} +
click(e)} + > + {renderMarks()} -
- {range ? ( - [0, 1].map((item, index) => { - return ( -
{ - if (typeof index === 'number') { - // 实时更新当前拖动的按钮索引 - setButtonIndex(index) - } - onTouchStart(e) - }} - onTouchMove={(e) => onTouchMove(e)} - onTouchEnd={onTouchEnd} - onTouchCancel={onTouchEnd} - onClick={(e) => e.stopPropagation()} - > - {renderButton(index)} -
- ) - }) - ) : ( -
onTouchStart(e)} - onTouchMove={(e) => onTouchMove(e)} - onTouchEnd={onTouchEnd} - onTouchCancel={onTouchEnd} - onClick={(e) => e.stopPropagation()} - > - {renderButton()} -
- )} +
+ {renderButtonWrapper()}
{maxDescription !== null && ( -
{maxDescription || max}
+
{maxDescription || max}
)}
) diff --git a/src/utils/use-touch.ts b/src/utils/use-touch.ts index 8653eea888..b3484987b5 100644 --- a/src/utils/use-touch.ts +++ b/src/utils/use-touch.ts @@ -52,10 +52,7 @@ export function useTouch() { typeof touch.pageX !== 'undefined' ) return touch.pageX - if (typeof touch.clientX !== 'undefined') return touch.clientX - if (typeof touch.pageX !== 'undefined') return touch.pageX - if (typeof touch.screenX !== 'undefined') return touch.screenX - return 0 + return touch.screenX ?? touch.pageX ?? touch.clientX ?? 0 } const getY = (touch: React.Touch) => { @@ -64,10 +61,7 @@ export function useTouch() { typeof touch.pageY !== 'undefined' ) return touch.pageY - if (typeof touch.clientY !== 'undefined') return touch.clientY - if (typeof touch.pageY !== 'undefined') return touch.pageY - if (typeof touch.screenY !== 'undefined') return touch.screenY - return 0 + return touch.screenY ?? touch.pageY ?? touch.clientY ?? 0 } const start = (event: React.TouchEvent) => {