@ledvance/group-ui-biz-bundle 1.0.0

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.
Files changed (40) hide show
  1. package/.babelrc +31 -0
  2. package/.eslintignore +6 -0
  3. package/.eslintrc.js +27 -0
  4. package/.prettierignore +0 -0
  5. package/.prettierrc.js +1 -0
  6. package/.versionrc +5 -0
  7. package/package.json +73 -0
  8. package/rn-cli.config.js +8 -0
  9. package/src/modules/mood/AddMoodPage.tsx +826 -0
  10. package/src/modules/mood/DynamicMoodEditorPage.tsx +547 -0
  11. package/src/modules/mood/MoodItem.tsx +114 -0
  12. package/src/modules/mood/MoodPage.tsx +332 -0
  13. package/src/modules/mood/RecommendMoodItem.tsx +75 -0
  14. package/src/modules/mood/SceneAction.ts +459 -0
  15. package/src/modules/mood/SceneInfo.ts +886 -0
  16. package/src/modules/mood/StaticMoodEditorPage.tsx +251 -0
  17. package/src/modules/mood/tools.ts +12 -0
  18. package/src/modules/select/SelectPage.d.ts +12 -0
  19. package/src/modules/select/SelectPage.tsx +139 -0
  20. package/src/modules/time_schedule/Interface.ts +85 -0
  21. package/src/modules/time_schedule/ScheduleCard.tsx +111 -0
  22. package/src/modules/time_schedule/TimeScheduleActions.ts +81 -0
  23. package/src/modules/time_schedule/TimeScheduleDetailPage.tsx +704 -0
  24. package/src/modules/time_schedule/TimeSchedulePage.tsx +246 -0
  25. package/src/modules/timer/TimerAction.d.ts +12 -0
  26. package/src/modules/timer/TimerAction.ts +150 -0
  27. package/src/modules/timer/TimerPage.d.ts +2 -0
  28. package/src/modules/timer/TimerPage.tsx +351 -0
  29. package/src/navigation/Routers.d.ts +5 -0
  30. package/src/navigation/Routers.ts +97 -0
  31. package/src/navigation/tools.d.ts +0 -0
  32. package/src/navigation/tools.ts +0 -0
  33. package/src/res/GroupBizRes.ts +4 -0
  34. package/src/res/materialiconsFilledCancel.png +0 -0
  35. package/src/res/materialiconsFilledCancel@2x.png +0 -0
  36. package/src/res/materialiconsFilledCancel@3x.png +0 -0
  37. package/src/res/materialiconsOutlinedArrowsNavAddBox.png +0 -0
  38. package/src/res/materialiconsOutlinedArrowsNavAddBox@2x.png +0 -0
  39. package/src/res/materialiconsOutlinedArrowsNavAddBox@3x.png +0 -0
  40. package/tsconfig.json +51 -0
