@jescon-technologies/react-native-ui-kit 0.0.14 → 0.0.16

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,504 @@
1
+ import React, { useEffect, useState, useRef } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ Platform,
6
+ TouchableOpacity,
7
+ StyleProp,
8
+ ViewStyle,
9
+ TextStyle,
10
+ } from 'react-native';
11
+ import DateTimePicker, {
12
+ DateTimePickerEvent,
13
+ } from '@react-native-community/datetimepicker';
14
+ import HIcons from '../Hoc/HIcons';
15
+ import moment from 'moment';
16
+ import { normalize } from '../CommonStyle/componentStyle';
17
+ import { isNU, ValueCheck } from '../CommonStyle/ComponentFunctions';
18
+ import { Global } from '../../../../../src/Common/Global';
19
+ ;
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Types
23
+ // ---------------------------------------------------------------------------
24
+
25
+ type PickerMode = 'date' | 'time';
26
+
27
+ interface IconDescriptor {
28
+ icongroup?: number;
29
+ name?: string;
30
+ size?: number;
31
+ color?: string;
32
+ style?: StyleProp<TextStyle>;
33
+ }
34
+
35
+ export interface SDateTimePickerNewProps {
36
+ /** Initial date (ISO string, date string, or Date). */
37
+ passingDate?: string | Date | null;
38
+ /** Extra data object; a `value: Date` field is written back on change. */
39
+ data?: Record<string, unknown> & { value?: Date };
40
+ /** Minimum selectable date. */
41
+ minimumdate?: string | Date | null;
42
+ /** Maximum selectable date. */
43
+ maximumdate?: string | Date | null;
44
+ /** Enable the date picker. */
45
+ date?: boolean;
46
+ /** Enable the time picker. */
47
+ time?: boolean;
48
+ /** Disable the date picker. */
49
+ disabled?: boolean;
50
+ /** Disable the time picker. */
51
+ timedisabled?: boolean;
52
+ /** Hide the floating TIME label when `table` is defined. */
53
+ table?: unknown;
54
+ /** Label shown above the date picker. */
55
+ labeltext?: string;
56
+ /** Placeholder label for the time field. Defaults to 'TIME'. */
57
+ timeplaceholder?: string;
58
+ /** Icon descriptor used for the date picker (native). */
59
+ icon?: IconDescriptor;
60
+ /** Icon descriptor used for the time picker (native). */
61
+ timeicon?: IconDescriptor;
62
+ /** Width of the time field (native + web). */
63
+ timewidth?: number;
64
+ /** Height of the time field (native + web). */
65
+ timeheight?: number;
66
+ /** Background color of the time field. */
67
+ timebgcolor?: string;
68
+ /** Container style for the date picker (native). */
69
+ pickerstyle?: StyleProp<ViewStyle>;
70
+ /** Container style for the floating label (native). */
71
+ positionviewstyle?: StyleProp<ViewStyle>;
72
+ /** Text style for the floating label (native). */
73
+ positiontextstyle?: StyleProp<TextStyle>;
74
+ /** Text style for the date value (native). */
75
+ textstyle?: StyleProp<TextStyle>;
76
+ /** Container style for the time picker (native). */
77
+ timepickerstyle?: StyleProp<ViewStyle>;
78
+ /** Text style for the time value (native). */
79
+ timetextstyle?: StyleProp<TextStyle>;
80
+ /** Style for the time icon (native). */
81
+ timeiconstyle?: StyleProp<TextStyle>;
82
+ /** Fired whenever the date/time changes. Receives an ISO 8601 string. */
83
+ onChangeData?: (iso: string) => void;
84
+ }
85
+
86
+ // ---------------------------------------------------------------------------
87
+ // Helpers
88
+ // ---------------------------------------------------------------------------
89
+
90
+ function formatTime12Hour(date: Date): string {
91
+ return date.toLocaleTimeString('en-US', {
92
+ hour: '2-digit',
93
+ minute: '2-digit',
94
+ hour12: true,
95
+ });
96
+ }
97
+
98
+ /** Web `<input>` shim — RN's TS config doesn't include DOM tags. */
99
+ const WebInput: React.FC<{
100
+ ref?: React.Ref<HTMLInputElement>;
101
+ type: string;
102
+ value: string;
103
+ min?: string;
104
+ max?: string;
105
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
106
+ disabled?: boolean;
107
+ style?: React.CSSProperties;
108
+ }> = (props) => React.createElement('input', props as React.InputHTMLAttributes<HTMLInputElement>);
109
+
110
+ // ---------------------------------------------------------------------------
111
+ // Component
112
+ // ---------------------------------------------------------------------------
113
+
114
+ const SDateTimePicker: React.FC<SDateTimePickerNewProps> = (props) => {
115
+ const {
116
+ passingDate,
117
+ data: dataProp,
118
+ minimumdate: minProp,
119
+ maximumdate: maxProp,
120
+ date: showDate = false,
121
+ time: showTime = false,
122
+ disabled,
123
+ timedisabled,
124
+ table,
125
+ labeltext,
126
+ timeplaceholder,
127
+ icon,
128
+ timeicon,
129
+ timewidth,
130
+ timeheight,
131
+ timebgcolor,
132
+ pickerstyle,
133
+ positionviewstyle,
134
+ positiontextstyle,
135
+ textstyle,
136
+ timepickerstyle,
137
+ timetextstyle,
138
+ timeiconstyle,
139
+ onChangeData,
140
+ } = props;
141
+
142
+ // State management
143
+ const [dateTime, setDateTime] = useState<Date>(
144
+ passingDate ? new Date(passingDate) : new Date()
145
+ );
146
+ const [timeZone, setTimeZone] = useState<string>(formatTime12Hour(new Date()));
147
+ const [mode, setMode] = useState<PickerMode>('date');
148
+ const [showPicker, setShowPicker] = useState<boolean>(false);
149
+ const [data, setData] = useState<Record<string, unknown> & { value?: Date }>(
150
+ dataProp ?? {}
151
+ );
152
+
153
+ // Minimum and maximum date constraints
154
+ const [minimumdate] = useState<Date | null>(
155
+ isNU(minProp) ? new Date(minProp as string | Date) : null
156
+ );
157
+ const [maximumdate] = useState<Date | null>(
158
+ isNU(maxProp) ? new Date(maxProp as string | Date) : null
159
+ );
160
+
161
+ const inputRef = useRef<HTMLInputElement>(null);
162
+
163
+ // Sync incoming `data` prop if it changes externally (original didn't, but
164
+ // keeping a no-op comment so the reader knows it's intentional).
165
+ useEffect(() => {
166
+ // no-op: original relied on `data` prop only at mount
167
+ }, [dataProp]);
168
+
169
+ // Format date for display
170
+ const formatDate = (d: Date): string =>
171
+ d.toLocaleDateString('en-US', {
172
+ weekday: 'short',
173
+ year: 'numeric',
174
+ month: 'short',
175
+ day: 'numeric',
176
+ });
177
+
178
+ // Handle date/time change for native platforms
179
+ const onNativeChange = (_event: DateTimePickerEvent, selectedValue?: Date): void => {
180
+ if (selectedValue) {
181
+ setDateTime((prevDateTime) => {
182
+ const newDateTime = new Date(prevDateTime);
183
+
184
+ if (mode === 'date') {
185
+ // If date is being selected, merge it with the existing time
186
+ newDateTime.setUTCFullYear(selectedValue.getUTCFullYear());
187
+ newDateTime.setUTCMonth(selectedValue.getUTCMonth());
188
+ newDateTime.setUTCDate(selectedValue.getUTCDate());
189
+
190
+ if (showTime === true) {
191
+ setMode('time');
192
+ setShowPicker(true);
193
+ } else {
194
+ setShowPicker(false);
195
+ setData((prev) => ({ ...prev, value: newDateTime }));
196
+ onChangeData?.(newDateTime.toISOString());
197
+ }
198
+ } else {
199
+ // If time is being selected, merge it with the existing date
200
+ newDateTime.setUTCHours(selectedValue.getUTCHours());
201
+ newDateTime.setUTCMinutes(selectedValue.getUTCMinutes());
202
+ newDateTime.setUTCSeconds(0);
203
+ newDateTime.setUTCMilliseconds(0);
204
+
205
+ setShowPicker(false);
206
+
207
+ setData((prev) => ({ ...prev, value: newDateTime }));
208
+ onChangeData?.(newDateTime.toISOString());
209
+
210
+ setTimeout(() => {
211
+ setTimeZone(formatTime12Hour(newDateTime));
212
+ }, 10);
213
+ }
214
+
215
+ return newDateTime;
216
+ });
217
+ }
218
+ };
219
+
220
+ // Handle date/time change for web platform
221
+ const onWebChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
222
+ const value = e.target.value;
223
+ if (!value) return;
224
+
225
+ let selectedDate: Date;
226
+
227
+ if (showTime === true && showDate === true) {
228
+ // datetime-local format: YYYY-MM-DDTHH:mm (local time)
229
+ const parts = value.split('T');
230
+ const dateParts = parts[0].split('-');
231
+ const timeParts = parts[1].split(':');
232
+
233
+ selectedDate = new Date(
234
+ parseInt(dateParts[0], 10),
235
+ parseInt(dateParts[1], 10) - 1,
236
+ parseInt(dateParts[2], 10),
237
+ parseInt(timeParts[0], 10),
238
+ parseInt(timeParts[1], 10),
239
+ 0
240
+ );
241
+ } else if (showTime !== true && showDate === true) {
242
+ // Date-only picker
243
+ selectedDate = new Date(value + 'T00:00:00');
244
+ } else {
245
+ // Time-only / other
246
+ selectedDate = new Date(value);
247
+ }
248
+
249
+ if (isNaN(selectedDate.getTime())) return;
250
+
251
+ setDateTime(selectedDate);
252
+ setData((prev) => ({ ...prev, value: selectedDate }));
253
+
254
+ if (showTime === true) {
255
+ setTimeZone(formatTime12Hour(selectedDate));
256
+ }
257
+
258
+ onChangeData?.(selectedDate.toISOString());
259
+ };
260
+
261
+ // Show date picker
262
+ const showDatepicker = (): void => {
263
+ setMode('date');
264
+ setShowPicker(true);
265
+ };
266
+
267
+ // Show time picker
268
+ const showTimepicker = (): void => {
269
+ setMode('time');
270
+ setShowPicker(true);
271
+ };
272
+
273
+ const getWebInputValue = (): string => {
274
+ if (!(dateTime instanceof Date) || isNaN(dateTime.getTime())) return '';
275
+
276
+ if (showTime === true && showDate === true) {
277
+ const year = dateTime.getFullYear();
278
+ const month = String(dateTime.getMonth() + 1).padStart(2, '0');
279
+ const day = String(dateTime.getDate()).padStart(2, '0');
280
+ const hours = String(dateTime.getHours()).padStart(2, '0');
281
+ const minutes = String(dateTime.getMinutes()).padStart(2, '0');
282
+ return `${year}-${month}-${day}T${hours}:${minutes}`;
283
+ } else if (showTime !== true && showDate === true) {
284
+ return dateTime.toISOString().split('T')[0];
285
+ } else if (showTime === true) {
286
+ return dateTime.toTimeString().split(' ')[0].substring(0, 5);
287
+ }
288
+ return dateTime.toISOString().substring(0, 16);
289
+ };
290
+
291
+ // -------------------------------------------------------------------------
292
+ // Render — web
293
+ // -------------------------------------------------------------------------
294
+
295
+ if (Platform.OS === 'web') {
296
+ return (
297
+ <>
298
+ {showDate === true && (
299
+ <View style={{ marginRight: showTime === true ? normalize(10) : 0 }}>
300
+ {labeltext && (
301
+ <Text style={{ color: Global.AppTheme.primary_color }}>{labeltext}</Text>
302
+ )}
303
+ <WebInput
304
+ ref={inputRef}
305
+ type={showTime === true ? 'datetime-local' : 'date'}
306
+ value={getWebInputValue()}
307
+ min={
308
+ minimumdate instanceof Date
309
+ ? minimumdate.toISOString().split('T')[0]
310
+ : undefined
311
+ }
312
+ max={
313
+ maximumdate instanceof Date
314
+ ? maximumdate.toISOString().split('T')[0]
315
+ : undefined
316
+ }
317
+ onChange={onWebChange}
318
+ disabled={disabled}
319
+ style={{
320
+ height: normalize(30),
321
+ width: '100%',
322
+ border: '1px solid #ccc',
323
+ borderRadius: normalize(5),
324
+ fontSize: normalize(14),
325
+ // ⚠️ Original spread `...props.icon` here. That's an icon
326
+ // descriptor object (icongroup/name/size/color/style), not
327
+ // CSS — spreading it as a style is almost certainly a bug.
328
+ // Preserved to match behavior; remove or replace with a
329
+ // dedicated `iconStyle` prop.
330
+ ...(icon as unknown as React.CSSProperties),
331
+ }}
332
+ />
333
+ </View>
334
+ )}
335
+
336
+ {showTime === true && showDate !== true && (
337
+ <View style={{ marginHorizontal: normalize(5) }}>
338
+ <View
339
+ style={{
340
+ position: 'absolute',
341
+ top: normalize(-4),
342
+ left: normalize(7),
343
+ backgroundColor: 'white',
344
+ flexDirection: 'row',
345
+ alignItems: 'center',
346
+ zIndex: 100,
347
+ }}
348
+ >
349
+ {table === undefined && (
350
+ <Text style={{ fontSize: normalize(9), paddingLeft: normalize(2) }}>
351
+ {ValueCheck(timeplaceholder) ? timeplaceholder : 'TIME'}
352
+ </Text>
353
+ )}
354
+ </View>
355
+
356
+ <WebInput
357
+ type="time"
358
+ value={dateTime.toTimeString().substring(0, 5)}
359
+ onChange={onWebChange}
360
+ disabled={timedisabled}
361
+ style={{
362
+ width: timewidth ? normalize(timewidth) : normalize(80),
363
+ height: timeheight ? normalize(timeheight) : normalize(35),
364
+ border: '1px solid lightgrey',
365
+ backgroundColor: ValueCheck(timebgcolor) ? timebgcolor : '#fff',
366
+ borderRadius: normalize(7),
367
+ padding: normalize(5),
368
+ fontSize: normalize(14),
369
+ }}
370
+ />
371
+ </View>
372
+ )}
373
+ </>
374
+ );
375
+ }
376
+
377
+ // -------------------------------------------------------------------------
378
+ // Render — native (iOS/Android)
379
+ // -------------------------------------------------------------------------
380
+
381
+ return (
382
+ <View>
383
+ <View style={{ flexDirection: 'row' }}>
384
+ {showDate === true && (
385
+ <TouchableOpacity
386
+ disabled={disabled}
387
+ onPress={showDatepicker}
388
+ style={pickerstyle}
389
+ >
390
+ <View style={positionviewstyle}>
391
+ <Text style={positiontextstyle}>{labeltext}</Text>
392
+ </View>
393
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
394
+ <HIcons.Icons
395
+ icongroup={icon?.icongroup}
396
+ name={icon?.name}
397
+ size={icon?.size}
398
+ color={icon?.color}
399
+ style={icon?.style}
400
+ />
401
+ <Text style={textstyle}>{moment(dateTime).format('DD-MM-YYYY')}</Text>
402
+ </View>
403
+ </TouchableOpacity>
404
+ )}
405
+
406
+ {showTime === true && (
407
+ <View
408
+ style={{
409
+ marginHorizontal: normalize(5),
410
+ marginVertical: normalize(1.5),
411
+ }}
412
+ >
413
+ <View
414
+ style={{
415
+ position: 'absolute',
416
+ top: normalize(-4),
417
+ left: normalize(7),
418
+ backgroundColor: 'white',
419
+ flexDirection: 'row',
420
+ alignItems: 'center',
421
+ zIndex: 100,
422
+ }}
423
+ >
424
+ {table === undefined && (
425
+ <Text style={{ fontSize: normalize(9), paddingLeft: normalize(2) }}>
426
+ {ValueCheck(timeplaceholder) ? timeplaceholder : 'TIME'}
427
+ </Text>
428
+ )}
429
+ </View>
430
+
431
+ <TouchableOpacity
432
+ disabled={timedisabled}
433
+ onPress={showTimepicker}
434
+ style={
435
+ isNU(timepickerstyle)
436
+ ? timepickerstyle
437
+ : {
438
+ flexDirection: 'row',
439
+ alignItems: 'center',
440
+ width: timewidth ? normalize(timewidth) : normalize(60),
441
+ height: timeheight ? normalize(timeheight) : normalize(30),
442
+ borderWidth: 1,
443
+ borderColor: 'lightgrey',
444
+ backgroundColor: ValueCheck(timebgcolor) ? timebgcolor : '#fff',
445
+ borderRadius: normalize(7),
446
+ }
447
+ }
448
+ >
449
+ {timeicon ? (
450
+ <HIcons.Icons
451
+ icongroup={isNU(timeicon.icongroup) ? timeicon.icongroup : 5}
452
+ name={timeicon.name}
453
+ size={timeicon.size}
454
+ style={timeiconstyle}
455
+ />
456
+ ) : (
457
+ <HIcons.MaterialCommunityIcons
458
+ name="timer-outline"
459
+ color={Global.AppTheme.primary_color}
460
+ size={normalize(15)}
461
+ style={{ paddingHorizontal: normalize(1) }}
462
+ />
463
+ )}
464
+
465
+ {isNU(timetextstyle) ? (
466
+ <Text adjustsFontSizeToFit style={timetextstyle}>
467
+ {isNU(timeZone) ? timeZone : null}
468
+ </Text>
469
+ ) : null}
470
+
471
+ {!isNU(timetextstyle) && (
472
+ <View>
473
+ <Text
474
+ style={{
475
+ fontSize: normalize(9),
476
+ color: 'black',
477
+ fontWeight: 'bold',
478
+ }}
479
+ >
480
+ {timeZone}
481
+ </Text>
482
+ </View>
483
+ )}
484
+ </TouchableOpacity>
485
+ </View>
486
+ )}
487
+ </View>
488
+
489
+ {showPicker && Platform.OS !== 'web' && (
490
+ <DateTimePicker
491
+ value={dateTime}
492
+ mode={mode}
493
+ is24Hour={false}
494
+ display={Platform.OS === 'ios' ? 'spinner' : 'default'}
495
+ minimumDate={minimumdate ?? undefined}
496
+ maximumDate={maximumdate ?? undefined}
497
+ onChange={onNativeChange}
498
+ />
499
+ )}
500
+ </View>
501
+ );
502
+ };
503
+
504
+ export default SDateTimePicker;
@@ -0,0 +1,118 @@
1
+ import React from 'react';
2
+ import {
3
+ TextInput,
4
+ View,
5
+ StyleSheet,
6
+ Image,
7
+ TouchableOpacity,
8
+ StyleProp,
9
+ ViewStyle,
10
+ Dimensions,
11
+ } from 'react-native';
12
+ import { normalize, ValueCheck } from '../CommonStyle/ComponentFunctions';
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Types
16
+ // ---------------------------------------------------------------------------
17
+
18
+ export interface SSearchBarProps {
19
+ /** Controlled value. If omitted, the component falls back to local state. */
20
+ value?: string;
21
+ /** Invoked on each text change with the current text. */
22
+ onChangeText?: (text: string) => void;
23
+ /** Invoked when the search icon is pressed. Receives `true`. */
24
+ callBack?: (pressed: boolean) => void;
25
+ /** Extra style applied to the outer container. */
26
+ style?: StyleProp<ViewStyle>;
27
+ }
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Component
31
+ // ---------------------------------------------------------------------------
32
+
33
+ const SSearchBar: React.FC<SSearchBarProps> = ({ value, onChangeText, callBack, style }) => {
34
+ const [localSearchData, setLocalSearchData] = React.useState<string>(value ?? '');
35
+
36
+ React.useEffect(() => {
37
+ setLocalSearchData(value ?? '');
38
+ }, [value]);
39
+
40
+ return (
41
+ <View
42
+ style={[
43
+ styles.searchTextbox_View,
44
+ styles.overlay_searchTextbox_View,
45
+ style ?? {},
46
+ ]}
47
+ >
48
+ <View style={styles.textinput_view}>
49
+ <TextInput
50
+ style={[styles.searchTextbox]}
51
+ editable={true}
52
+ autoCapitalize={'characters'}
53
+ keyboardType={'default'}
54
+ placeholder="Search"
55
+ value={localSearchData}
56
+ onChangeText={(text: string) => {
57
+ setLocalSearchData(text);
58
+ onChangeText?.(text);
59
+ }}
60
+ />
61
+ </View>
62
+
63
+ <TouchableOpacity
64
+ disabled={ValueCheck(callBack) ? false : true}
65
+ onPress={() => callBack?.(true)}
66
+ >
67
+ <Image
68
+ style={[styles.searchIcon]}
69
+ source={{ uri: 'https://i.ibb.co/j8ywYpJ/search.png' }}
70
+ />
71
+ </TouchableOpacity>
72
+ </View>
73
+ );
74
+ };
75
+
76
+ export default SSearchBar;
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Styles
80
+ // ---------------------------------------------------------------------------
81
+
82
+ const styles = StyleSheet.create({
83
+ overlay_searchTextbox_View: {
84
+ width: '95%',
85
+ alignSelf: 'center',
86
+ justifyContent: 'center',
87
+ },
88
+ textinput_view: {
89
+ flex: 1,
90
+ paddingLeft: '1%',
91
+ },
92
+ searchTextbox_View: {
93
+ flexDirection: 'row',
94
+ borderWidth: 0,
95
+ borderColor: '#ddd',
96
+ justifyContent: 'flex-start',
97
+ alignItems: 'flex-start',
98
+ borderRadius: 7,
99
+ // margin: Dimensions.get("window").height * 0.002,
100
+ marginBottom: Dimensions.get('window').height * 0.003,
101
+ height: 35,
102
+ // width:'100%',
103
+ backgroundColor: '#f1f3f6',
104
+ // backgroundColor:colors.color_componentBgcolor,
105
+ marginTop: 0, padding: 5,
106
+ },
107
+ searchTextbox: {
108
+ color: 'black',
109
+ width: '100%', marginLeft: '1%', marginRight: '1%',
110
+ fontWeight: 'bold',
111
+ fontSize: normalize(18),
112
+ textAlign: 'justify',
113
+ padding: 0, height: '100%', marginBottom: -(Dimensions.get('window').height * 0.003),
114
+ },
115
+ searchIcon: {
116
+ width: 20, height: 20, margin: 5, resizeMode: 'contain',
117
+ },
118
+ });