@ledvance/group-ui-biz-bundle 1.0.30 → 1.0.32

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.
@@ -0,0 +1,123 @@
1
+ import React, { ReactElement, memo } from "react";
2
+ import { Text, Image, ViewStyle, View, StyleSheet } from 'react-native'
3
+ import res from "@ledvance/base/src/res"
4
+ import I18n from "@ledvance/base/src/i18n";
5
+ import { Utils } from "tuya-panel-kit";
6
+ import Spacer from "@ledvance/base/src/components/Spacer";
7
+
8
+ const { convertX: cx } = Utils.RatioUtils;
9
+
10
+ interface SummaryProps {
11
+ style?: ViewStyle
12
+ frequency?: string | ReactElement
13
+ time?: string | ReactElement
14
+ actions?: ReactElement
15
+ hideActions?: boolean
16
+ }
17
+
18
+ const Summary = (props: SummaryProps) => {
19
+ return (
20
+ <View style={styles.cardContainer}>
21
+ <Text style={styles.itemTitle}>{I18n.getLang('add_randomtimecycle_subheadline_text')}</Text>
22
+ <Spacer height={cx(10)} />
23
+ <View style={{}}>
24
+ <View style={styles.summaryContainer}>
25
+ <View style={styles.summaryLeft}>
26
+ <Image
27
+ source={res.summary_icon1}
28
+ resizeMode="contain"
29
+ style={styles.summaryImg}
30
+ />
31
+ <View>
32
+ <Text style={styles.leftTitle}>{I18n.getLang('feature_summary_frequency_headline')}</Text>
33
+ </View>
34
+ </View>
35
+ <View style={styles.summaryRight}>
36
+ <View style={styles.rightWrap}>
37
+ <Text style={styles.rightItem}>{props.frequency}</Text>
38
+ </View>
39
+ </View>
40
+ </View>
41
+ <View style={styles.summaryContainer}>
42
+ <View style={styles.summaryLeft}>
43
+ <Image
44
+ source={res.summary_icon2}
45
+ resizeMode="contain"
46
+ style={styles.summaryImg}
47
+ />
48
+ <View>
49
+ <Text style={styles.leftTitle}>{I18n.getLang('feature_summary_time_headline')}</Text>
50
+ </View>
51
+ </View>
52
+ <View style={styles.summaryRight}>
53
+ <View style={styles.rightWrap}>
54
+ <Text style={styles.rightItem}>{props.time}</Text>
55
+ </View>
56
+ </View>
57
+ </View>
58
+ {!props.hideActions && <View style={[styles.summaryContainer, { alignItems: 'flex-start' }]}>
59
+ <View style={styles.summaryLeft}>
60
+ <Image
61
+ source={res.summary_icon3}
62
+ resizeMode="contain"
63
+ style={styles.summaryImg}
64
+ />
65
+ <View>
66
+ <Text style={styles.leftTitle}>{I18n.getLang('motion_detection_add_time_schedule_actions_text1')}</Text>
67
+ </View>
68
+ </View>
69
+ <View style={styles.summaryRight}>
70
+ {props.actions}
71
+ </View>
72
+ </View>}
73
+ </View>
74
+ </View>
75
+ )
76
+ }
77
+
78
+ const styles = StyleSheet.create({
79
+ cardContainer: {
80
+ marginHorizontal: cx(24),
81
+ },
82
+ itemTitle: {
83
+ color: '#000',
84
+ fontSize: cx(16),
85
+ fontWeight: 'bold',
86
+ fontFamily: 'helvetica_neue_lt_std_bd',
87
+ },
88
+ summaryContainer: {
89
+ flex: 1,
90
+ flexDirection: 'row',
91
+ marginBottom: cx(10),
92
+ },
93
+ summaryLeft: {
94
+ flexDirection: 'row',
95
+ alignItems: 'center',
96
+ minWidth: cx(100)
97
+ },
98
+ summaryImg: {
99
+ width: cx(12),
100
+ height: cx(12),
101
+ marginRight: cx(6)
102
+ },
103
+ leftTitle: {
104
+ fontSize: cx(14),
105
+ color: '#000'
106
+ },
107
+ summaryRight: {
108
+ flex: 1,
109
+ flexDirection: 'column',
110
+ marginLeft: cx(15),
111
+ },
112
+ rightWrap: {
113
+ borderRadius: cx(16),
114
+ paddingHorizontal: cx(12),
115
+ alignSelf: 'flex-start',
116
+ backgroundColor: '#cbcbcb',
117
+ },
118
+ rightItem: {
119
+ color: '#000',
120
+ },
121
+ })
122
+
123
+ export default memo(Summary)
@@ -0,0 +1,76 @@
1
+ import React from 'react'
2
+ import Card from "@ledvance/base/src/components/Card";
3
+ import Spacer from "@ledvance/base/src/components/Spacer";
4
+ import {StyleSheet, Text, View} from "react-native";
5
+ import {SwitchButton, Utils} from "tuya-panel-kit";
6
+ import {loopText} from "@ledvance/base/src/utils/common";
7
+ import {parseHour12} from "@tuya/tuya-panel-lamp-sdk/lib/utils";
8
+
9
+ const {convertX: cx} = Utils.RatioUtils
10
+ const {parseTimer} = Utils.TimeUtils
11
+
12
+ export interface ItemCardProps<T> {
13
+ item: T & {
14
+ power: boolean
15
+ startTime: number
16
+ endTime: number
17
+ weeks: number[]
18
+ name?: string
19
+ },
20
+ is24Hour?: boolean,
21
+ onSwitch: (enable: boolean) => void
22
+ onPress: () => void
23
+ }
24
+
25
+ const ItemCard = <T, >(props: ItemCardProps<T>) => {
26
+ const {item, is24Hour, onSwitch, onPress} = props
27
+ return (
28
+ <Card style={styles.itemCard} onPress={onPress}>
29
+ <Spacer height={cx(16)}/>
30
+ <View style={styles.switchLine}>
31
+ <Text style={styles.time}>
32
+ {is24Hour ?
33
+ `${parseTimer(item.startTime * 60)} - ${parseTimer(item.endTime * 60)}`
34
+ :
35
+ `${parseHour12(item.startTime * 60)} - ${parseHour12(item.endTime * 60)}`
36
+ }
37
+ </Text>
38
+ <SwitchButton
39
+ style={styles.switchBtn}
40
+ value={item.power}
41
+ thumbStyle={{elevation: 0}}
42
+ onValueChange={onSwitch}/>
43
+ </View>
44
+ <Text style={styles.loopText}>{loopText(item.weeks, parseTimer(item.startTime * 60))}</Text>
45
+ <Spacer height={cx(5)}/>
46
+ <Text style={styles.loopText}>{item.name}</Text>
47
+ <Spacer/>
48
+ </Card>
49
+ )
50
+ }
51
+
52
+ const styles = StyleSheet.create({
53
+ itemCard: {
54
+ marginHorizontal: cx(24),
55
+ paddingHorizontal: cx(16),
56
+ },
57
+ switchLine: {
58
+ flexDirection: 'row',
59
+ alignItems: 'center',
60
+ },
61
+ time: {
62
+ flex: 1,
63
+ color: '#000',
64
+ fontSize: cx(16),
65
+ fontWeight: 'bold',
66
+ fontFamily: 'helvetica_neue_lt_std_bd',
67
+ },
68
+ switchBtn: {},
69
+ loopText: {
70
+ color: '#000',
71
+ fontSize: cx(14),
72
+ fontFamily: 'helvetica_neue_lt_std_roman',
73
+ },
74
+ })
75
+
76
+ export default ItemCard
@@ -0,0 +1,43 @@
1
+ import {useFeatureHook} from "@ledvance/base/src/models/modules/NativePropsSlice";
2
+ import {Formatter} from "@tuya/tuya-panel-lamp-sdk";
3
+ const RandomTimerFormatter = new Formatter.RandomTimerFormatter()
4
+
5
+ export interface RandomTimingParam {
6
+ channels: string[]
7
+ is24Hour?: boolean
8
+ isSupportColor: boolean
9
+ isSupportTemperature: boolean
10
+ isSupportBrightness: boolean
11
+ }
12
+
13
+ export interface RandomTimingItem {
14
+ power: boolean
15
+ channel?: number
16
+ weeks: number[]
17
+ startTime: number
18
+ endTime: number
19
+ color: {
20
+ hue: number
21
+ saturation: number
22
+ value: number
23
+ brightness: number
24
+ temperature: number
25
+ }
26
+ index?: number
27
+ name?: string
28
+ }
29
+
30
+ interface LightConfig {
31
+ randomTiming: RandomTimingItem[]
32
+ }
33
+
34
+ export function useRandomTiming() {
35
+ return useFeatureHook<LightConfig, RandomTimingItem[]>('randomTiming', [], (val) => {
36
+ const data = {
37
+ version: 0,
38
+ length: 12,
39
+ nodes: val
40
+ }
41
+ return RandomTimerFormatter.format(data)
42
+ })
43
+ }
@@ -0,0 +1,342 @@
1
+ import React, {useEffect, useMemo} from 'react'
2
+ import Page from "@ledvance/base/src/components/Page";
3
+ import I18n from "@ledvance/base/src/i18n";
4
+ import {Image, ScrollView, StyleSheet, Text, TouchableOpacity, View} from "react-native";
5
+ import {TimerPicker, Utils} from "tuya-panel-kit";
6
+ import {RandomTimingItem, RandomTimingParam} from "./RandomTimingForLightAction";
7
+ import res from "@ledvance/base/src/res/index";
8
+ import {useReactive} from "ahooks";
9
+ import {useNavigation, useRoute} from '@react-navigation/core';
10
+ import LdvTopName from "@ledvance/base/src/components/ldvTopName";
11
+ import TextField from "@ledvance/base/src/components/TextField";
12
+ import LdvWeekView from "@ledvance/base/src/components/weekSelect";
13
+ import Spacer from "@ledvance/base/src/components/Spacer";
14
+ import {loopText, showDialog} from "@ledvance/base/src/utils/common";
15
+ import DeleteButton from "@ledvance/base/src/components/DeleteButton";
16
+ import dayjs from "dayjs";
17
+ import {cloneDeep, isEqual} from "lodash";
18
+ import Summary from "./Summary";
19
+ import Card from "@ledvance/base/src/components/Card";
20
+ import LdvSwitch from "@ledvance/base/src/components/ldvSwitch";
21
+ import LampAdjustView from "@ledvance/base/src/components/LampAdjustView";
22
+ import {parseHour12} from "@tuya/tuya-panel-lamp-sdk/lib/utils";
23
+
24
+ const {parseTimer} = Utils.TimeUtils
25
+ const {convertX: cx} = Utils.RatioUtils;
26
+
27
+ interface RandomTimeDetailParam extends RandomTimingParam {
28
+ mode: 'add' | 'edit',
29
+ item: RandomTimingItem,
30
+ onPost: (mode: 'add' | 'edit' | 'del', item: RandomTimingItem, goBack?: boolean) => Promise<void>
31
+ }
32
+
33
+ const newRandomTimingItem = (): RandomTimingItem => {
34
+ const startTime = dayjs().hour() * 60 + dayjs().minute()
35
+ return {
36
+ power: true,
37
+ channel: 0,
38
+ weeks: [0, 0, 0, 0, 0, 0, 0],
39
+ startTime: startTime,
40
+ endTime: startTime + 60,
41
+ color: {
42
+ hue: 0,
43
+ saturation: 100,
44
+ value: 100,
45
+ brightness: 100,
46
+ temperature: 0,
47
+ }
48
+ }
49
+ }
50
+
51
+ const RandomTimingForLightDetailPage = () => {
52
+ const navigation = useNavigation()
53
+ const params = useRoute().params as RandomTimeDetailParam
54
+ const state = useReactive({
55
+ item: params.mode === 'add' ? newRandomTimingItem() : cloneDeep(params.item),
56
+ loading: false,
57
+ isColorMode: false
58
+ })
59
+
60
+ useEffect(() => {
61
+ const { brightness, temperature, hue, saturation, value } = state.item.color
62
+ state.isColorMode = brightness === 0 && temperature === 0 && (hue !== 0 || saturation !== 0 || value !== 0)
63
+ }, [])
64
+
65
+ const canSave = useMemo(() => {
66
+ return state.item.name && state.item.name.length < 33 && state.item.channel !== undefined
67
+ }, [state.item.name, state.item.channel])
68
+
69
+ const showConfirm = useMemo(() => {
70
+ return params.mode === 'edit' && !isEqual(state.item, params.item)
71
+ }, [JSON.stringify(state.item), JSON.stringify(params.item)])
72
+
73
+ const onSave = async () => {
74
+ if (!canSave) {
75
+ return
76
+ }
77
+ const timeInterval = state.item.endTime - state.item.startTime;
78
+ if (timeInterval >= 0 && timeInterval < 30) {
79
+ showDialog({
80
+ method: 'alert',
81
+ title: I18n.getLang('conflict_dialog_save_item_randomtimecycle_tips'),
82
+ subTitle: I18n.getLang('conflict_dialog_save_item_randomtimecycle_interval_description'),
83
+ onConfirm: async (_, { close }) => {
84
+ close()
85
+ }
86
+ })
87
+ return
88
+ }
89
+ await params.onPost(params.mode, {
90
+ ...state.item,
91
+ power: true,
92
+ color: {
93
+ hue: state.isColorMode ? state.item.color.hue : 0,
94
+ saturation: state.isColorMode ? state.item.color.saturation : 0,
95
+ value: state.isColorMode ? state.item.color.value : 0,
96
+ temperature: state.isColorMode ? 0 : state.item.color.temperature,
97
+ brightness: state.isColorMode ? 0 : state.item.color.brightness
98
+ }
99
+ }, true);
100
+ }
101
+
102
+ const onDelete = () => {
103
+ return showDialog({
104
+ method: 'confirm',
105
+ title: I18n.getLang('cancel_dialog_delete_item_randomtimecycle_titel'),
106
+ subTitle: I18n.getLang('cancel_dialog_delete_item_randomtimecycle_description'),
107
+ onConfirm: async (_, {close}) => {
108
+ close()
109
+ await params.onPost('del', state.item, true)
110
+ }
111
+ })
112
+ }
113
+ return (
114
+ <Page
115
+ backText={I18n.getLang('randomtimecycle_sockets_headline_text')}
116
+ onBackClick={!showConfirm ? navigation.goBack : undefined}
117
+ rightButtonIcon={canSave ? res.ic_check : res.ic_uncheck}
118
+ rightButtonDisabled={state.loading}
119
+ loading={state.loading}
120
+ showBackDialog={showConfirm}
121
+ backDialogTitle={I18n.getLang('cancel_dialog_leave_unsaved_titel')}
122
+ backDialogContent={I18n.getLang('cancel_dialog_delete_item_randomtimecycle_description')}
123
+ rightButtonIconClick={onSave}
124
+ >
125
+ <ScrollView nestedScrollEnabled={true}>
126
+ <LdvTopName
127
+ title={I18n.getLang(params.mode === 'add' ? 'add_fixedtimecycle_headline_text' : 'edit_fixedtimecycle_headline_text')}/>
128
+ <TextField
129
+ style={styles.cardContainer}
130
+ value={state.item.name}
131
+ showError={(state.item.name?.length || 0) > 32}
132
+ maxLength={33}
133
+ errorText={I18n.getLang('add_new_dynamic_mood_alert_text')}
134
+ placeholder={I18n.getLang('add_new_trigger_time_inputfield_value_text')}
135
+ onChangeText={(t: string) => {
136
+ state.item.name = t;
137
+ }}
138
+ />
139
+ {/* pick */}
140
+ <TimerPicker
141
+ itemTextColor='#aeadb5'
142
+ style={{paddingVertical: cx(0), marginVertical: cx(0)}}
143
+ is12Hours={!params.is24Hour}
144
+ startTime={state.item.startTime}
145
+ endTime={state.item.endTime}
146
+ onTimerChange={(startTime, endTime) => {
147
+ state.item.startTime = startTime
148
+ state.item.endTime = endTime
149
+ }}/>
150
+ <LdvWeekView
151
+ value={state.item.weeks}
152
+ style={styles.cardContainer}
153
+ onSelect={(index: number) => {
154
+ const rawIndex = index - 1
155
+ state.item.weeks[rawIndex] = state.item.weeks[rawIndex] === 1 ? 0 : 1
156
+ }}/>
157
+ <Spacer/>
158
+ <Text style={styles.cardContainer}>{loopText(state.item.weeks, parseTimer(state.item.startTime * 60))}</Text>
159
+ <Spacer/>
160
+ {/*Apply for */}
161
+ <View style={styles.cardContainer}>
162
+ <Text style={styles.itemTitle}>{I18n.getLang('timeschedule_add_schedule_subheadline_text')}</Text>
163
+ <Spacer height={cx(10)}/>
164
+ <View style={styles.applyContent}>
165
+ {state.item.channel === undefined ?
166
+ <Text>{I18n.getLang('timer_ceiling_fan_selectionfield_no_components_text')}</Text> :
167
+ <View style={[styles.applyItem, {marginBottom: cx(10), borderRadius: 4}]}>
168
+ <Text style={{color: '#000'}}>{params.channels[state.item.channel]}</Text>
169
+ {params.channels.length > 1 && <TouchableOpacity
170
+ onPress={() => {
171
+ state.item.channel = undefined
172
+ }}
173
+ style={{paddingHorizontal: cx(5)}}>
174
+ <Image style={{width: cx(16), height: cx(16)}} source={res.ic_arrows_nav_clear}/>
175
+ </TouchableOpacity>}
176
+ </View>
177
+ }
178
+ </View>
179
+ {params.channels.map((item: string, index: number) => {
180
+ if (state.item.channel === index) {
181
+ return null
182
+ }
183
+ return (
184
+ <TouchableOpacity
185
+ style={styles.applyItem}
186
+ key={item}
187
+ onPress={() => {
188
+ state.item.channel = index
189
+ }}
190
+ >
191
+ <Text style={{color: '#000'}}>{item}</Text>
192
+ <Image style={{width: cx(16), height: cx(16)}} source={res.device_panel_timer_add}/>
193
+ </TouchableOpacity>
194
+ )
195
+ })}
196
+ </View>
197
+ <Spacer/>
198
+ {/* Devices */}
199
+ <View style={styles.cardContainer}>
200
+ <Text style={styles.itemTitle}>{I18n.getLang('timeschedule_add_schedule_subheadline2_text')}</Text>
201
+ <Spacer height={cx(10)} />
202
+ <Card>
203
+ <LdvSwitch
204
+ title={I18n.getLang('light_sources_tile_tw_lighting_headline')}
205
+ color={'#fff'}
206
+ colorAlpha={1}
207
+ enable={true}
208
+ setEnable={() => { }}
209
+ showSwitch={false}
210
+ />
211
+ <LampAdjustView
212
+ isSupportColor={params.isSupportColor}
213
+ isSupportBrightness={params.isSupportBrightness}
214
+ isSupportTemperature={params.isSupportTemperature}
215
+ isColorMode={state.isColorMode}
216
+ reserveSV={true}
217
+ setIsColorMode={(v) => state.isColorMode = v}
218
+ h={state.item.color.hue}
219
+ s={state.item.color.saturation}
220
+ v={state.item.color.value}
221
+ colorTemp={state.item.color.temperature}
222
+ brightness={state.item.color.brightness}
223
+ onHSVChangeComplete={(h, s, v) => {
224
+ state.item.color = {
225
+ ...state.item.color,
226
+ hue: h,
227
+ saturation: s,
228
+ value: v
229
+ }
230
+ }}
231
+ onCCTChangeComplete={(v) => {
232
+ state.item.color = {
233
+ ...state.item.color,
234
+ temperature: v
235
+ }
236
+ }}
237
+ onBrightnessChangeComplete={(v) => {
238
+ state.item.color = {
239
+ ...state.item.color,
240
+ brightness: v
241
+ }
242
+ }}
243
+ />
244
+ </Card>
245
+ <Spacer />
246
+ </View>
247
+ {/* summary */}
248
+ <Summary
249
+ frequency={loopText(state.item.weeks)}
250
+ time={params.is24Hour ?
251
+ `${parseTimer(state.item.startTime * 60)} - ${parseTimer(state.item.endTime * 60)}`
252
+ :
253
+ `${parseHour12(state.item.startTime * 60)} - ${parseHour12(state.item.endTime * 60)}`
254
+ }
255
+ hideActions={true}
256
+ />
257
+ <Spacer/>
258
+ {params.mode === 'edit' &&
259
+ <View style={{marginHorizontal: cx(24)}}>
260
+ <DeleteButton
261
+ text={I18n.getLang('edit_fixedtimecycle_bttn_text')}
262
+ onPress={onDelete}/>
263
+ </View>
264
+ }
265
+ <Spacer/>
266
+ </ScrollView>
267
+ </Page>
268
+ )
269
+ }
270
+
271
+ const styles = StyleSheet.create({
272
+ cardContainer: {
273
+ marginHorizontal: cx(24)
274
+ },
275
+ itemTitle: {
276
+ color: '#000',
277
+ fontSize: cx(16),
278
+ fontWeight: 'bold',
279
+ fontFamily: 'helvetica_neue_lt_std_bd',
280
+ },
281
+ applyContent: {
282
+ backgroundColor: '#f6f6f6',
283
+ borderRadius: 4,
284
+ minHeight: cx(55),
285
+ flex: 1,
286
+ justifyContent: 'center',
287
+ paddingHorizontal: cx(10),
288
+ paddingTop: cx(10)
289
+ },
290
+ applyItem: {
291
+ paddingLeft: cx(5),
292
+ flexDirection: 'row',
293
+ justifyContent: 'space-between',
294
+ alignItems: 'center',
295
+ backgroundColor: '#fff',
296
+ height: cx(35),
297
+ },
298
+ summaryContainer: {
299
+ flexDirection: 'row',
300
+ justifyContent: 'flex-start',
301
+ marginBottom: cx(10)
302
+ },
303
+ summaryLeft: {
304
+ flexDirection: 'row',
305
+ justifyContent: 'flex-start',
306
+ alignItems: 'center',
307
+ },
308
+ summaryImg: {
309
+ width: cx(12),
310
+ height: cx(12),
311
+ marginRight: cx(6)
312
+ },
313
+ leftTitle: {
314
+ fontSize: cx(14),
315
+ color: '#000'
316
+ },
317
+ summaryRight: {
318
+ flexDirection: 'column',
319
+ marginLeft: cx(21),
320
+ borderRadius: cx(16),
321
+ backgroundColor: '#cbcbcb',
322
+ },
323
+ rightItem: {
324
+ paddingHorizontal: cx(12),
325
+ color: '#000',
326
+ },
327
+ rightTitle: {
328
+ paddingLeft: cx(12),
329
+ paddingRight: cx(12),
330
+ fontSize: cx(10),
331
+ textAlign: 'center',
332
+ alignSelf: 'flex-start'
333
+ },
334
+ filletCorner: {
335
+ flexDirection: 'row',
336
+ backgroundColor: '#cbcbcb',
337
+ borderRadius: cx(16),
338
+ alignSelf: 'flex-start'
339
+ }
340
+ })
341
+
342
+ export default RandomTimingForLightDetailPage