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

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