@@ -0,0 +1,547 @@
1
+ import React, { useCallback, useEffect, useMemo } from 'react'
2
+ import { FlatList, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
3
+ import { Utils } from 'tuya-panel-kit'
4
+ import { useReactive } from 'ahooks'
5
+ import { cloneDeep, find, isEqual } from 'lodash'
6
+ import Page from '@ledvance/base/src/components/Page'
7
+ import Strings from '@ledvance/base/src/i18n'
8
+ import { StaticMoodEditorPageParams, StaticMoodEditorPageState } from './StaticMoodEditorPage'
9
+ import { useNavigation, useRoute } from '@react-navigation/native'
10
+ import { hsv2Hex, mapFloatToRange } from '@ledvance/base/src/utils'
11
+ import { cctToColor } from '@ledvance/base/src/utils/cctUtils'
12
+ import res from '@ledvance/base/src/res'
13
+ import TextField from '@ledvance/base/src/components/TextField'
14
+ import Card from '@ledvance/base/src/components/Card'
15
+ import Spacer from '@ledvance/base/src/components/Spacer'
16
+ import LampAdjustView from '@ledvance/base/src/components/LampAdjustView'
17
+ import LdvSlider from '@ledvance/base/src/components/ldvSlider'
18
+ import { useFanMaxSpeed, useRole } from '@ledvance/base/src/models/modules/NativePropsSlice'
19
+ import TextButton from '@ledvance/base/src/components/TextButton'
20
+ import { SceneNodeInfo, SceneNodeTransitionMode, stripLightSceneMode, stringLightSceneMode, lightSceneMode, StripLightSceneMode } from './SceneInfo'
21
+ import TextFieldStyleButton from '@ledvance/base/src/components/TextFieldStyleButton'
22
+ import FanAdjustView from '@ledvance/base/src/components/FanAdjustView'
23
+ import { showDeleteMoodDialog } from './tools'
24
+ import { ui_biz_routerKey } from '../../navigation/Routers'
25
+ import { SelectPageParams } from '../select/SelectPage'
26
+ import I18n from '@ledvance/base/src/i18n'
27
+ import Segmented from '@ledvance/base/src/components/Segmented'
28
+
29
+ const cx = Utils.RatioUtils.convertX
30
+ interface DynamicMoodEditorPageState extends StaticMoodEditorPageState {
31
+ paintBucketSelected: boolean
32
+ sceneMode: StripLightSceneMode
33
+ }
34
+ const DynamicMoodEditorPage = () => {
35
+ const navigation = useNavigation()
36
+ const routeParams = useRoute().params as StaticMoodEditorPageParams
37
+ const params = cloneDeep(routeParams)
38
+ const moduleParams = params.moduleParams
39
+
40
+ const role = useRole()
41
+ const state = useReactive<DynamicMoodEditorPageState>({
42
+ headline: '',
43
+ mood: params.currentMood,
44
+ currentNode: params.currentMood.nodes[params.currentMood.nodes.length - 1],
45
+ paintBucketSelected: false,
46
+ loading: false,
47
+ sceneMode: moduleParams.isStringLight ? stringLightSceneMode : moduleParams.isStripLight ? stripLightSceneMode : lightSceneMode
48
+ })
49
+
50
+ const canDelete = useMemo(() => (role === 0 || role === 2), [role])
51
+ useEffect(() => {
52
+ state.headline = Strings.getLang(params.mode ? 'add_new_dynamic_mood_headline_text' : 'edit_static_mood_headline_text')
53
+ }, [params.mode])
54
+
55
+ const getColorBlockColor = useCallback(() => {
56
+ return '#fff'
57
+ }, [state.currentNode, state.currentNode.colorTemp])
58
+
59
+ const getNodeColor = useCallback((node: SceneNodeInfo) => {
60
+ if (node.isColorNode) {
61
+ const s = Math.round(mapFloatToRange(node.s / 100, 30, 100))
62
+ return hsv2Hex(node.h, s, 100)
63
+ }
64
+ return cctToColor(node.colorTemp.toFixed())
65
+ }, [])
66
+
67
+ const getButtonStatus = () => {
68
+ return (params.mode === 'edit' && isEqual(state.mood, routeParams.currentMood)) ||
69
+ !(!!state.mood.name) ||
70
+ nameRepeat ||
71
+ state.mood.name.length > 32 ||
72
+ state.mood.nodes.length < 2
73
+ }
74
+
75
+ const createSelectModeData = useCallback(() => {
76
+ return Object.values(state.sceneMode).map(scene => {
77
+ return {
78
+ text: scene.title,
79
+ selected: scene.mode === state.mood.mode,
80
+ value: scene.mode,
81
+ }
82
+ })
83
+ }, [state.mood.mode, state.sceneMode])
84
+
85
+ const createSelectOtherData = useCallback((otherData) => {
86
+ return otherData.map(other => {
87
+ return {
88
+ text: other.label,
89
+ selected: other.value === state.mood.expand,
90
+ value: other.value,
91
+ }
92
+ })
93
+ }, [state.mood.expand])
94
+
95
+ const getSelectOther = useCallback((otherData) => {
96
+ const currentOther = otherData.find(other => other.value === state.mood.expand)
97
+ return currentOther.label
98
+ }, [state.mood.expand])
99
+
100
+ const nameRepeat = useMemo(() => {
101
+ return !!find(params.moods, m => (m.id !== state.mood.id && m.name === state.mood.name))
102
+ }, [state.mood.name])
103
+
104
+ return (
105
+ <Page
106
+ backText={Strings.getLang('mesh_device_detail_mode')}
107
+ showBackDialog={true}
108
+ backDialogTitle={
109
+ Strings.getLang(params.mode === 'add' ?
110
+ 'string_light_pp_dialog_sm_add_headline_c' :
111
+ 'manage_user_unsaved_changes_dialog_headline')
112
+ }
113
+ backDialogContent={
114
+ Strings.getLang(params.mode === 'add' ?
115
+ 'strip_light_static_mood_add_step_2_dialog_text' :
116
+ 'strip_light_static_mood_editor_step_2_dialog_text')
117
+ }
118
+ headlineText={state.headline}
119
+ rightButtonIcon={getButtonStatus() ? res.ic_uncheck : res.ic_check}
120
+ rightButtonDisabled={getButtonStatus()}
121
+ rightButtonIconClick={async () => {
122
+ if (state.loading) return
123
+ state.loading = true
124
+ const res = await params.modDeleteMood(params.mode, cloneDeep(state.mood))
125
+ if (res.success) {
126
+ navigation.navigate(ui_biz_routerKey.group_ui_biz_mood)
127
+ }
128
+ state.loading = false
129
+ }}
130
+ loading={state.loading}>
131
+ <ScrollView
132
+ style={{ flex: 1 }}
133
+ nestedScrollEnabled={true}>
134
+ <View style={styles.root}>
135
+ <TextField
136
+ style={styles.name}
137
+ value={state.mood.name}
138
+ placeholder={Strings.getLang('edit_static_mood_inputfield_topic_text')}
139
+ onChangeText={text => {
140
+ state.mood.name = text
141
+ }}
142
+ maxLength={33}
143
+ showError={state.mood.name.length > 32 || nameRepeat}
144
+ tipColor={nameRepeat ? '#f00' : undefined}
145
+ tipIcon={nameRepeat ? res.ic_text_field_input_error : undefined}
146
+ errorText={Strings.getLang(nameRepeat ? 'string_light_pp_field_sm_add_error1' : 'add_new_dynamic_mood_alert_text')} />
147
+ <Card style={styles.adjustCard}>
148
+ <Spacer height={cx(16)} />
149
+ <View style={styles.lightLine}>
150
+ <Text style={styles.light}>
151
+ {Strings.getLang('light_sources_tile_tw_lighting_headline')}
152
+ </Text>
153
+ </View>
154
+ <Spacer height={cx(18)} />
155
+ <TextFieldStyleButton
156
+ style={styles.transitionMode}
157
+ text={state.sceneMode[state.mood.mode]?.title}
158
+ placeholder={Strings.getLang('add_new_dynamic_mood_color_changing_mode_headline')}
159
+ onPress={() => {
160
+ const paramsSelect: SelectPageParams<number> = {
161
+ title: I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline'),
162
+ data: createSelectModeData(),
163
+ onSelect: selectPageData => {
164
+ state.mood.mode = selectPageData.value
165
+ },
166
+ }
167
+ navigation.navigate(ui_biz_routerKey.group_ui_biz_select_page, paramsSelect)
168
+ }} />
169
+ <Spacer height={cx(10)} />
170
+ <LdvSlider
171
+ title={Strings.getLang('add_new_dynamic_mood_lights_field_speed_topic_text')}
172
+ value={state.mood.speed}
173
+ onValueChange={() => { }}
174
+ onSlidingComplete={value => {
175
+ state.mood.speed = value
176
+ }} />
177
+ <Spacer height={cx(16)} />
178
+ {state.sceneMode[state.mood.mode]?.turnOn && <View style={styles.transitionMode}>
179
+ <Segmented
180
+ value={state.mood.direction}
181
+ options={[
182
+ {
183
+ label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text1'),
184
+ value: 0
185
+ },
186
+ {
187
+ label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text2'),
188
+ value: 1
189
+ },
190
+ ]}
191
+ onChange={(v) => state.mood.direction = Number(v)}
192
+ />
193
+ <Spacer />
194
+ </View>}
195
+ {state.sceneMode[state.mood.mode]?.paragraph && <View style={styles.transitionMode}>
196
+ <Segmented
197
+ value={state.mood.segmented}
198
+ options={[
199
+ {
200
+ label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text3'),
201
+ value: 0
202
+ },
203
+ {
204
+ label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text4'),
205
+ value: 1
206
+ },
207
+ ]}
208
+ onChange={(v) => state.mood.segmented = Number(v)}
209
+ />
210
+ <Spacer />
211
+ </View>}
212
+ {state.sceneMode[state.mood.mode]?.other && <>
213
+ <TextFieldStyleButton
214
+ style={styles.transitionMode}
215
+ text={getSelectOther(state.sceneMode[state.mood.mode]?.other)}
216
+ placeholder={I18n.getLang('add_new_dynamic_mood_strip_lights_selectionfield2_topic_text')}
217
+ onPress={() => {
218
+ const paramsSelect: SelectPageParams<number> = {
219
+ title: I18n.getLang('add_new_dynamic_mood_strip_lights_selectionfield2_topic_text'),
220
+ data: createSelectOtherData(state.sceneMode[state.mood.mode]?.other),
221
+ onSelect: selectPageData => {
222
+ state.mood.expand = selectPageData.value
223
+ },
224
+ }
225
+ navigation.navigate(ui_biz_routerKey.group_ui_biz_select_page, paramsSelect)
226
+ }} />
227
+ <Spacer />
228
+ </>}
229
+ <View style={styles.nodesAdjust}>
230
+ <View style={styles.adjustButtons}>
231
+ <TouchableOpacity
232
+ onPress={() => {
233
+ state.paintBucketSelected = true
234
+ }}>
235
+ <Image
236
+ style={[styles.adjustButton, { tintColor: state.paintBucketSelected ? '#f60' : '#666' }]}
237
+ source={res.ic_paint_bucket} />
238
+ </TouchableOpacity>
239
+ <TouchableOpacity
240
+ onPress={() => {
241
+ state.paintBucketSelected = false
242
+ }}>
243
+ <Image
244
+ style={[styles.adjustButton, { tintColor: state.paintBucketSelected ? '#666' : '#f60' }]}
245
+ source={res.ic_colorize} />
246
+ </TouchableOpacity>
247
+ </View>
248
+ <FlatList
249
+ data={state.mood.nodes}
250
+ style={styles.nodeList}
251
+ renderItem={({ item, index }) => {
252
+ return (
253
+ <View style={styles.nodeItem}>
254
+ <TouchableOpacity
255
+ style={[
256
+ styles.nodeBlock,
257
+ {
258
+ backgroundColor: getNodeColor(item),
259
+ },
260
+ ]}
261
+ onPress={() => {
262
+ state.currentNode = item
263
+ }} />
264
+ <TouchableOpacity
265
+ style={styles.nodeDeleteBtn}
266
+ disabled={state.mood.nodes.length < 3}
267
+ onPress={() => {
268
+ state.mood.nodes.splice(index, 1)
269
+ state.currentNode = state.mood.nodes[state.mood.nodes.length - 1]
270
+ }}>
271
+ <Image
272
+ style={[
273
+ styles.nodeDeleteIcon,
274
+ {
275
+ tintColor: state.mood.nodes.length < 3 ? '#ccc' : '#666',
276
+ },
277
+ ]}
278
+ source={res.ic_mood_del} />
279
+ </TouchableOpacity>
280
+ </View>
281
+ )
282
+ }}
283
+ keyExtractor={(_, index) => `${index}`}
284
+ ItemSeparatorComponent={() => <Spacer height={cx(12)} />}
285
+ ListFooterComponent={() => {
286
+ if (state.mood.nodes.length >= 8) {
287
+ return (<></>)
288
+ }
289
+ return (
290
+ <View>
291
+ <Spacer height={cx(12)} />
292
+ <TouchableOpacity
293
+ style={styles.nodeAddBtn}
294
+ onPress={() => {
295
+ const node = {
296
+ ...state.currentNode,
297
+ }
298
+ state.mood.nodes.push(node)
299
+ state.currentNode = node
300
+ }}>
301
+ <Image
302
+ style={{
303
+ width: cx(18),
304
+ height: cx(18),
305
+ tintColor: '#000',
306
+ }}
307
+ source={{ uri: res.add }} />
308
+ </TouchableOpacity>
309
+ </View>
310
+ )
311
+ }} />
312
+ </View>
313
+ <Spacer />
314
+ <View style={styles.lightLine}>
315
+ <Text style={styles.light}>
316
+ {Strings.getLang('add_new_dynamic_mood_lights_field_headline2_text')}
317
+ </Text>
318
+ <View style={[styles.preview, { backgroundColor: getColorBlockColor() }]} />
319
+ </View>
320
+ <Spacer />
321
+ <LampAdjustView
322
+ isSupportColor={moduleParams.isSupportColor}
323
+ isSupportBrightness={moduleParams.isSupportBrightness}
324
+ isSupportTemperature={moduleParams.isSupportTemperature}
325
+ isColorMode={state.currentNode.isColorNode}
326
+ reserveSV={true}
327
+ setIsColorMode={isColorMode => {
328
+ if (state.paintBucketSelected) {
329
+ state.mood.nodes.forEach(node => {
330
+ node.isColorNode = isColorMode
331
+ })
332
+ } else {
333
+ state.currentNode.isColorNode = isColorMode
334
+ }
335
+ }}
336
+ h={state.currentNode.h}
337
+ s={state.currentNode.s}
338
+ v={state.currentNode.v}
339
+ onHSVChange={(h, s, v) => {
340
+ if (state.paintBucketSelected) {
341
+ state.mood.nodes.forEach(node => {
342
+ node.isColorNode = true
343
+ node.h = h
344
+ node.s = s
345
+ node.v = v
346
+ })
347
+ } else {
348
+ state.currentNode.h = h
349
+ state.currentNode.s = s
350
+ state.currentNode.v = v
351
+ }
352
+ }}
353
+ onHSVChangeComplete={(h, s, v) => {
354
+ if (state.paintBucketSelected) {
355
+ state.mood.nodes.forEach(node => {
356
+ node.isColorNode = true
357
+ node.h = h
358
+ node.s = s
359
+ node.v = v
360
+ })
361
+ } else {
362
+ state.currentNode.h = h
363
+ state.currentNode.s = s
364
+ state.currentNode.v = v
365
+ }
366
+ }}
367
+ colorTemp={state.currentNode.colorTemp}
368
+ brightness={state.currentNode.brightness}
369
+ onCCTChange={cct => {
370
+ if (state.paintBucketSelected) {
371
+ state.mood.nodes.forEach(node => {
372
+ node.isColorNode = false
373
+ node.colorTemp = cct
374
+ })
375
+ } else {
376
+ state.currentNode.colorTemp = cct
377
+ }
378
+ }}
379
+ onCCTChangeComplete={cct => {
380
+ if (state.paintBucketSelected) {
381
+ state.mood.nodes.forEach(node => {
382
+ node.isColorNode = false
383
+ node.colorTemp = cct
384
+ })
385
+ } else {
386
+ state.currentNode.colorTemp = cct
387
+ }
388
+ }}
389
+ onBrightnessChange={brightness => {
390
+ if (state.paintBucketSelected) {
391
+ state.mood.nodes.forEach(node => {
392
+ node.isColorNode = false
393
+ node.brightness = brightness
394
+ })
395
+ } else {
396
+ state.currentNode.brightness = brightness
397
+ }
398
+ }}
399
+ onBrightnessChangeComplete={brightness => {
400
+ if (state.paintBucketSelected) {
401
+ state.mood.nodes.forEach(node => {
402
+ node.isColorNode = false
403
+ node.brightness = brightness
404
+ })
405
+ } else {
406
+ state.currentNode.brightness = brightness
407
+ }
408
+ }} />
409
+ </Card>
410
+ <Spacer />
411
+ {moduleParams.isSupportFan &&
412
+ <FanAdjustView
413
+ fanEnable={!!state.mood.fanEnable}
414
+ fanSpeed={state.mood.fanSpeed || 1}
415
+ maxFanSpeed={useFanMaxSpeed()}
416
+ onFanSwitch={fanEnable => {
417
+ state.mood.fanEnable = fanEnable
418
+ }}
419
+ onFanSpeedChange={fanSpeed => {
420
+ state.mood.fanSpeed = fanSpeed
421
+ }}
422
+ onFanSpeedChangeComplete={fanSpeed => {
423
+ state.mood.fanSpeed = fanSpeed
424
+ }}
425
+ style={styles.fanAdjustCard} />}
426
+ {params.mode === 'edit' && canDelete &&
427
+ <View style={{ marginTop: cx(20), marginHorizontal: cx(24) }}>
428
+ <TextButton
429
+ style={styles.deleteBtn}
430
+ textStyle={styles.deleteBtnText}
431
+ text={Strings.getLang('edit_static_mood_button_delete_text')}
432
+ onPress={() => {
433
+ showDeleteMoodDialog(async (_, { close }) => {
434
+ state.loading = true
435
+ close()
436
+ const res = await params.modDeleteMood('del', state.mood)
437
+ if (res.success) {
438
+ navigation.navigate(ui_biz_routerKey.group_ui_biz_mood)
439
+ }
440
+ state.loading = false
441
+ })
442
+ }} />
443
+ </View>}
444
+ <Spacer />
445
+ </View>
446
+ </ScrollView>
447
+ </Page>
448
+ )
449
+ }
450
+ const styles = StyleSheet.create({
451
+ root: {
452
+ flex: 1,
453
+ flexDirection: 'column',
454
+ },
455
+ name: {
456
+ marginHorizontal: cx(24),
457
+ },
458
+ adjustCard: {
459
+ marginVertical: cx(12),
460
+ marginHorizontal: cx(24),
461
+ },
462
+ fanAdjustCard: {
463
+ marginHorizontal: cx(24),
464
+ },
465
+ lightLine: {
466
+ flexDirection: 'row',
467
+ marginHorizontal: cx(16),
468
+ },
469
+ light: {
470
+ color: '#000',
471
+ fontSize: cx(18),
472
+ fontFamily: 'helvetica_neue_lt_std_bd',
473
+ },
474
+ transitionMode: {
475
+ marginHorizontal: cx(16),
476
+ },
477
+ preview: {
478
+ width: cx(20),
479
+ height: cx(20),
480
+ marginStart: cx(12),
481
+ borderRadius: cx(4),
482
+ },
483
+ nodesAdjust: {
484
+ flexDirection: 'row',
485
+ alignItems: 'center',
486
+ },
487
+ adjustButtons: {
488
+ width: cx(44),
489
+ marginStart: cx(16),
490
+ },
491
+ adjustButton: {
492
+ width: cx(44),
493
+ height: cx(44),
494
+ },
495
+ nodeList: {
496
+ flex: 1,
497
+ marginHorizontal: cx(16),
498
+ },
499
+ nodeItem: {
500
+ flexDirection: 'row',
501
+ alignItems: 'center',
502
+ },
503
+ nodeBlock: {
504
+ flex: 1,
505
+ height: cx(40),
506
+ borderRadius: cx(8),
507
+ },
508
+ nodeDeleteBtn: {
509
+ width: cx(24),
510
+ height: cx(30),
511
+ justifyContent: 'center',
512
+ alignItems: 'center',
513
+ },
514
+ nodeDeleteIcon: {
515
+ width: cx(16),
516
+ height: cx(16),
517
+ },
518
+ nodeAddBtn: {
519
+ height: cx(40),
520
+ justifyContent: 'center',
521
+ alignItems: 'center',
522
+ marginEnd: cx(26),
523
+ borderRadius: cx(8),
524
+ borderWidth: cx(1),
525
+ borderStyle: 'dashed',
526
+ borderColor: '#666',
527
+ backgroundColor: '#f6f6f6',
528
+ },
529
+ deleteBtn: {
530
+ width: '100%',
531
+ height: cx(50),
532
+ backgroundColor: '#666',
533
+ borderRadius: cx(8),
534
+ },
535
+ deleteBtnText: {
536
+ color: '#fff',
537
+ fontSize: cx(16),
538
+ fontFamily: 'helvetica_neue_lt_std_bd',
539
+ },
540
+ })
541
+ export default DynamicMoodEditorPage
542
+ export function getTransitionModeString(transitionMode: SceneNodeTransitionMode): string {
543
+ if (transitionMode === SceneNodeTransitionMode.Jump) {
544
+ return Strings.getLang('other_lights_modes_jump_text')
545
+ }
546
+ return Strings.getLang('other_lights_modes_gradient_text')
547
+ }
@@ -0,0 +1,114 @@
1
+ import React, { useEffect } from 'react'
2
+ import { StyleSheet, Text, View, ViewProps, ViewStyle } from 'react-native'
3
+ import { SceneUIState, StripSceneUIState } from './SceneInfo'
4
+ import { SwitchButton, Utils } from 'tuya-panel-kit'
5
+ import Strings from '@ledvance/base/src/i18n'
6
+ import { useReactive } from 'ahooks'
7
+ import { hsv2Hex } from '@ledvance/base/src/utils'
8
+ import {cctToColor} from '@ledvance/base/src/utils/cctUtils'
9
+ import {mapFloatToRange} from '@ledvance/base/src/utils'
10
+ import Card from '@ledvance/base/src/components/Card'
11
+ import Spacer from '@ledvance/base/src/components/Spacer'
12
+ import MoodColorsLine from '@ledvance/base/src/components/MoodColorsLine'
13
+
14
+ const cx = Utils.RatioUtils.convertX
15
+
16
+ interface MoodItemProps extends ViewProps {
17
+ enable: boolean
18
+ mood: SceneUIState | StripSceneUIState
19
+ type: 'gradient' | 'separate'
20
+ style?: ViewStyle
21
+ onPress?: () => void
22
+ onSwitch: (enable: boolean) => void
23
+ }
24
+
25
+ const MoodItem = (props: MoodItemProps) => {
26
+ const { mood } = props
27
+ const state = useReactive<{ colors: string[] }>({
28
+ colors: [],
29
+ })
30
+
31
+ useEffect(() => {
32
+ state.colors = props.mood.nodes.map(node => {
33
+ if (node.isColorNode) {
34
+ return hsv2Hex(node.h, Math.round(node.s), Math.round(mapFloatToRange(node.v / 100, 50, 100)))
35
+ }
36
+ return cctToColor(node.colorTemp.toFixed())
37
+ })
38
+ }, [props.mood.nodes])
39
+
40
+ return (
41
+ <Card
42
+ style={[styles.card, props.style]}
43
+ onPress={props.onPress}>
44
+ <View>
45
+ <Spacer height={cx(16)} />
46
+ <View style={styles.headline}>
47
+ <Text style={styles.headText}>{mood.name}</Text>
48
+ <SwitchButton
49
+ thumbStyle={{ elevation: 0 }}
50
+ value={props.enable}
51
+ onValueChange={props.onSwitch} />
52
+ </View>
53
+ <Spacer />
54
+ <View style={styles.gradientItem}>
55
+ <MoodColorsLine
56
+ type={props.type}
57
+ colors={state.colors} />
58
+ </View>
59
+ <Spacer height={cx(12)} />
60
+ <View style={styles.moodTypeItem}>
61
+ <View style={styles.moodTypeLabel}>
62
+ <Text style={styles.moodTypeLabelText}>
63
+ {Strings.getLang(mood.nodes.length > 1 ? 'mood_overview_field_chip_2' : 'mood_overview_field_chip_text')}
64
+ </Text>
65
+ </View>
66
+ </View>
67
+ <Spacer height={cx(16)} />
68
+ </View>
69
+ </Card>
70
+ )
71
+ }
72
+
73
+ export default MoodItem
74
+
75
+ const styles = StyleSheet.create({
76
+ card: {
77
+ marginHorizontal: cx(24),
78
+ },
79
+ headline: {
80
+ flexDirection: 'row',
81
+ marginHorizontal: cx(16),
82
+ },
83
+ headText: {
84
+ flex: 1,
85
+ color: '#000',
86
+ fontSize: cx(16),
87
+ fontFamily: 'helvetica_neue_lt_std_bd',
88
+ lineHeight: cx(20),
89
+ },
90
+ gradientItem: {
91
+ alignItems: 'center',
92
+ },
93
+ gradient: {
94
+ borderRadius: cx(8),
95
+ },
96
+ moodTypeItem: {
97
+ flexDirection: 'row',
98
+ },
99
+ moodTypeLabel: {
100
+ marginStart: cx(16),
101
+ paddingHorizontal: cx(12.5),
102
+ backgroundColor: '#E6E7E8',
103
+ borderRadius: cx(8),
104
+ },
105
+ moodTypeLabelText: {
106
+ height: cx(16),
107
+ color: '#000000DD',
108
+ fontSize: cx(10),
109
+ textAlignVertical: 'center',
110
+ fontFamily: 'helvetica_neue_lt_std_roman',
111
+ lineHeight: cx(16),
112
+ },
113
+ })
114
+