pixel-react 1.0.4 → 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- package/lib/assets/utils/functionUtils.d.ts +3 -0
- package/lib/components/DatePicker/DatePicker.d.ts +5 -0
- package/lib/components/DatePicker/DatePicker.stories.d.ts +9 -0
- package/lib/components/DatePicker/Timepicker.d.ts +4 -0
- package/lib/components/DatePicker/index.d.ts +1 -0
- package/lib/components/DatePicker/types.d.ts +81 -0
- package/lib/components/IconButton/IconButton.d.ts +5 -0
- package/lib/components/IconButton/IconButton.stories.d.ts +6 -0
- package/lib/components/IconButton/index.d.ts +1 -0
- package/lib/components/IconButton/types.d.ts +5 -0
- package/lib/components/InputWithDropdown/InputWithDropdown.d.ts +1 -1
- package/lib/components/InputWithDropdown/types.d.ts +3 -7
- package/lib/components/TableTree/TableTree.d.ts +1 -1
- package/lib/index.d.ts +68 -10
- package/lib/index.esm.js +8556 -224
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +8558 -224
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -1
- package/src/assets/Themes/BaseTheme.scss +7 -0
- package/src/assets/Themes/DarkTheme.scss +7 -1
- package/src/assets/icons/add_variable_icon.svg +5 -0
- package/src/assets/icons/calendar_icon.svg +9 -0
- package/src/assets/icons/clock_icon.svg +11 -0
- package/src/assets/icons/info_icon.svg +4 -0
- package/src/assets/icons/left_arrow_icon.svg +3 -0
- package/src/assets/icons/right_arrow_icon.svg +4 -0
- package/src/assets/styles/_mixins.scss +1 -0
- package/src/assets/utils/functionUtils.ts +37 -0
- package/src/components/DatePicker/DatePicker.scss +387 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +161 -0
- package/src/components/DatePicker/DatePicker.tsx +438 -0
- package/src/components/DatePicker/Timepicker.tsx +372 -0
- package/src/components/DatePicker/index.ts +1 -0
- package/src/components/DatePicker/types.ts +100 -0
- package/src/components/Drawer/Drawer.scss +0 -1
- package/src/components/Drawer/Drawer.tsx +10 -14
- package/src/components/Icon/iconList.ts +17 -9
- package/src/components/{AddButton/AddButton.scss → IconButton/IconButton.scss} +6 -2
- package/src/components/IconButton/IconButton.stories.tsx +25 -0
- package/src/components/{AddButton/AddButton.tsx → IconButton/IconButton.tsx} +10 -7
- package/src/components/IconButton/index.ts +1 -0
- package/src/components/{AddButton → IconButton}/types.ts +3 -2
- package/src/components/InputWithDropdown/InputWithDropdown.scss +1 -1
- package/src/components/InputWithDropdown/InputWithDropdown.stories.tsx +10 -13
- package/src/components/InputWithDropdown/InputWithDropdown.tsx +10 -8
- package/src/components/InputWithDropdown/types.ts +4 -7
- package/src/components/RadioButton/RadioButton.scss +3 -3
- package/src/components/Select/Select.scss +1 -1
- package/src/components/StateDropdown/StateDropdown.tsx +10 -15
- package/src/components/TableTree/TableTree.tsx +4 -0
- package/src/index.ts +5 -0
- package/src/assets/icons/expired_license_icon.svg +0 -3
- package/src/assets/icons/expiringSoon_license_icon.svg +0 -3
- package/src/components/AddButton/AddButton.stories.tsx +0 -24
- package/src/components/AddButton/index.ts +0 -1
@@ -0,0 +1,372 @@
|
|
1
|
+
import React, { useState, useRef, useEffect } from 'react';
|
2
|
+
import Typography from '../Typography';
|
3
|
+
import { TimePickerProps } from './types';
|
4
|
+
import Icon from '../Icon';
|
5
|
+
import classNames from 'classnames';
|
6
|
+
import { convertTo24Hour } from '../../assets/utils/functionUtils';
|
7
|
+
import { convertTo12Hour } from '../../assets/utils/functionUtils';
|
8
|
+
import { isValid12HourTime } from '../../assets/utils/functionUtils';
|
9
|
+
|
10
|
+
const TimePicker: React.FC<TimePickerProps> = ({
|
11
|
+
value,
|
12
|
+
onChange,
|
13
|
+
minTime,
|
14
|
+
maxTime,
|
15
|
+
onErrorChange,
|
16
|
+
}) => {
|
17
|
+
const [inputValue, setInputValue] = useState<string>('');
|
18
|
+
const [period, setPeriod] = useState<string | undefined>('AM');
|
19
|
+
const [timeFieldError, setTimeFieldError] = useState<boolean>(false);
|
20
|
+
const [isPeriodDropdownOpen, setIsPeriodDropdownOpen] = useState(false);
|
21
|
+
const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0 });
|
22
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
23
|
+
const periodRef = useRef<HTMLDivElement>(null);
|
24
|
+
|
25
|
+
useEffect(() => {
|
26
|
+
if (value) {
|
27
|
+
const time12h = convertTo12Hour(value);
|
28
|
+
const [time, periodValue] = time12h.split(' ') as [string, 'AM' | 'PM'];
|
29
|
+
setInputValue(time);
|
30
|
+
setPeriod(periodValue);
|
31
|
+
|
32
|
+
setTimeFieldError(!isTimeWithInBounds(time12h, minTime, maxTime));
|
33
|
+
}
|
34
|
+
}, [value, minTime, maxTime]);
|
35
|
+
|
36
|
+
useEffect(() => {
|
37
|
+
const handleClickOutside = (e: MouseEvent) => {
|
38
|
+
if (
|
39
|
+
isPeriodDropdownOpen &&
|
40
|
+
periodRef.current &&
|
41
|
+
!periodRef.current.contains(e.target as Node)
|
42
|
+
) {
|
43
|
+
setIsPeriodDropdownOpen(false);
|
44
|
+
}
|
45
|
+
};
|
46
|
+
document.addEventListener('mousedown', handleClickOutside);
|
47
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
48
|
+
}, [isPeriodDropdownOpen]);
|
49
|
+
|
50
|
+
useEffect(() => {
|
51
|
+
onErrorChange(timeFieldError);
|
52
|
+
}, [timeFieldError]);
|
53
|
+
|
54
|
+
const generateTimeOptions = (): string[] => {
|
55
|
+
const options: string[] = [];
|
56
|
+
const start = 0;
|
57
|
+
const end = 23 * 60 + 45;
|
58
|
+
const interval = 15;
|
59
|
+
|
60
|
+
for (let i = start; i <= end; i += interval) {
|
61
|
+
const hours = Math.floor(i / 60);
|
62
|
+
const minutes = i % 60;
|
63
|
+
const period = hours >= 12 ? 'PM' : 'AM';
|
64
|
+
const displayHour = hours % 12 || 12;
|
65
|
+
const displayMinutes = minutes < 10 ? '0' + minutes : minutes;
|
66
|
+
options.push(`${displayHour}:${displayMinutes} ${period}`);
|
67
|
+
}
|
68
|
+
return options;
|
69
|
+
};
|
70
|
+
|
71
|
+
const timeOptions = generateTimeOptions();
|
72
|
+
|
73
|
+
const isTimeWithInBounds = (
|
74
|
+
time: string,
|
75
|
+
minTime?: string,
|
76
|
+
maxTime?: string
|
77
|
+
): boolean => {
|
78
|
+
const [hours = 0, minutes = 0] = convertTo24Hour(time)
|
79
|
+
.split(':')
|
80
|
+
.map(Number);
|
81
|
+
const timeValue = hours * 60 + minutes;
|
82
|
+
|
83
|
+
if (minTime) {
|
84
|
+
const [minHours = 0, minMinutes = 0] = minTime.split(':').map(Number);
|
85
|
+
const minValue = minHours * 60 + minMinutes;
|
86
|
+
if (timeValue < minValue) return false;
|
87
|
+
}
|
88
|
+
|
89
|
+
if (maxTime) {
|
90
|
+
const [maxHours = 0, maxMinutes = 0] = maxTime.split(':').map(Number);
|
91
|
+
const maxValue = maxHours * 60 + maxMinutes;
|
92
|
+
if (timeValue > maxValue) return false;
|
93
|
+
}
|
94
|
+
|
95
|
+
return true;
|
96
|
+
};
|
97
|
+
|
98
|
+
const handleTimeInput = (event: React.ChangeEvent<HTMLInputElement>) => {
|
99
|
+
let [hourSection, minuteSection] = inputValue.split(':');
|
100
|
+
let [newHour, newMinute] = event.target.value.split(':');
|
101
|
+
const selectionStart = inputRef.current?.selectionStart ?? 0;
|
102
|
+
|
103
|
+
// Handle hour part input
|
104
|
+
if (selectionStart <= 2) {
|
105
|
+
const currentHour = parseInt(hourSection ?? '0') || 0;
|
106
|
+
|
107
|
+
if (newHour?.length === 1) {
|
108
|
+
hourSection = currentHour
|
109
|
+
? hourSection?.slice(-1) + newHour
|
110
|
+
: `0${newHour}`; // Single digit input
|
111
|
+
} else {
|
112
|
+
hourSection = newHour?.padStart(2, '0'); // Two digits for hour
|
113
|
+
}
|
114
|
+
|
115
|
+
// Ensure hour part is valid (between 1 and 12)
|
116
|
+
let parsedHour = parseInt(hourSection ?? '0') || 0;
|
117
|
+
if (parsedHour > 12) {
|
118
|
+
parsedHour = parseInt(newHour ?? '0'); // Fix if hour exceeds 12
|
119
|
+
}
|
120
|
+
|
121
|
+
hourSection = parsedHour.toString().padStart(2, '0');
|
122
|
+
|
123
|
+
// Move to minute part if hour is greater than 1, otherwise stay on hour
|
124
|
+
if (parsedHour > 1) {
|
125
|
+
setTimeout(() => {
|
126
|
+
inputRef.current?.setSelectionRange(3, 5); // Move to minute part
|
127
|
+
}, 0);
|
128
|
+
} else {
|
129
|
+
setTimeout(() => {
|
130
|
+
inputRef.current?.setSelectionRange(0, 2); // Keep cursor on hour part
|
131
|
+
}, 0);
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
// Handle minute part input
|
136
|
+
if (selectionStart >= 3) {
|
137
|
+
const currentMinute = parseInt(minuteSection ?? '0') || 0;
|
138
|
+
|
139
|
+
if (newMinute?.length === 1) {
|
140
|
+
minuteSection = currentMinute
|
141
|
+
? minuteSection?.slice(-1) + newMinute
|
142
|
+
: `0${newMinute}`; // Single digit input
|
143
|
+
} else {
|
144
|
+
minuteSection = newMinute?.padStart(2, '0'); // Two digits for minute
|
145
|
+
}
|
146
|
+
|
147
|
+
// Ensure minute part is valid (between 00 and 59)
|
148
|
+
let parsedMinute = parseInt(minuteSection ?? '0') || 0;
|
149
|
+
if (parsedMinute > 59) {
|
150
|
+
parsedMinute = parseInt(newMinute ?? '0'); // Fix if minute exceeds 59
|
151
|
+
}
|
152
|
+
|
153
|
+
minuteSection = parsedMinute.toString().padStart(2, '0');
|
154
|
+
|
155
|
+
if (parsedMinute < 6) {
|
156
|
+
setTimeout(() => {
|
157
|
+
inputRef.current?.setSelectionRange(3, 5); // Keep cursor on minute part
|
158
|
+
}, 0);
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
setInputValue(`${hourSection}:${minuteSection}`);
|
163
|
+
if (isValid12HourTime(`${hourSection}:${minuteSection}`)) {
|
164
|
+
const time24h = convertTo24Hour(
|
165
|
+
`${hourSection}:${minuteSection} ${period}`
|
166
|
+
);
|
167
|
+
onChange(time24h);
|
168
|
+
setTimeFieldError(false);
|
169
|
+
} else {
|
170
|
+
setTimeFieldError(true);
|
171
|
+
}
|
172
|
+
};
|
173
|
+
|
174
|
+
const togglePeriodDropdown = () => {
|
175
|
+
setIsPeriodDropdownOpen((prev) => !prev);
|
176
|
+
if (periodRef.current) {
|
177
|
+
const { top, left, height } = periodRef.current.getBoundingClientRect();
|
178
|
+
setDropdownPosition({ top: top + height, left: left });
|
179
|
+
}
|
180
|
+
};
|
181
|
+
|
182
|
+
const handlePeriodChange = (option: string) => {
|
183
|
+
setPeriod(option);
|
184
|
+
const time24h = convertTo24Hour(`${inputValue} ${option}`);
|
185
|
+
onChange(time24h);
|
186
|
+
setIsPeriodDropdownOpen(false);
|
187
|
+
};
|
188
|
+
|
189
|
+
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
190
|
+
const { key } = event;
|
191
|
+
const selectionStart = inputRef.current?.selectionStart ?? 0;
|
192
|
+
const selectionEnd = inputRef.current?.selectionEnd ?? 0;
|
193
|
+
let [hourSection, minuteSection] = inputValue.split(':');
|
194
|
+
|
195
|
+
if (key === 'Backspace' && selectionStart <= 2) {
|
196
|
+
event.preventDefault();
|
197
|
+
hourSection = 'HH';
|
198
|
+
setInputValue(`${hourSection}:${minuteSection}`);
|
199
|
+
setTimeout(() => inputRef.current?.setSelectionRange(0, 2), 0);
|
200
|
+
setTimeFieldError(true);
|
201
|
+
}
|
202
|
+
|
203
|
+
if (key === 'Backspace' && selectionStart >= 3) {
|
204
|
+
event.preventDefault();
|
205
|
+
minuteSection = 'MM';
|
206
|
+
setInputValue(`${hourSection}:${minuteSection}`);
|
207
|
+
setTimeout(() => inputRef.current?.setSelectionRange(3, 5), 0);
|
208
|
+
setTimeFieldError(true);
|
209
|
+
}
|
210
|
+
|
211
|
+
if (key === 'ArrowRight') {
|
212
|
+
if (selectionEnd === 2) {
|
213
|
+
event.preventDefault();
|
214
|
+
inputRef.current?.setSelectionRange(3, 5);
|
215
|
+
} else if (selectionStart === 0) {
|
216
|
+
event.preventDefault();
|
217
|
+
inputRef.current?.setSelectionRange(0, 2);
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
if (key === 'ArrowLeft') {
|
222
|
+
if (selectionStart === 3) {
|
223
|
+
event.preventDefault();
|
224
|
+
inputRef.current?.setSelectionRange(0, 2);
|
225
|
+
} else if (selectionStart === 5) {
|
226
|
+
event.preventDefault();
|
227
|
+
inputRef.current?.setSelectionRange(3, 5);
|
228
|
+
}
|
229
|
+
}
|
230
|
+
};
|
231
|
+
|
232
|
+
const handleFocus = () => {
|
233
|
+
if (inputValue === '') {
|
234
|
+
setInputValue('HH:MM');
|
235
|
+
}
|
236
|
+
setTimeout(() => {
|
237
|
+
inputRef.current?.setSelectionRange(0, 2);
|
238
|
+
}, 0);
|
239
|
+
};
|
240
|
+
|
241
|
+
const handleBlur = () => {
|
242
|
+
if (inputValue === 'HH:MM') {
|
243
|
+
setInputValue('');
|
244
|
+
}
|
245
|
+
};
|
246
|
+
|
247
|
+
const handleClick = () => {
|
248
|
+
const selectionStart = inputRef.current?.selectionStart ?? 0;
|
249
|
+
if (selectionStart >= 3) {
|
250
|
+
inputRef.current?.setSelectionRange(3, 5);
|
251
|
+
} else {
|
252
|
+
inputRef.current?.setSelectionRange(0, 2);
|
253
|
+
}
|
254
|
+
};
|
255
|
+
|
256
|
+
const handleTimeOptionClick = (time: string) => {
|
257
|
+
const [timeValue, periodValue] = time.split(' ') as [string, 'AM' | 'PM'];
|
258
|
+
setInputValue(timeValue);
|
259
|
+
setPeriod(periodValue);
|
260
|
+
const time24h = convertTo24Hour(`${timeValue} ${periodValue}`);
|
261
|
+
onChange(time24h);
|
262
|
+
setTimeFieldError(false);
|
263
|
+
};
|
264
|
+
|
265
|
+
return (
|
266
|
+
<div className="ff-time-picker-container">
|
267
|
+
<div className="ff-time-picker-fields">
|
268
|
+
<div
|
269
|
+
className={classNames(
|
270
|
+
'ff-time-input-container',
|
271
|
+
{ 'ff-time-input-container--float': !!inputValue },
|
272
|
+
{ 'ff-time-input-container--danger': !!timeFieldError }
|
273
|
+
)}
|
274
|
+
>
|
275
|
+
<Typography
|
276
|
+
as="label"
|
277
|
+
htmlFor="time"
|
278
|
+
className={classNames('ff-time-input-label', {
|
279
|
+
'ff-time-input-label--danger': !!timeFieldError,
|
280
|
+
})}
|
281
|
+
>
|
282
|
+
Time
|
283
|
+
</Typography>
|
284
|
+
<input
|
285
|
+
name="time"
|
286
|
+
ref={inputRef}
|
287
|
+
type="text"
|
288
|
+
id="time-input"
|
289
|
+
value={inputValue}
|
290
|
+
inputMode="numeric"
|
291
|
+
placeholder="HH:MM"
|
292
|
+
onChange={handleTimeInput}
|
293
|
+
onKeyDown={handleKeyDown}
|
294
|
+
onFocus={handleFocus}
|
295
|
+
onClick={handleClick}
|
296
|
+
onBlur={handleBlur}
|
297
|
+
maxLength={5}
|
298
|
+
className={classNames('ff-time-input', {
|
299
|
+
'ff-time-input--danger': !!timeFieldError,
|
300
|
+
})}
|
301
|
+
/>
|
302
|
+
</div>
|
303
|
+
<div
|
304
|
+
className={classNames('ff-time-period-container', {
|
305
|
+
'ff-time-period-container--active': !!isPeriodDropdownOpen,
|
306
|
+
})}
|
307
|
+
ref={periodRef}
|
308
|
+
>
|
309
|
+
<div
|
310
|
+
onClick={togglePeriodDropdown}
|
311
|
+
className="ff-time-period-select"
|
312
|
+
aria-haspopup="listbox"
|
313
|
+
aria-expanded={isPeriodDropdownOpen}
|
314
|
+
>
|
315
|
+
<Typography>{period}</Typography>
|
316
|
+
<Icon
|
317
|
+
name={isPeriodDropdownOpen ? 'arrow_up' : 'arrow_down'}
|
318
|
+
className="ff-time-period-icon"
|
319
|
+
height={12}
|
320
|
+
width={12}
|
321
|
+
/>
|
322
|
+
</div>
|
323
|
+
|
324
|
+
{isPeriodDropdownOpen && (
|
325
|
+
<ul
|
326
|
+
className="ff-time-period-options"
|
327
|
+
style={{
|
328
|
+
top: `${dropdownPosition.top}px`,
|
329
|
+
left: `${dropdownPosition.left}px`,
|
330
|
+
}}
|
331
|
+
role="listbox"
|
332
|
+
>
|
333
|
+
<Typography
|
334
|
+
as="li"
|
335
|
+
className="ff-option-item"
|
336
|
+
onClick={() => handlePeriodChange('AM')}
|
337
|
+
aria-selected={period === 'AM'}
|
338
|
+
>
|
339
|
+
AM
|
340
|
+
</Typography>
|
341
|
+
<Typography
|
342
|
+
as="li"
|
343
|
+
className="ff-option-item"
|
344
|
+
onClick={() => handlePeriodChange('PM')}
|
345
|
+
aria-selected={period === 'PM'}
|
346
|
+
>
|
347
|
+
PM
|
348
|
+
</Typography>
|
349
|
+
</ul>
|
350
|
+
)}
|
351
|
+
</div>
|
352
|
+
</div>
|
353
|
+
<div className="ff-time-picker-options">
|
354
|
+
{timeOptions
|
355
|
+
.filter((time) => isTimeWithInBounds(time, minTime, maxTime))
|
356
|
+
.map((time, index) => (
|
357
|
+
<div
|
358
|
+
key={index}
|
359
|
+
className="ff-time-option"
|
360
|
+
onClick={() => handleTimeOptionClick(time)}
|
361
|
+
>
|
362
|
+
<Typography lineHeight="18px" textAlign="center">
|
363
|
+
{time}
|
364
|
+
</Typography>
|
365
|
+
</div>
|
366
|
+
))}
|
367
|
+
</div>
|
368
|
+
</div>
|
369
|
+
);
|
370
|
+
};
|
371
|
+
|
372
|
+
export default TimePicker;
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default } from './DatePicker';
|
@@ -0,0 +1,100 @@
|
|
1
|
+
export interface DatePickerProps {
|
2
|
+
/**
|
3
|
+
* The minimum selectable date.
|
4
|
+
*/
|
5
|
+
minDate?: DateValue;
|
6
|
+
|
7
|
+
/**
|
8
|
+
* The maximum selectable date.
|
9
|
+
*/
|
10
|
+
maxDate?: DateValue;
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Selected date value.
|
14
|
+
*/
|
15
|
+
value?: DateValue;
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Function to handle date selection.
|
19
|
+
*/
|
20
|
+
onChange: (value: DateValue) => void;
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Placeholder text for the input field.
|
24
|
+
*/
|
25
|
+
placeholder?: string;
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Disables the date picker.
|
29
|
+
*/
|
30
|
+
disabled?: boolean;
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Format for displaying the selected date. Default is 'EEEE, dd MMM yyyy'.
|
34
|
+
*/
|
35
|
+
dateFormat?: string;
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Format for displaying the selected time. Default is 'hh:mm a'.
|
39
|
+
*/
|
40
|
+
timeFormat?: string;
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Timezone for the date picker.
|
44
|
+
*/
|
45
|
+
timezone?: string;
|
46
|
+
|
47
|
+
/**
|
48
|
+
* Custom width for the calendar. This will override the default width of the calendar.
|
49
|
+
*/
|
50
|
+
calendarWidth?: number;
|
51
|
+
|
52
|
+
/**
|
53
|
+
* When true, displays the input field error.
|
54
|
+
*/
|
55
|
+
error?: boolean;
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Helper text to display below the input field, used for error messages or instructions.
|
59
|
+
*/
|
60
|
+
helperText?: string | undefined;
|
61
|
+
}
|
62
|
+
|
63
|
+
export type DateValue = Date | undefined;
|
64
|
+
|
65
|
+
export interface CustomCaptionProps {
|
66
|
+
children?: React.ReactNode; // You can use ReactNode as a fallback for dynamic content
|
67
|
+
}
|
68
|
+
|
69
|
+
export interface CustomCalenderButtonProps {
|
70
|
+
className?: string;
|
71
|
+
disabled?: boolean | undefined;
|
72
|
+
onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
73
|
+
}
|
74
|
+
|
75
|
+
export interface TimePickerProps {
|
76
|
+
/**
|
77
|
+
* The current time value, in string format (e.g., '14:30').
|
78
|
+
*/
|
79
|
+
value: string;
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Function to handle time selection changes, receiving the selected time as a string.
|
83
|
+
*/
|
84
|
+
onChange: (time: string) => void;
|
85
|
+
|
86
|
+
/**
|
87
|
+
* The minimum selectable time, used to restrict the earliest selectable time.
|
88
|
+
*/
|
89
|
+
minTime?: string;
|
90
|
+
|
91
|
+
/**
|
92
|
+
* The maximum selectable time, used to restrict the latest selectable time.
|
93
|
+
*/
|
94
|
+
maxTime?: string;
|
95
|
+
|
96
|
+
/**
|
97
|
+
* Function to handle error state changes, receiving a boolean indicating the presence of an error.
|
98
|
+
*/
|
99
|
+
onErrorChange: (error: boolean) => void;
|
100
|
+
}
|
@@ -3,10 +3,10 @@ import { DrawerProps } from './Types.js';
|
|
3
3
|
import Button from '../Button/Button.js';
|
4
4
|
import classNames from 'classnames';
|
5
5
|
import './Drawer.scss';
|
6
|
-
import { useEffect, useState } from 'react';
|
6
|
+
import { useContext, useEffect, useState } from 'react';
|
7
7
|
import Icon from '../Icon';
|
8
8
|
import useEscapeKey from '../../hooks/keyboardevents/useEscKeyEvent.js';
|
9
|
-
|
9
|
+
import { ThemeContext } from '../ThemeProvider/ThemeProvider.js';
|
10
10
|
const Drawer = ({
|
11
11
|
isOpen = true,
|
12
12
|
children = 'Drawer content area',
|
@@ -57,9 +57,10 @@ const Drawer = ({
|
|
57
57
|
};
|
58
58
|
|
59
59
|
const drawerSize = isExpanded ? 'x-large' : size;
|
60
|
-
|
60
|
+
const themeContext = useContext(ThemeContext);
|
61
|
+
const currentTheme = themeContext?.currentTheme;
|
61
62
|
return createPortal(
|
62
|
-
<div className=
|
63
|
+
<div className={classNames('ff-drawer-container', currentTheme)}>
|
63
64
|
{overlay && <div className="ff-overlay" />}
|
64
65
|
|
65
66
|
<div
|
@@ -102,16 +103,11 @@ const Drawer = ({
|
|
102
103
|
<div className="ff-drawer-title">{title}</div>
|
103
104
|
</div>
|
104
105
|
{_isCloseModalButtonVisible && (
|
105
|
-
<
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
width={16}
|
111
|
-
hoverEffect={false}
|
112
|
-
/>
|
113
|
-
</button>
|
114
|
-
</div>
|
106
|
+
<Icon
|
107
|
+
name="close"
|
108
|
+
hoverEffect={false}
|
109
|
+
onClick={onClose}
|
110
|
+
/>
|
115
111
|
)}
|
116
112
|
</div>
|
117
113
|
</div>
|
@@ -29,23 +29,27 @@ import WrongMarkIcon from '../../assets/icons/wrong_mark.svg?react';
|
|
29
29
|
import Tick from '../../assets/Icons/tick_icon.svg?react';
|
30
30
|
import Search from '../../assets/icons/search.svg?react';
|
31
31
|
import Filter from '../../assets/icons/filter.svg?react';
|
32
|
+
import RightArrow from '../../assets/icons/right_arrow_icon.svg?react';
|
33
|
+
import LeftArrow from '../../assets/icons/left_arrow_icon.svg?react';
|
34
|
+
import ClockIcon from '../../assets/icons/clock_icon.svg?react';
|
32
35
|
import Edit from '../../assets/icons/edit_icon.svg?react';
|
33
36
|
import ViewIcon from '../../assets/icons/view_icon.svg?react';
|
34
|
-
import HideIcon from '../../assets/icons/hide_icon.svg?react';
|
35
37
|
import ExportCollection from '../../assets/icons/export_collection_icon.svg?react';
|
36
38
|
import RunIcon from '../../assets/icons/run_icon.svg?react';
|
39
|
+
import AddVariable from '../../assets/icons/add_variable_icon.svg?react';
|
37
40
|
|
38
|
-
import
|
39
|
-
import ExpiredLicenseIcon from '../../assets/icons/expired_license_icon.svg?react';
|
40
|
-
import ExpiringSoonLicenseIcon from '../../assets/icons/expiringSoon_license_icon.svg?react';
|
41
|
+
import LicenseIcon from '../../assets/icons/active_license_icon.svg?react';
|
41
42
|
import DeleteIcon from '../../assets/icons/delete.svg?react';
|
42
43
|
import DetailsIcon from '../../assets/icons/details.svg?react';
|
43
44
|
import ImpactListIcon from '../../assets/icons/impactList.svg?react';
|
45
|
+
import InfoIcon from '../../assets/icons/info_icon.svg?react';
|
46
|
+
import CalendarIcon from '../../assets/icons/calendar_icon.svg?react';
|
47
|
+
import HideIcon from '../../assets/icons/hide_icon.svg?react';
|
44
48
|
//icons
|
45
49
|
Components['delete_info'] = DeleteInfoIcon;
|
46
50
|
Components['success'] = ToastSuccessIcon;
|
47
51
|
Components['warning'] = WarningIcon;
|
48
|
-
Components['
|
52
|
+
Components['toast_info'] = ToastInfoIcon;
|
49
53
|
Components['error'] = ToastErrorIcon;
|
50
54
|
Components['close'] = ToastCloseIcon;
|
51
55
|
Components['more'] = MoreIcon;
|
@@ -71,16 +75,20 @@ Components['tick'] = Tick;
|
|
71
75
|
Components['arrow_right'] = ArrowRight;
|
72
76
|
Components['search'] = Search;
|
73
77
|
Components['filter'] = Filter;
|
78
|
+
Components['right_arrow_icon'] = RightArrow;
|
79
|
+
Components['left_arrow_icon'] = LeftArrow;
|
80
|
+
Components['clock_icon'] = ClockIcon;
|
74
81
|
Components['edit'] = Edit;
|
75
82
|
Components['view_icon'] = ViewIcon;
|
76
|
-
Components['hide_icon'] = HideIcon;
|
77
83
|
Components['export_collection'] = ExportCollection;
|
78
84
|
Components['run_icon'] = RunIcon;
|
79
|
-
Components['
|
80
|
-
Components['expired_license_icon'] = ExpiredLicenseIcon;
|
81
|
-
Components['expiringSoon_license_icon'] = ExpiringSoonLicenseIcon;
|
85
|
+
Components['license'] = LicenseIcon;
|
82
86
|
Components['delete'] = DeleteIcon;
|
83
87
|
Components['details'] = DetailsIcon;
|
84
88
|
Components['impactList'] = ImpactListIcon;
|
89
|
+
Components['add_variable'] = AddVariable;
|
90
|
+
Components['info'] = InfoIcon;
|
91
|
+
Components['calendar_icon'] = CalendarIcon;
|
92
|
+
Components['hide_icon'] = HideIcon;
|
85
93
|
|
86
94
|
export default Components;
|
@@ -15,7 +15,9 @@
|
|
15
15
|
padding: 2px 8px 2px 2px;
|
16
16
|
border: 1px solid transparent;
|
17
17
|
.ff-icon-color {
|
18
|
-
|
18
|
+
path {
|
19
|
+
color: var(--primary-icon-color);
|
20
|
+
}
|
19
21
|
}
|
20
22
|
.icon-name {
|
21
23
|
min-width: 33px;
|
@@ -32,7 +34,9 @@
|
|
32
34
|
color: var(--secondary-icon-color);
|
33
35
|
}
|
34
36
|
.ff-icon-color {
|
35
|
-
|
37
|
+
path {
|
38
|
+
color: var(--secondary-icon-color);
|
39
|
+
}
|
36
40
|
}
|
37
41
|
}
|
38
42
|
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
2
|
+
import IconButton from './IconButton';
|
3
|
+
|
4
|
+
const meta: Meta<typeof IconButton> = {
|
5
|
+
title: 'Components/IconButton',
|
6
|
+
component: IconButton,
|
7
|
+
parameters: {
|
8
|
+
layout: 'centered',
|
9
|
+
},
|
10
|
+
tags: ['autodocs'],
|
11
|
+
};
|
12
|
+
|
13
|
+
type Story = StoryObj<typeof IconButton>;
|
14
|
+
|
15
|
+
export const PrimaryIconButton: Story = {
|
16
|
+
render: () => {
|
17
|
+
const name = 'Project';
|
18
|
+
const icon = 'plus_icon';
|
19
|
+
const onClick = () => {};
|
20
|
+
|
21
|
+
return <IconButton label={name} iconName={icon} onClick={onClick} />;
|
22
|
+
},
|
23
|
+
};
|
24
|
+
|
25
|
+
export default meta;
|
@@ -1,25 +1,28 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import Icon from '../Icon';
|
3
|
-
import './
|
3
|
+
import './IconButton.scss';
|
4
4
|
import classNames from 'classnames';
|
5
5
|
import Typography from '../Typography';
|
6
|
-
import {
|
6
|
+
import { IconButtonProps } from './types';
|
7
7
|
|
8
|
-
const
|
8
|
+
const IconButton: React.FC<IconButtonProps> = ({
|
9
|
+
label,
|
10
|
+
iconName = 'plus_icon',
|
11
|
+
onClick,
|
12
|
+
}) => {
|
9
13
|
return (
|
10
14
|
<button onClick={onClick} className={classNames('ff-plus-icon')}>
|
11
15
|
<Icon
|
12
16
|
height={20}
|
13
17
|
width={20}
|
14
|
-
name={
|
15
|
-
color=''
|
18
|
+
name={iconName}
|
16
19
|
className={'ff-icon-color'}
|
17
20
|
/>
|
18
21
|
<Typography as="div" textAlign="center" className="icon-name">
|
19
|
-
{
|
22
|
+
{label}
|
20
23
|
</Typography>
|
21
24
|
</button>
|
22
25
|
);
|
23
26
|
};
|
24
27
|
|
25
|
-
export default
|
28
|
+
export default IconButton;
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default } from './IconButton';
|