pixel-react 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. package/lib/assets/utils/functionUtils.d.ts +3 -0
  2. package/lib/components/DatePicker/DatePicker.d.ts +5 -0
  3. package/lib/components/DatePicker/DatePicker.stories.d.ts +9 -0
  4. package/lib/components/DatePicker/Timepicker.d.ts +4 -0
  5. package/lib/components/DatePicker/index.d.ts +1 -0
  6. package/lib/components/DatePicker/types.d.ts +81 -0
  7. package/lib/components/IconButton/IconButton.d.ts +5 -0
  8. package/lib/components/IconButton/IconButton.stories.d.ts +6 -0
  9. package/lib/components/IconButton/index.d.ts +1 -0
  10. package/lib/components/IconButton/types.d.ts +5 -0
  11. package/lib/components/InputWithDropdown/InputWithDropdown.d.ts +1 -1
  12. package/lib/components/InputWithDropdown/types.d.ts +3 -7
  13. package/lib/components/TableTree/TableTree.d.ts +1 -1
  14. package/lib/index.d.ts +65 -14
  15. package/lib/index.esm.js +8536 -227
  16. package/lib/index.esm.js.map +1 -1
  17. package/lib/index.js +8539 -229
  18. package/lib/index.js.map +1 -1
  19. package/lib/tsconfig.tsbuildinfo +1 -1
  20. package/package.json +3 -1
  21. package/src/assets/Themes/BaseTheme.scss +7 -0
  22. package/src/assets/Themes/DarkTheme.scss +7 -1
  23. package/src/assets/icons/add_variable_icon.svg +5 -0
  24. package/src/assets/icons/calendar_icon.svg +9 -0
  25. package/src/assets/icons/clock_icon.svg +11 -0
  26. package/src/assets/icons/info_icon.svg +4 -0
  27. package/src/assets/icons/left_arrow_icon.svg +3 -0
  28. package/src/assets/icons/right_arrow_icon.svg +4 -0
  29. package/src/assets/styles/_mixins.scss +1 -0
  30. package/src/assets/utils/functionUtils.ts +37 -0
  31. package/src/components/DatePicker/DatePicker.scss +387 -0
  32. package/src/components/DatePicker/DatePicker.stories.tsx +161 -0
  33. package/src/components/DatePicker/DatePicker.tsx +438 -0
  34. package/src/components/DatePicker/Timepicker.tsx +372 -0
  35. package/src/components/DatePicker/index.ts +1 -0
  36. package/src/components/DatePicker/types.ts +100 -0
  37. package/src/components/Drawer/Drawer.scss +0 -1
  38. package/src/components/Drawer/Drawer.tsx +10 -14
  39. package/src/components/Icon/iconList.ts +17 -9
  40. package/src/components/{AddButton/AddButton.scss → IconButton/IconButton.scss} +6 -2
  41. package/src/components/IconButton/IconButton.stories.tsx +25 -0
  42. package/src/components/{AddButton/AddButton.tsx → IconButton/IconButton.tsx} +10 -7
  43. package/src/components/IconButton/index.ts +1 -0
  44. package/src/components/{AddButton → IconButton}/types.ts +3 -2
  45. package/src/components/InputWithDropdown/InputWithDropdown.scss +1 -1
  46. package/src/components/InputWithDropdown/InputWithDropdown.stories.tsx +10 -13
  47. package/src/components/InputWithDropdown/InputWithDropdown.tsx +10 -8
  48. package/src/components/InputWithDropdown/types.ts +4 -7
  49. package/src/components/RadioButton/RadioButton.scss +3 -3
  50. package/src/components/Select/Select.scss +1 -1
  51. package/src/components/TableTree/TableTree.tsx +4 -0
  52. package/src/index.ts +5 -2
  53. package/src/assets/icons/expired_license_icon.svg +0 -3
  54. package/src/assets/icons/expiringSoon_license_icon.svg +0 -3
  55. package/src/components/AddButton/AddButton.stories.tsx +0 -24
  56. package/src/components/AddButton/index.ts +0 -1
