@ledvance/base 1.3.103 → 1.3.105
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/localazy.json +9 -1
- package/package.json +1 -1
- package/src/components/AdvanceCard.tsx +13 -5
- package/src/components/AdvanceList.tsx +16 -8
- package/src/components/Battery.tsx +88 -0
- package/src/components/BatteryPercentageView.tsx +47 -42
- package/src/components/ColorAdjustView.tsx +5 -4
- package/src/components/ColorTempAdjustView.tsx +4 -3
- package/src/components/ColorsLine.tsx +2 -0
- package/src/components/DiySceneItem.tsx +0 -1
- package/src/components/DrawToolView.tsx +7 -4
- package/src/components/Marquee.tsx +99 -0
- package/src/components/MoodColorsLine.tsx +4 -2
- package/src/components/ldvColorSlider.tsx +50 -46
- package/src/composeLayout.tsx +7 -3
- package/src/i18n/strings.ts +315 -75
- package/src/models/TuyaApi.ts +60 -43
- package/src/models/modules/NativePropsSlice.tsx +14 -3
- package/translateKey.txt +9 -1
package/localazy.json
CHANGED
|
@@ -1291,7 +1291,15 @@
|
|
|
1291
1291
|
"MATCH:infobutton_randomtimecycle",
|
|
1292
1292
|
"MATCH:infobutton_timer",
|
|
1293
1293
|
"MATCH:infobutton_poweronbehavior",
|
|
1294
|
-
"MATCH:infobutton_history"
|
|
1294
|
+
"MATCH:infobutton_history",
|
|
1295
|
+
"MATCH:country_JO",
|
|
1296
|
+
"MATCH:country_CV",
|
|
1297
|
+
"MATCH:country_PA",
|
|
1298
|
+
"MATCH:country_northern_ireland",
|
|
1299
|
+
"MATCH:country_wales",
|
|
1300
|
+
"MATCH:country_BA",
|
|
1301
|
+
"MATCH:country_north_macedonia",
|
|
1302
|
+
"MATCH:country_werder_bremen"
|
|
1295
1303
|
],
|
|
1296
1304
|
"replacements": {
|
|
1297
1305
|
"REGEX:% %1\\$s.*?\\)%": "{0}",
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import { Utils } from 'tuya-panel-kit'
|
|
|
4
4
|
import Card from './Card'
|
|
5
5
|
import Spacer from './Spacer'
|
|
6
6
|
import ThemeType from '../config/themeType'
|
|
7
|
+
import Marquee from './Marquee'
|
|
7
8
|
|
|
8
9
|
const { convertX: cx } = Utils.RatioUtils
|
|
9
10
|
const { withTheme } = Utils.ThemeUtils
|
|
@@ -47,6 +48,8 @@ export interface AdvanceCardProps extends PropsWithChildren<ViewProps> {
|
|
|
47
48
|
|
|
48
49
|
const AdvanceCard = (props: AdvanceCardProps) => {
|
|
49
50
|
const {statusColor} = props.data;
|
|
51
|
+
const subtitles = props.data.subtitles
|
|
52
|
+
const subtitleStyle = {...styles.subtitle, color: props.theme?.global.secondFontColor};
|
|
50
53
|
return (
|
|
51
54
|
<Card
|
|
52
55
|
style={styles.itemContainer}
|
|
@@ -68,9 +71,15 @@ const AdvanceCard = (props: AdvanceCardProps) => {
|
|
|
68
71
|
<Text style={{...styles.title, color: props.theme?.global.fontColor}}>{props.data.title}</Text>
|
|
69
72
|
<Spacer height={cx(8)} />
|
|
70
73
|
{
|
|
71
|
-
(!!
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
(!!subtitles && subtitles.length > 0) && (
|
|
75
|
+
subtitles.length === 1 ? (
|
|
76
|
+
// 如果只有一个subtitle,静态显示
|
|
77
|
+
<Text style={subtitleStyle}>{subtitles[0]}</Text>
|
|
78
|
+
) : (
|
|
79
|
+
// 如果有多个subtitle,使用Marquee组件
|
|
80
|
+
<Marquee subtitles={subtitles} textStyle={subtitleStyle} />
|
|
81
|
+
)
|
|
82
|
+
)
|
|
74
83
|
}
|
|
75
84
|
</View>
|
|
76
85
|
}
|
|
@@ -85,7 +94,7 @@ const styles = StyleSheet.create({
|
|
|
85
94
|
},
|
|
86
95
|
itemContent: {},
|
|
87
96
|
titleBg: {
|
|
88
|
-
height: cx(
|
|
97
|
+
height: cx(80),
|
|
89
98
|
justifyContent: 'center',
|
|
90
99
|
alignItems: 'center',
|
|
91
100
|
},
|
|
@@ -98,7 +107,6 @@ const styles = StyleSheet.create({
|
|
|
98
107
|
subtitle: {
|
|
99
108
|
fontSize: cx(10),
|
|
100
109
|
textAlign: 'center',
|
|
101
|
-
fontFamily: 'helvetica_neue_lt_std_roman',
|
|
102
110
|
},
|
|
103
111
|
dotBg: {
|
|
104
112
|
position: 'absolute',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { useIsPad } from '../models/modules/NativePropsSlice'
|
|
1
2
|
import { useNavigation } from '@react-navigation/core'
|
|
2
|
-
import React, { useCallback } from 'react'
|
|
3
|
+
import React, { useCallback, useState } from 'react'
|
|
3
4
|
import { FlatList, Image, StyleSheet, View } from 'react-native'
|
|
4
5
|
import { Utils } from 'tuya-panel-kit'
|
|
5
6
|
import { getMicrophoneAccess } from '../api/native'
|
|
@@ -16,6 +17,8 @@ export interface AdvanceListProps {
|
|
|
16
17
|
|
|
17
18
|
function AdvanceList(props: AdvanceListProps) {
|
|
18
19
|
const navigation = useNavigation()
|
|
20
|
+
const isPad = useIsPad()
|
|
21
|
+
const [columns, setColumns] = useState(isPad ? 3 : 2)
|
|
19
22
|
|
|
20
23
|
const { advanceData = [] } = props
|
|
21
24
|
|
|
@@ -72,27 +75,32 @@ function AdvanceList(props: AdvanceListProps) {
|
|
|
72
75
|
container: {
|
|
73
76
|
marginHorizontal: cx(14),
|
|
74
77
|
},
|
|
78
|
+
columnContainer: {
|
|
79
|
+
justifyContent: 'space-between'
|
|
80
|
+
},
|
|
75
81
|
item: {
|
|
76
|
-
flex: 0
|
|
77
|
-
|
|
78
|
-
alignItems: '
|
|
82
|
+
flex: 0,
|
|
83
|
+
marginHorizontal: cx(10),
|
|
84
|
+
alignItems: 'flex-start',
|
|
79
85
|
},
|
|
80
86
|
iconContainer: {
|
|
81
|
-
height: cx(
|
|
87
|
+
height: cx(80),
|
|
82
88
|
justifyContent: 'center' as const,
|
|
83
89
|
alignItems: 'center' as const,
|
|
84
90
|
},
|
|
85
91
|
iconImage: {
|
|
86
|
-
width: cx(
|
|
87
|
-
height: cx(
|
|
92
|
+
width: cx(50),
|
|
93
|
+
height: cx(50),
|
|
88
94
|
}
|
|
89
95
|
})
|
|
90
96
|
|
|
91
97
|
return (
|
|
92
98
|
<FlatList
|
|
99
|
+
key={`flatlist-columns-${columns}`}
|
|
93
100
|
scrollEnabled={false}
|
|
94
|
-
numColumns={
|
|
101
|
+
numColumns={columns}
|
|
95
102
|
style={styles.container}
|
|
103
|
+
columnWrapperStyle={styles.columnContainer}
|
|
96
104
|
data={advanceData}
|
|
97
105
|
renderItem={renderItem}
|
|
98
106
|
keyExtractor={keyExtractor}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { View, ColorPropType, ViewStyle, StyleProp } from 'react-native';
|
|
3
|
+
import Svg, { Path, Polygon } from 'react-native-svg';
|
|
4
|
+
import { Utils } from 'tuya-panel-kit';
|
|
5
|
+
|
|
6
|
+
const { convertX: cx } = Utils.RatioUtils;
|
|
7
|
+
|
|
8
|
+
// 电池外壳SVG路径 (常量)
|
|
9
|
+
const WRAP_BATTERY_D =
|
|
10
|
+
'M6.5,0 C6.77614237,-5.07265313e-17 7,0.223857625 7,0.5 L7,1 L9.5,1 C10.3284271,1 11,1.67157288 11,2.5 L11,17.5 C11,18.3284271 10.3284271,19 9.5,19 L1.5,19 C0.671572875,19 0,18.3284271 0,17.5 L0,2.5 C0,1.67157288 0.671572875,1 1.5,1 L4,1 L4,0.5 C4,0.223857625 4.22385763,5.07265313e-17 4.5,0 L6.5,0 Z M9.5,2 L1.5,2 C1.22385763,2 1,2.22385763 1,2.5 L1,17.5 C1,17.7761424 1.22385763,18 1.5,18 L9.5,18 C9.77614237,18 10,17.7761424 10,17.5 L10,2.5 C10,2.2385763 9.77614237,2 9.5,2 Z';
|
|
11
|
+
|
|
12
|
+
// "粗壮"闪电图标
|
|
13
|
+
const LIGHTNING_D = 'M6,5 L3.5,10 L5,10 L5,14 L7.5,9 L6,9 L6,5 Z';
|
|
14
|
+
|
|
15
|
+
// 定义组件的Props类型
|
|
16
|
+
interface BatteryProps {
|
|
17
|
+
size?: number;
|
|
18
|
+
batteryColor?: ColorPropType;
|
|
19
|
+
value?: number;
|
|
20
|
+
highColor?: ColorPropType;
|
|
21
|
+
middleColor?: ColorPropType;
|
|
22
|
+
lowColor?: ColorPropType;
|
|
23
|
+
onCalcColor?: (top: number, high: string, middle: string, low: string) => string;
|
|
24
|
+
charging?: boolean;
|
|
25
|
+
chargingColor?: ColorPropType;
|
|
26
|
+
style?: StyleProp<ViewStyle>;
|
|
27
|
+
batteryRotation?: number; // *** 新增 prop,用于控制电池旋转角度 ***
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const Battery: React.FC<BatteryProps> = ({
|
|
31
|
+
size = cx(10),
|
|
32
|
+
batteryColor = 'rgba(0,0,0,.5)',
|
|
33
|
+
value = 80,
|
|
34
|
+
highColor = '#70CF98',
|
|
35
|
+
middleColor = '#F5A623',
|
|
36
|
+
lowColor = '#FF4444',
|
|
37
|
+
onCalcColor,
|
|
38
|
+
charging = false,
|
|
39
|
+
chargingColor = '#3AC400',
|
|
40
|
+
style,
|
|
41
|
+
batteryRotation = 0, // 默认为0,不旋转
|
|
42
|
+
}) => {
|
|
43
|
+
|
|
44
|
+
const top = useMemo(() => {
|
|
45
|
+
const boundedValue = Math.max(0, Math.min(100, value));
|
|
46
|
+
return 17 - (14 * boundedValue) / 100;
|
|
47
|
+
}, [value]);
|
|
48
|
+
|
|
49
|
+
const insideColor = useMemo(() => {
|
|
50
|
+
if (typeof onCalcColor === 'function') {
|
|
51
|
+
const customColor = onCalcColor(top, highColor, middleColor, lowColor);
|
|
52
|
+
if (customColor) return customColor;
|
|
53
|
+
}
|
|
54
|
+
if (value >= 50) return highColor;
|
|
55
|
+
if (value >= 20) return middleColor;
|
|
56
|
+
return lowColor;
|
|
57
|
+
}, [value, top, highColor, middleColor, lowColor, onCalcColor]);
|
|
58
|
+
|
|
59
|
+
const batteryLevelPoints = `2 ${top} 9 ${top} 9 17 2 17`;
|
|
60
|
+
|
|
61
|
+
const svgWidth = 1.1 * size;
|
|
62
|
+
const svgHeight = 1.9 * size;
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
// 1. 将旋转应用到根View上
|
|
66
|
+
<View style={[{ transform: [{ rotate: `${batteryRotation}deg` }] }, style]}>
|
|
67
|
+
<Svg width={svgWidth} height={svgHeight} viewBox="0 0 11 19">
|
|
68
|
+
<Path d={WRAP_BATTERY_D} fill={batteryColor} />
|
|
69
|
+
<Polygon points={batteryLevelPoints} fill={insideColor as string} />
|
|
70
|
+
{charging && (
|
|
71
|
+
<Path
|
|
72
|
+
d={LIGHTNING_D}
|
|
73
|
+
fill={chargingColor as string}
|
|
74
|
+
stroke="white"
|
|
75
|
+
strokeWidth={0.2}
|
|
76
|
+
strokeLinejoin="round"
|
|
77
|
+
// 2. 对闪电图标进行“反向旋转”
|
|
78
|
+
rotation={-batteryRotation}
|
|
79
|
+
// 3. 指定旋转中心为SVG画布的中心
|
|
80
|
+
origin="5.5, 9.5"
|
|
81
|
+
/>
|
|
82
|
+
)}
|
|
83
|
+
</Svg>
|
|
84
|
+
</View>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export default Battery;
|
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
1
|
+
import React, { useCallback, useMemo } from 'react'
|
|
2
|
+
import { StyleSheet, Text } from 'react-native'
|
|
3
|
+
import { Utils } from 'tuya-panel-kit'
|
|
4
|
+
import ThemeType from '../config/themeType'
|
|
5
5
|
import I18n from '../i18n'
|
|
6
|
+
import Battery from './Battery'
|
|
7
|
+
import Card from './Card'
|
|
6
8
|
import Spacer from './Spacer'
|
|
7
|
-
import ThemeType from '../config/themeType'
|
|
8
9
|
|
|
9
10
|
const cx = Utils.RatioUtils.convertX
|
|
10
|
-
const {withTheme} = Utils.ThemeUtils
|
|
11
|
+
const { withTheme } = Utils.ThemeUtils
|
|
11
12
|
type BatteryProps = {
|
|
12
13
|
value: number
|
|
13
14
|
lowValue?: number
|
|
14
15
|
middleValue?: number
|
|
15
16
|
highValue?: number
|
|
17
|
+
charging?: boolean
|
|
16
18
|
theme?: ThemeType
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
const BatteryPercentageView = (props: BatteryProps) => {
|
|
20
|
-
const {value, middleValue = 10, highValue = 90} = props
|
|
22
|
+
const { value, middleValue = 10, highValue = 90 } = props
|
|
21
23
|
const calcColor = (_: any, normalColor: string, lowColor: string, emptyColor: string) => {
|
|
22
24
|
if (value >= highValue) {
|
|
23
25
|
return normalColor
|
|
@@ -28,50 +30,53 @@ const BatteryPercentageView = (props: BatteryProps) => {
|
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
33
|
+
const getStyles = useCallback(() => {
|
|
34
|
+
return StyleSheet.create({
|
|
35
|
+
container: {
|
|
36
|
+
flexDirection: 'row',
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
},
|
|
39
|
+
title: {
|
|
40
|
+
marginStart: cx(16),
|
|
41
|
+
marginVertical: cx(18),
|
|
42
|
+
color: props.theme?.global.fontColor,
|
|
43
|
+
fontSize: cx(16),
|
|
44
|
+
fontWeight: 'bold',
|
|
45
|
+
},
|
|
46
|
+
content: {
|
|
47
|
+
marginEnd: cx(16),
|
|
48
|
+
width: cx(60),
|
|
49
|
+
color: props.theme?.global.fontColor,
|
|
50
|
+
textAlign: 'right',
|
|
51
|
+
fontSize: cx(14),
|
|
52
|
+
},
|
|
53
|
+
low: {
|
|
54
|
+
color: props.theme?.global.error
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
}, [props.theme])
|
|
58
|
+
|
|
59
|
+
const styles = getStyles()
|
|
55
60
|
|
|
56
61
|
return (
|
|
57
62
|
<Card
|
|
58
63
|
style={{ marginHorizontal: cx(24) }}
|
|
59
|
-
containerStyle={
|
|
64
|
+
containerStyle={styles.container}
|
|
60
65
|
>
|
|
61
66
|
<Text
|
|
62
67
|
style={styles.title}>
|
|
63
68
|
{I18n.getLang('motion_detector_battery__state4')}
|
|
64
69
|
</Text>
|
|
65
|
-
<Spacer height={0} style={{ flex: 1 }}
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
<Spacer height={0} style={{ flex: 1 }}/>
|
|
71
|
+
<Battery
|
|
72
|
+
value={props.value}
|
|
73
|
+
onCalcColor={calcColor}
|
|
74
|
+
charging={props.charging}
|
|
75
|
+
size={cx(30)}
|
|
76
|
+
batteryColor={props.theme?.global.secondFontColor}
|
|
77
|
+
middleColor="#999999"
|
|
78
|
+
batteryRotation={90}
|
|
79
|
+
/>
|
|
75
80
|
<Text style={[styles.content, value <= middleValue ? styles.low : null]}>{value}%</Text>
|
|
76
81
|
</Card>
|
|
77
82
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {View} from 'react-native'
|
|
2
2
|
import React, {useCallback} from 'react'
|
|
3
3
|
import LdvColorSlider from './ldvColorSlider'
|
|
4
|
-
import {hex2Hsv, hsv2Hex} from '
|
|
4
|
+
import {hex2Hsv, hsv2Hex} from '@utils'
|
|
5
5
|
import LdvPresetView from './ldvPresetView'
|
|
6
6
|
import LdvSaturation from './ldvSaturation'
|
|
7
7
|
import LdvColorBrightness from './ldvColorBrightness'
|
|
@@ -9,12 +9,11 @@ import I18n from '../i18n/index'
|
|
|
9
9
|
import RectColorAndBrightPicker from './rect-color-and-bright-picker'
|
|
10
10
|
import {Utils} from "tuya-panel-kit";
|
|
11
11
|
import {useReactive, useUpdateEffect} from "ahooks";
|
|
12
|
-
import {useNewPalette} from "../models/modules/NativePropsSlice";
|
|
12
|
+
import {useNewPalette, useIsPad} from "../models/modules/NativePropsSlice";
|
|
13
13
|
|
|
14
|
-
const cx = Utils.RatioUtils
|
|
14
|
+
const {convertX: cx, width: screenWidth} = Utils.RatioUtils
|
|
15
15
|
const scaleUp = (value) => value * 10
|
|
16
16
|
const scaleDown = (value) => Math.round(value / 10)
|
|
17
|
-
const width = Utils.RatioUtils.width - cx(80)
|
|
18
17
|
|
|
19
18
|
export interface ColorAdjustViewProps {
|
|
20
19
|
h: number
|
|
@@ -30,6 +29,8 @@ export interface ColorAdjustViewProps {
|
|
|
30
29
|
|
|
31
30
|
const NewColorPicker = React.memo((props: ColorAdjustViewProps) => {
|
|
32
31
|
const { h = 0, s = 100, v = 100, minBrightness, onHSVChange, onHSVChangeComplete } = props
|
|
32
|
+
const isPad = useIsPad()
|
|
33
|
+
const width = isPad ? cx(screenWidth - 10) : (screenWidth - 80)
|
|
33
34
|
const state = useReactive({
|
|
34
35
|
hue: h,
|
|
35
36
|
saturation: scaleUp(s),
|
|
@@ -9,13 +9,12 @@ import I18n from '../i18n/index'
|
|
|
9
9
|
import {cctToColor} from '../utils/cctUtils'
|
|
10
10
|
import RectColorAndBrightPicker from "./rect-color-and-bright-picker";
|
|
11
11
|
import {useReactive, useUpdateEffect} from 'ahooks'
|
|
12
|
-
import { useNewPalette } from 'models/modules/NativePropsSlice'
|
|
12
|
+
import { useNewPalette, useIsPad } from 'models/modules/NativePropsSlice'
|
|
13
13
|
|
|
14
|
-
const {convertX: cx} = Utils.RatioUtils
|
|
14
|
+
const {convertX: cx, width: screenWidth} = Utils.RatioUtils
|
|
15
15
|
|
|
16
16
|
const scaleUp = (value: number) => value * 10;
|
|
17
17
|
const scaleDown = (value: number) => Math.round(value / 10);
|
|
18
|
-
const width = Utils.RatioUtils.width - cx(80);
|
|
19
18
|
|
|
20
19
|
export interface ColorTempAdjustViewProps {
|
|
21
20
|
colorTemp: number
|
|
@@ -41,6 +40,8 @@ const NewColorTempPicker = React.memo((props: ColorTempAdjustViewProps) => {
|
|
|
41
40
|
onBrightnessChange,
|
|
42
41
|
onBrightnessChangeComplete,
|
|
43
42
|
} = props
|
|
43
|
+
const isPad = useIsPad()
|
|
44
|
+
const width = isPad ? cx(screenWidth - 10) : (screenWidth - 80)
|
|
44
45
|
const state = useReactive({
|
|
45
46
|
temperature: scaleUp(colorTemp),
|
|
46
47
|
brightness: scaleUp(brightness),
|
|
@@ -14,6 +14,7 @@ import Stepper from './Stepper'
|
|
|
14
14
|
import StripLightView from './StripLightView'
|
|
15
15
|
import { nativeEventEmitter } from '../api/nativeEventEmitter'
|
|
16
16
|
import ThemeType from '../config/themeType'
|
|
17
|
+
import { useIsPad } from '../models/modules/NativePropsSlice'
|
|
17
18
|
|
|
18
19
|
const { convertX: cx } = Utils.RatioUtils
|
|
19
20
|
const { withTheme } = Utils.ThemeUtils
|
|
@@ -66,6 +67,7 @@ interface DrawToolViewProps extends PropsWithChildren<ViewProps> {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
const DrawToolView = (props: DrawToolViewProps) => {
|
|
70
|
+
const isPad = useIsPad()
|
|
69
71
|
const state = useReactive({
|
|
70
72
|
visible: props.ledNumModalVisible || false,
|
|
71
73
|
ledNum: props.ledNum || 5,
|
|
@@ -98,8 +100,9 @@ const DrawToolView = (props: DrawToolViewProps) => {
|
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
const height = useMemo(() => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
const base = isPad ? 100 : 52
|
|
104
|
+
return cx(base * Math.ceil((props.nodes.length / (props.fixCount || 5))))
|
|
105
|
+
}, [props.nodes, isPad])
|
|
103
106
|
|
|
104
107
|
const getBlockColor = useCallback(() => {
|
|
105
108
|
if (props.isColorMode) {
|
|
@@ -161,8 +164,8 @@ const DrawToolView = (props: DrawToolViewProps) => {
|
|
|
161
164
|
</TouchableOpacity>
|
|
162
165
|
{!props.hideDisableLight && <TouchableOpacity
|
|
163
166
|
accessibilityLabel='AdjustButton'
|
|
164
|
-
|
|
165
|
-
|
|
167
|
+
accessibilityHint='3'
|
|
168
|
+
accessibilityState={{ checked: props.adjustType === 3 }}
|
|
166
169
|
onPress={() => {
|
|
167
170
|
props.setAdjustType(3)
|
|
168
171
|
}}>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { View, Text, Animated, StyleSheet, StyleProp, TextStyle } from 'react-native';
|
|
3
|
+
import { Utils } from 'tuya-panel-kit';
|
|
4
|
+
|
|
5
|
+
const { convertX: cx } = Utils.RatioUtils;
|
|
6
|
+
|
|
7
|
+
// 定义每个字幕项的固定高度
|
|
8
|
+
const SUBTITLE_HEIGHT = cx(14);
|
|
9
|
+
// 动画切换的持续时间
|
|
10
|
+
const ANIMATION_DURATION = 500;
|
|
11
|
+
// 每个字幕停留显示的时间
|
|
12
|
+
const PAUSE_DURATION = 2000;
|
|
13
|
+
|
|
14
|
+
interface MarqueeProps {
|
|
15
|
+
subtitles: string[];
|
|
16
|
+
textStyle: StyleProp<TextStyle>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const Marquee: React.FC<MarqueeProps> = ({ subtitles, textStyle }) => {
|
|
20
|
+
const scrollY = useRef(new Animated.Value(0)).current;
|
|
21
|
+
|
|
22
|
+
// 复制第一个元素到末尾以实现无缝循环
|
|
23
|
+
const displaySubtitles = [...subtitles, subtitles[0]];
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
let animation: Animated.CompositeAnimation | null = null;
|
|
27
|
+
|
|
28
|
+
const animate = (index: number) => {
|
|
29
|
+
const nextIndex = index + 1;
|
|
30
|
+
|
|
31
|
+
animation = Animated.sequence([
|
|
32
|
+
Animated.delay(PAUSE_DURATION),
|
|
33
|
+
Animated.timing(scrollY, {
|
|
34
|
+
toValue: -nextIndex * SUBTITLE_HEIGHT,
|
|
35
|
+
duration: ANIMATION_DURATION,
|
|
36
|
+
useNativeDriver: true,
|
|
37
|
+
}),
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
animation.start(({ finished }) => {
|
|
41
|
+
if (finished) {
|
|
42
|
+
if (nextIndex >= subtitles.length) {
|
|
43
|
+
// 当滚动到复制的最后一项后,无动画地重置到起点
|
|
44
|
+
scrollY.setValue(0);
|
|
45
|
+
// 从头开始循环
|
|
46
|
+
animate(0);
|
|
47
|
+
} else {
|
|
48
|
+
// 继续下一次滚动
|
|
49
|
+
animate(nextIndex);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// 仅当有多个字幕时才启动动画
|
|
56
|
+
if (subtitles.length > 1) {
|
|
57
|
+
animate(0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return () => {
|
|
61
|
+
if (animation) {
|
|
62
|
+
animation.stop();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}, [subtitles]);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<View style={styles.marqueeContainer}>
|
|
69
|
+
<Animated.View
|
|
70
|
+
style={{
|
|
71
|
+
transform: [{ translateY: scrollY }],
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
{displaySubtitles.map((subtitle, index) => (
|
|
75
|
+
<Text
|
|
76
|
+
key={`${subtitle}_${index}`}
|
|
77
|
+
style={[textStyle, styles.subtitleText]}
|
|
78
|
+
numberOfLines={1}
|
|
79
|
+
>
|
|
80
|
+
{subtitle}
|
|
81
|
+
</Text>
|
|
82
|
+
))}
|
|
83
|
+
</Animated.View>
|
|
84
|
+
</View>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const styles = StyleSheet.create({
|
|
89
|
+
marqueeContainer: {
|
|
90
|
+
height: SUBTITLE_HEIGHT, // 容器高度被限制为只能显示一个字幕
|
|
91
|
+
overflow: 'hidden', // 超出部分隐藏
|
|
92
|
+
},
|
|
93
|
+
subtitleText: {
|
|
94
|
+
height: SUBTITLE_HEIGHT, // 确保每个Text元素有相同的高度
|
|
95
|
+
lineHeight: SUBTITLE_HEIGHT, // **【关键】** 让lineHeight等于height,实现文本在行内垂直居中
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
export default Marquee;
|
|
@@ -3,8 +3,9 @@ import React from 'react'
|
|
|
3
3
|
import {Utils} from 'tuya-panel-kit'
|
|
4
4
|
import {StyleProp, StyleSheet, ViewStyle} from 'react-native'
|
|
5
5
|
import ColorsLine from './ColorsLine'
|
|
6
|
+
import {useIsPad} from '../models/modules/NativePropsSlice'
|
|
6
7
|
|
|
7
|
-
const cx = Utils.RatioUtils
|
|
8
|
+
const { convertX: cx, width: screenWidth } = Utils.RatioUtils
|
|
8
9
|
|
|
9
10
|
export type MoodColorsLineType = 'gradient' | 'separate'
|
|
10
11
|
|
|
@@ -17,7 +18,8 @@ interface MoodColorsLineProps {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export default function MoodColorsLine(props: MoodColorsLineProps) {
|
|
20
|
-
const
|
|
21
|
+
const isPad = useIsPad()
|
|
22
|
+
const width = props.width || (isPad ? cx(screenWidth - 10) : (screenWidth - 80))
|
|
21
23
|
const height = props.height || cx(24)
|
|
22
24
|
if (props.type === 'separate' || props.colors.length < 2) {
|
|
23
25
|
return (<ColorsLine colors={props.colors} style={{width, height}} nodeStyle={props.nodeStyle}/>)
|