@@ -0,0 +1,161 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import CustomDatePicker from './DatePicker';
3
+ import { useState } from 'react';
4
+
5
+ const meta: Meta<typeof CustomDatePicker> = {
6
+ title: 'Components/DatePicker',
7
+ component: CustomDatePicker,
8
+ parameters: {
9
+ layout: 'centered',
10
+ docs: {
11
+ description: {
12
+ component:
13
+ 'A custom date picker component with built-in calendar and input field.',
14
+ },
15
+ },
16
+ },
17
+ tags: ['autodocs'],
18
+ argTypes: {
19
+ minDate: {
20
+ description: 'The minimum selectable date',
21
+ control: {
22
+ type: 'date',
23
+ },
24
+ },
25
+ maxDate: {
26
+ description: 'The maximum selectable date',
27
+ control: {
28
+ type: 'date',
29
+ },
30
+ },
31
+ value: {
32
+ description: 'Selected date value',
33
+ control: {
34
+ type: 'date',
35
+ },
36
+ },
37
+ onChange: {
38
+ description: 'Function to handle date selection',
39
+ action: 'changed',
40
+ },
41
+ placeholder: {
42
+ description: 'Placeholder text for the input field',
43
+ control: 'text',
44
+ },
45
+ disabled: {
46
+ description: 'Disables the date picker',
47
+ control: 'boolean',
48
+ },
49
+ dateFormat: {
50
+ description: 'Date format to display',
51
+ control: 'text',
52
+ },
53
+ timeFormat: {
54
+ description: 'time format to display',
55
+ control: 'text',
56
+ },
57
+ timezone: {
58
+ description: 'Timezone for date formatting',
59
+ control: 'text',
60
+ },
61
+ calendarWidth: {
62
+ description: 'Custom width for the calendar in pixel',
63
+ control: "number"
64
+ },
65
+ error: {
66
+ description: 'Displays the input field in an error state',
67
+ control: 'boolean',
68
+ },
69
+ helperText: {
70
+ description: 'Helper text to show below the input, often used for error messages',
71
+ control: 'text',
72
+ },
73
+ },
74
+ };
75
+
76
+ export default meta;
77
+
78
+ type Story = StoryObj<typeof CustomDatePicker>;
79
+
80
+ // Default story for DatePicker
81
+ export const Default: Story = {
82
+ render: (args) => {
83
+ const [date, setDate] = useState<Date | undefined>();
84
+
85
+ return(
86
+ <CustomDatePicker
87
+ {...args}
88
+ value={date}
89
+ onChange={setDate}
90
+ />
91
+ )
92
+ }
93
+ };
94
+
95
+ // Start Date Filter story
96
+ export const StartDateFilter: Story = {
97
+ render: (args) => {
98
+ const [startDate, setStartDate] = useState<Date | undefined>();
99
+
100
+ return (
101
+ <CustomDatePicker
102
+ {...args}
103
+ value={startDate}
104
+ onChange={setStartDate}
105
+ calendarWidth = {240}
106
+ maxDate={new Date()} // Disable future dates for start date picker
107
+ />
108
+ );
109
+ },
110
+ };
111
+
112
+ // End Date Input story
113
+ export const EndDateInput: Story = {
114
+ render: (args) => {
115
+ const [startDate, setStartDate] = useState<Date | undefined>();
116
+ const [endDate, setEndDate] = useState<Date | undefined>();
117
+
118
+ return (
119
+ <>
120
+ <p>Select Start Date:</p>
121
+ <CustomDatePicker
122
+ {...args}
123
+ value={startDate}
124
+ onChange={setStartDate}
125
+ maxDate={new Date()} // Disable future dates for start date picker
126
+ />
127
+
128
+ <p>Select End Date:</p>
129
+ <CustomDatePicker
130
+ {...args}
131
+ value={endDate}
132
+ onChange={setEndDate}
133
+ disabled={!startDate}
134
+ minDate={startDate || undefined} // Restrict the end date based on selected start date
135
+ maxDate={new Date()} // Disable future dates for end date picker
136
+ />
137
+ </>
138
+ );
139
+ },
140
+ };
141
+
142
+ // Schedule Date Input story
143
+ export const ScheduleDateInput: Story = {
144
+ args: {
145
+ error: false,
146
+ helperText: "Error"
147
+ },
148
+
149
+ render: (args) => {
150
+ const [selectDate, setSelectDate] = useState<Date | undefined>(new Date());
151
+
152
+ return (
153
+ <CustomDatePicker
154
+ {...args}
155
+ value={selectDate}
156
+ onChange={setSelectDate}
157
+ minDate={new Date()} // Set minimum date to today
158
+ />
159
+ );
160
+ }
161
+ };
@@ -0,0 +1,438 @@
1
+ import React, { useEffect, useState, useRef } from 'react';
2
+ import { DayPicker } from 'react-day-picker';
3
+ import { formatInTimeZone } from 'date-fns-tz';
4
+ import TimePicker from './Timepicker';
5
+ import Icon from '../Icon';
6
+ import './DatePicker.scss';
7
+ import {
8
+ DatePickerProps,
9
+ CustomCaptionProps,
10
+ CustomCalenderButtonProps,
11
+ } from './types';
12
+ import Button from '../Button';
13
+ import Typography from '../Typography';
14
+ import classNames from 'classnames';
15
+
16
+ const CustomDatePicker: React.FC<DatePickerProps> = ({
17
+ minDate,
18
+ maxDate,
19
+ value,
20
+ onChange,
21
+ placeholder = 'Select a date',
22
+ disabled = false,
23
+ dateFormat = 'EEEE, dd MMM yyyy',
24
+ calendarWidth,
25
+ timezone = 'Asia/Kolkata',
26
+ timeFormat = 'hh:mm a',
27
+ error,
28
+ helperText = '',
29
+ }) => {
30
+ const [timeValue, setTimeValue] = useState<string>('');
31
+ const [selectedDate, setSelectedDate] = useState<Date | undefined>();
32
+ const [isPickerOpen, setIsPickerOpen] = useState<boolean>(false);
33
+ const [timeError, setTimeError] = useState<boolean>(false);
34
+ const [selectedMonth, setSelectedMonth] = useState<Date | undefined>(
35
+ new Date()
36
+ );
37
+ const [view, setView] = useState<string>('days');
38
+ const [startYear, setStartYear] = useState(() => {
39
+ const currentYear =
40
+ selectedMonth?.getFullYear() ?? new Date().getFullYear();
41
+ return currentYear - (currentYear % 12); // Set to the first year in the 12-year range
42
+ });
43
+ const [datePickerPosition, setDatePickerPosition] = useState<
44
+ 'top' | 'bottom'
45
+ >('bottom');
46
+ const pickerRef = useRef<HTMLDivElement>(null); // Ref to track the picker
47
+ const containerRef = useRef<HTMLDivElement>(null);
48
+
49
+ useEffect(() => {
50
+ if (value) {
51
+ setTimeValue(formatTimeStr(value));
52
+ setSelectedDate(value);
53
+ }
54
+ }, [value, isPickerOpen]);
55
+
56
+ const formatTimeStr = (date: Date): string => {
57
+ const hours = date.getHours().toString().padStart(2, '0');
58
+ const minutes = date.getMinutes().toString().padStart(2, '0');
59
+ return `${hours}:${minutes}`;
60
+ };
61
+
62
+ // Convert minDate and maxDate to "hh:mm" for the TimePicker if the dates match
63
+ const minTime =
64
+ selectedDate &&
65
+ minDate &&
66
+ selectedDate.toDateString() === minDate.toDateString()
67
+ ? formatTimeStr(minDate)
68
+ : undefined;
69
+
70
+ const maxTime =
71
+ selectedDate &&
72
+ maxDate &&
73
+ selectedDate.toDateString() === maxDate.toDateString()
74
+ ? formatTimeStr(maxDate)
75
+ : undefined;
76
+
77
+ useEffect(() => {
78
+ const adjustPosition = () => {
79
+ if (containerRef.current && pickerRef.current) {
80
+ const relativeRect = containerRef.current.getBoundingClientRect();
81
+ const absoluteRect = pickerRef.current.getBoundingClientRect();
82
+
83
+ const spaceBelow = window.innerHeight - relativeRect.bottom;
84
+ const spaceAbove = relativeRect.top;
85
+
86
+ // Check if there is more space above than below
87
+ if (
88
+ spaceBelow < absoluteRect.height &&
89
+ spaceAbove >= absoluteRect.height
90
+ ) {
91
+ setDatePickerPosition('top');
92
+ } else {
93
+ setDatePickerPosition('bottom');
94
+ }
95
+ }
96
+ };
97
+
98
+ // Initial check
99
+ adjustPosition();
100
+
101
+ // Adjust position on window resize
102
+ window.addEventListener('resize', adjustPosition);
103
+ return () => window.removeEventListener('resize', adjustPosition);
104
+ }, []);
105
+
106
+ const calendarStyle = {
107
+ '--rdp-day-width': calendarWidth ? `${calendarWidth / 7 - 4}px` : undefined,
108
+ } as React.CSSProperties;
109
+
110
+ const handleTimeChange = (time: string) => {
111
+ setTimeValue(time);
112
+ if (selectedDate) {
113
+ const [hoursStr, minutesStr] = time.split(':');
114
+ const newSelectedDate = new Date(selectedDate);
115
+ newSelectedDate.setHours(parseInt(hoursStr ? hoursStr : '0', 10));
116
+ newSelectedDate.setMinutes(
117
+ parseInt(minutesStr ? minutesStr : '0', 10) || 0
118
+ );
119
+ setSelectedDate(newSelectedDate);
120
+ }
121
+ };
122
+
123
+ const handleDaySelect = (date: Date | undefined) => {
124
+ if (!timeValue || !date) {
125
+ // if need to set time to current time in case no time selected
126
+ const now = new Date();
127
+ date?.setHours(now.getHours(), now.getMinutes(), now.getSeconds());
128
+
129
+ setSelectedDate(date);
130
+ } else {
131
+ const [hoursStr, minutesStr] = timeValue.split(':');
132
+ date.setHours(parseInt(hoursStr ? hoursStr : '0', 10) || 0);
133
+ date.setMinutes(parseInt(minutesStr ? minutesStr : '0', 10) || 0);
134
+ setSelectedDate(date);
135
+ }
136
+ };
137
+
138
+ const handleSave = () => {
139
+ onChange(selectedDate);
140
+ resetAndCloseDatePicker();
141
+ };
142
+
143
+ const handleDateInputClick = () => {
144
+ setIsPickerOpen((prev) => !prev);
145
+ setView('days');
146
+ setSelectedMonth(value ?? new Date());
147
+ };
148
+
149
+ const handleClickOutside = (event: MouseEvent) => {
150
+ if (
151
+ pickerRef.current &&
152
+ !pickerRef.current.contains(event.target as Node)
153
+ ) {
154
+ resetAndCloseDatePicker();
155
+ }
156
+ };
157
+
158
+ const handleCancel = () => {
159
+ resetAndCloseDatePicker();
160
+ };
161
+
162
+ const resetAndCloseDatePicker = () => {
163
+ setIsPickerOpen(false); // Close the picker if the click was outside
164
+ setView('days'); // Return to default day view on close of day picker
165
+ setSelectedMonth(value ?? new Date());
166
+ };
167
+
168
+ useEffect(() => {
169
+ if (isPickerOpen) {
170
+ document.addEventListener('mousedown', handleClickOutside);
171
+ } else {
172
+ document.removeEventListener('mousedown', handleClickOutside);
173
+ }
174
+ return () => {
175
+ document.removeEventListener('mousedown', handleClickOutside); // Clean up the event listener
176
+ };
177
+ }, [isPickerOpen]);
178
+
179
+ const months = Array.from({ length: 12 }, (_, i) =>
180
+ new Date(0, i).toLocaleString('default', { month: 'long' })
181
+ );
182
+ const years = Array.from({ length: 12 }, (_, i) => startYear + i);
183
+
184
+ const handleMonthClick = () => setView('months');
185
+ const handleYearClick = () => setView('years');
186
+
187
+ const handleMonthSelect = (monthIndex: number) => {
188
+ const newDate = new Date(selectedMonth || '');
189
+ newDate.setMonth(monthIndex);
190
+ setSelectedMonth(newDate);
191
+ setView('days');
192
+ };
193
+
194
+ const handleYearSelect = (year: number) => {
195
+ const newDate = new Date(selectedMonth || '');
196
+ newDate.setFullYear(year);
197
+ setSelectedMonth(newDate);
198
+ setView('days');
199
+ };
200
+
201
+ const handleNextClick = () => {
202
+ if (view === 'years') {
203
+ setStartYear((prev) => prev + 12);
204
+ }
205
+ };
206
+
207
+ const handlePrevClick = () => {
208
+ if (view === 'years') {
209
+ setStartYear((prev) => prev - 12);
210
+ }
211
+ };
212
+
213
+ useEffect(() => {
214
+ if (isPickerOpen && view === 'years') {
215
+ const currentYear =
216
+ selectedMonth?.getFullYear() ?? new Date().getFullYear();
217
+ setStartYear(currentYear - (currentYear % 12)); // Reset to the first year in the range that contains the selected year
218
+ }
219
+ }, [isPickerOpen, selectedMonth, view]);
220
+
221
+ // Custom Caption Component
222
+ const CustomCaption: React.FC<CustomCaptionProps> = ({ children }) => {
223
+ // If children is a React node (like an element), render it directly
224
+ if (React.isValidElement(children)) {
225
+ return <div>{children}</div>;
226
+ }
227
+
228
+ const [month = '', year = ''] = String(children).split(' ');
229
+
230
+ return (
231
+ <Typography
232
+ as="div"
233
+ fontWeight="medium"
234
+ lineHeight="16.8px"
235
+ className="ff-calendar-haeder"
236
+ >
237
+ <span onClick={handleMonthClick} className="ff-cursor-pointer">
238
+ {month}
239
+ </span>
240
+ <span onClick={handleYearClick} className="ff-cursor-pointer">
241
+ {year}
242
+ </span>
243
+ </Typography>
244
+ );
245
+ };
246
+
247
+ // Custom MonthGrid Component
248
+ const CustomMonthGrid: React.FC = () => {
249
+ return (
250
+ <div className="ff-custom-month_grid">
251
+ {months.map((month: string, index: number) => (
252
+ <div
253
+ key={index}
254
+ onClick={() => handleMonthSelect(index)}
255
+ className={classNames('ff-custom-month', {
256
+ 'ff-custom-month--selected': index === selectedMonth?.getMonth(),
257
+ })}
258
+ >
259
+ <Typography>{month}</Typography>
260
+ </div>
261
+ ))}
262
+ </div>
263
+ );
264
+ };
265
+
266
+ const CustomPrevButton: React.FC<CustomCalenderButtonProps> = ({
267
+ disabled,
268
+ onClick,
269
+ }) => {
270
+ return (
271
+ <button
272
+ className="ff-calendar-nav-button"
273
+ onClick={onClick}
274
+ disabled={disabled}
275
+ >
276
+ <Icon name="left_arrow_icon" height={12} width={12} />
277
+ </button>
278
+ );
279
+ };
280
+ const CustomNextButton: React.FC<CustomCalenderButtonProps> = ({
281
+ disabled,
282
+ onClick,
283
+ }) => {
284
+ return (
285
+ <button
286
+ className="ff-calendar-nav-button"
287
+ onClick={onClick}
288
+ disabled={disabled}
289
+ >
290
+ <Icon name="right_arrow_icon" height={12} width={12} />
291
+ </button>
292
+ );
293
+ };
294
+
295
+ // Custom YearGrid Component
296
+ const CustomYearGrid: React.FC = () => {
297
+ return (
298
+ <div className="ff-custom-year_grid">
299
+ {years.map((year: number) => (
300
+ <div
301
+ key={year}
302
+ onClick={() => handleYearSelect(year)}
303
+ className={classNames('ff-custom-year', {
304
+ 'ff-custom-year--selected': year === selectedMonth?.getFullYear(),
305
+ })}
306
+ >
307
+ <Typography>{year}</Typography>
308
+ </div>
309
+ ))}
310
+ </div>
311
+ );
312
+ };
313
+
314
+ return (
315
+ <div className="ff-date-picker" ref={containerRef}>
316
+ <div className="ff-datepicker-input-container">
317
+ <div className="ff-input-with-icon ff-date-input-field">
318
+ <Icon
319
+ name={'calendar_icon'}
320
+ hoverEffect={false}
321
+ className="ff-calendar-icon"
322
+ />
323
+ <input
324
+ type="text"
325
+ value={value ? formatInTimeZone(value, timezone, dateFormat) : ''}
326
+ readOnly
327
+ placeholder={placeholder}
328
+ className="ff-date-input"
329
+ disabled={disabled}
330
+ onClick={handleDateInputClick}
331
+ />
332
+ {helperText && error && (
333
+ <span
334
+ className={classNames('ff-date-input-message', {
335
+ 'ff-date-input-message--danger': !!error,
336
+ })}
337
+ >
338
+ {helperText}
339
+ </span>
340
+ )}
341
+ </div>
342
+
343
+ <div className="ff-input-with-icon ff-time-input-field">
344
+ <Icon
345
+ name={'clock_icon'}
346
+ hoverEffect={false}
347
+ className="ff-clock-icon"
348
+ />
349
+ <input
350
+ type="text"
351
+ placeholder="Select time"
352
+ className="ff-time-input"
353
+ value={value ? formatInTimeZone(value, timezone, timeFormat) : ''}
354
+ disabled={disabled}
355
+ onClick={handleDateInputClick}
356
+ readOnly
357
+ />
358
+ </div>
359
+ </div>
360
+
361
+ {isPickerOpen && (
362
+ <div
363
+ className="ff-date-picker-container"
364
+ ref={pickerRef}
365
+ style={{
366
+ top: datePickerPosition === 'top' ? 'auto' : '110%',
367
+ bottom: datePickerPosition === 'top' ? '110%' : 'auto',
368
+ }}
369
+ >
370
+ <div className="ff-calendar-container">
371
+ <DayPicker
372
+ style={calendarStyle}
373
+ className="ff-calendar"
374
+ mode="single"
375
+ selected={selectedDate}
376
+ onSelect={handleDaySelect}
377
+ month={selectedMonth}
378
+ onMonthChange={(month) => {
379
+ if (view === 'days') {
380
+ setSelectedMonth(month);
381
+ }
382
+ }}
383
+ onNextClick={handleNextClick}
384
+ onPrevClick={handlePrevClick}
385
+ disableNavigation={view === 'months'}
386
+ disabled={[
387
+ {
388
+ before: new Date(minDate ? minDate : ''),
389
+ after: new Date(maxDate ? maxDate : ''),
390
+ },
391
+ ]}
392
+ timeZone={timezone}
393
+ components={{
394
+ CaptionLabel: (props) => <CustomCaption {...props} />,
395
+ PreviousMonthButton: (props) => <CustomPrevButton {...props} />,
396
+ NextMonthButton: (props) => <CustomNextButton {...props} />,
397
+ ...(view === 'months'
398
+ ? {
399
+ MonthGrid: () => <CustomMonthGrid />,
400
+ }
401
+ : {}),
402
+ ...(view === 'years'
403
+ ? {
404
+ MonthGrid: () => <CustomYearGrid />,
405
+ }
406
+ : {}),
407
+ }}
408
+ />
409
+ <TimePicker
410
+ value={timeValue}
411
+ onChange={handleTimeChange}
412
+ minTime={minTime}
413
+ maxTime={maxTime}
414
+ onErrorChange={setTimeError}
415
+ />
416
+ </div>
417
+ <div className="ff-date-picker-controls">
418
+ <Button
419
+ className="ff-date-picker-button"
420
+ variant="secondary"
421
+ onClick={handleCancel}
422
+ label="Cancel"
423
+ />
424
+ <Button
425
+ className="ff-date-picker-button"
426
+ variant="primary"
427
+ onClick={handleSave}
428
+ label="Save"
429
+ disabled={timeError}
430
+ />
431
+ </div>
432
+ </div>
433
+ )}
434
+ </div>
435
+ );
436
+ };
437
+
438
+ export default CustomDatePicker;