torch-glare 1.1.4 → 1.1.6

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.
@@ -3,6 +3,7 @@ import {
3
3
  forwardRef,
4
4
  InputHTMLAttributes,
5
5
  ReactNode,
6
+ useEffect,
6
7
  useRef,
7
8
  useState,
8
9
  } from "react";
@@ -15,7 +16,6 @@ import { useClickOutside } from "../hooks/useClickOutside";
15
16
  import { Badge } from "./Badge";
16
17
  import { Tag, useTagSelection } from "../hooks/useTagSelection";
17
18
 
18
-
19
19
  interface Props
20
20
  extends Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "variant"> {
21
21
  size?: "XS" | "S" | "M"; // this is used to change the size style of the component
@@ -26,13 +26,12 @@ interface Props
26
26
  toolTipSide?: ToolTipSide;
27
27
  label?: string;
28
28
  required?: boolean;
29
- theme?: Themes
30
- actionButton?: ReactNode
31
- tags: Tag[]
32
- onValueChange?: (tags: Tag[]) => void
29
+ theme?: Themes;
30
+ actionButton?: ReactNode;
31
+ tags: Tag[];
32
+ onValueChange?: (tags: Tag[]) => void;
33
33
  }
34
34
 
35
-
36
35
  export const BadgeField = forwardRef<HTMLInputElement, Props>(
37
36
  (
38
37
  {
@@ -48,7 +47,6 @@ export const BadgeField = forwardRef<HTMLInputElement, Props>(
48
47
  actionButton,
49
48
  theme,
50
49
  tags,
51
- onValueChange,
52
50
  children,
53
51
  ...props
54
52
  },
@@ -62,7 +60,8 @@ export const BadgeField = forwardRef<HTMLInputElement, Props>(
62
60
  if (
63
61
  !inputGroupRef?.current?.contains(e?.target as Node) &&
64
62
  !popoverContentRef?.current?.contains(e?.target as Node)
65
- ) setIsPopoverOpen(false);
63
+ )
64
+ setIsPopoverOpen(false);
66
65
  else setIsPopoverOpen(true);
67
66
  });
68
67
 
@@ -79,7 +78,16 @@ export const BadgeField = forwardRef<HTMLInputElement, Props>(
79
78
  focusedPopoverIndex,
80
79
  isPopoverOpen,
81
80
  setIsPopoverOpen,
82
- } = useTagSelection({ Tags: tags, onTagsChange: onValueChange, inputRef });
81
+ searchTags
82
+ } = useTagSelection({
83
+ Tags: tags,
84
+ onTagsChange: (e) => props.onChange?.({
85
+ target: {
86
+ value: e
87
+ }
88
+ } as any),
89
+ inputRef
90
+ });
83
91
 
84
92
  return (
85
93
  <Popover open={isPopoverOpen}>
@@ -88,8 +96,7 @@ export const BadgeField = forwardRef<HTMLInputElement, Props>(
88
96
  open={errorMessage !== undefined}
89
97
  text={errorMessage}
90
98
  >
91
- <PopoverTrigger asChild
92
- >
99
+ <PopoverTrigger asChild>
93
100
  <Group
94
101
  error={errorMessage !== undefined}
95
102
  onTable={onTable}
@@ -102,44 +109,42 @@ export const BadgeField = forwardRef<HTMLInputElement, Props>(
102
109
  onFocus={(e: any) => {
103
110
  setDropDownListWidth(e.currentTarget.offsetWidth);
104
111
  }}
105
- className={cn("flex gap-1 flex-row w-full relative p-1 flex-nowrap overflow-hidden justify-end h-fit items-center",
112
+ className={cn(
113
+ "flex gap-1 flex-row w-full relative p-1 flex-nowrap overflow-hidden justify-end items-center",
106
114
  {
107
115
  "flex-wrap justify-start": isPopoverOpen,
116
+ "h-fit": isPopoverOpen,
108
117
  },
109
118
  className
110
119
  )}
111
120
  >
112
- {icon && (
113
- <Icon >
114
- {icon}
115
- </Icon>
116
- )}
121
+ {icon && <Icon>{icon}</Icon>}
117
122
 
118
- {
119
- selectedTagsStack.map((tag, index) => (
120
- <Badge
121
- key={tag.id}
122
- size={size}
123
- variant={tag.variant as any}
124
- label={tag.name}
125
- isSelected={true}
126
- onUnselect={() => handleUnselectTag(tag.id)}
127
- className={focusedTagIndex === index ? "ring-2 ring-blue-500" : ""}
128
- tabIndex={focusedTagIndex === index ? 0 : -1}
129
- />
130
- ))
131
- }
123
+ {selectedTagsStack.map((tag, index) => (
124
+ <Badge
125
+ key={tag.id}
126
+ size={size}
127
+ variant={tag.variant as any}
128
+ label={tag.name}
129
+ isSelected={true}
130
+ onUnselect={() => handleUnselectTag(tag.id)}
131
+ className={
132
+ focusedTagIndex === index ? "ring-2 ring-blue-500" : ""
133
+ }
134
+ tabIndex={focusedTagIndex === index ? 0 : -1}
135
+ />
136
+ ))}
132
137
 
133
138
  <Input
134
139
  {...props}
140
+ value={searchTags}
135
141
  onChange={(e) => {
136
- props.onChange?.(e)
137
- filterTagsBySearch(e.target.value)
142
+ filterTagsBySearch(e.target.value);
138
143
  }}
139
144
  onFocus={(e) => {
140
- props.onFocus?.(e)
145
+ props.onFocus?.(e);
141
146
  setFocusedTagIndex(null);
142
- setIsPopoverOpen(true)
147
+ setIsPopoverOpen(true);
143
148
  }}
144
149
  ref={inputRef}
145
150
  className={cn(
@@ -151,13 +156,12 @@ export const BadgeField = forwardRef<HTMLInputElement, Props>(
151
156
  }
152
157
  )}
153
158
  />
154
- {
155
- actionButton && (
156
- <Trilling className="py-0" > {/* Keep the ActionButton right aligned */}
157
- {actionButton}
158
- </Trilling>
159
- )
160
- }
159
+ {actionButton && (
160
+ <Trilling className="py-0">
161
+ {/* Keep the ActionButton right aligned */}
162
+ {actionButton}
163
+ </Trilling>
164
+ )}
161
165
  </Group>
162
166
  </PopoverTrigger>
163
167
  </Tooltip>
@@ -178,13 +182,16 @@ export const BadgeField = forwardRef<HTMLInputElement, Props>(
178
182
  variant={tag.variant as any}
179
183
  label={tag.name}
180
184
  onClick={() => handleSelectTag(tag.id)}
181
- className={`outline-none ${focusedPopoverIndex === index ? "ring-2 ring-blue-500" : ""} ${index !== 0 ? "mt-1" : ""}`}
185
+ className={`outline-none ${focusedPopoverIndex === index ? "ring-2 ring-blue-500" : ""
186
+ } ${index !== 0 ? "mt-1" : ""}`}
182
187
  tabIndex={focusedPopoverIndex === index ? 0 : -1}
183
188
  />
184
189
  ))
185
190
  ) : (
186
191
  <div className="text-sm text-gray-500 py-1 px-2">
187
- {tags.length === 0 ? "All tags selected" : "No matching tags found"}
192
+ {tags.length === 0
193
+ ? "All tags selected"
194
+ : "No matching tags found"}
188
195
  </div>
189
196
  )}
190
197
  </>
@@ -9,7 +9,7 @@ import { SimpleSelectValue, SimpleSelectItem } from "./SimpleSelect";
9
9
 
10
10
  export type CalendarProps = React.ComponentProps<typeof DayPicker>
11
11
 
12
- const Calender = ({
12
+ const Calendar = ({
13
13
  className,
14
14
  classNames,
15
15
  captionLayout = "dropdown",
@@ -86,8 +86,8 @@ const Calender = ({
86
86
  range_start: `rounded-l-[8px] bg-background-system-action-secondary-hover`,
87
87
  range_end: `rounded-r-[8px] bg-background-system-action-secondary-hover`,
88
88
  range_middle: `bg-background-system-action-secondary-hover [&_button]:!border-none [&_button]:!bg-transparent`,
89
- weekdays: `text-content-presentation-global-highlight-darkback text-[12px]`,
90
- week_number: `text-content-presentation-global-highlight-darkback text-[12px] px-1`,
89
+ weekdays: `text-content-presentation-global-primary text-[12px]`,
90
+ week_number: `text-content-presentation-global-primary text-[12px] px-1`,
91
91
  month_caption: `flex items-center justify-center`,
92
92
  caption_label: `text-content-presentation-global-primary text-[14px] pt-[2px]`,
93
93
  months: `flex items-end justify-center gap-4 flex-wrap`,
@@ -97,6 +97,6 @@ const Calender = ({
97
97
  />
98
98
  )
99
99
  }
100
- Calender.displayName = "Calender"
100
+ Calendar.displayName = "Calendar"
101
101
 
102
- export { Calender }
102
+ export { Calendar }
@@ -1,15 +1,28 @@
1
- import React, { useState, forwardRef, ForwardedRef, HTMLAttributes, useEffect, cloneElement, isValidElement } from 'react';
2
- import { Popover, PopoverContent, PopoverTrigger } from './Popover';
3
- import { Calender } from './Calender';
4
- import { Input, Trilling } from './Input';
5
- import { ActionButton } from './ActionButton';
6
- import { Group } from './Input'; import { DateRange } from 'react-day-picker';
7
- import Picker, { PickerValue } from 'torch-react-mobile-picker';
1
+ import React, {
2
+ useState,
3
+ forwardRef,
4
+ ForwardedRef,
5
+ HTMLAttributes,
6
+ useEffect,
7
+ cloneElement,
8
+ isValidElement,
9
+ } from "react";
10
+ import { Popover, PopoverContent, PopoverTrigger } from "./Popover";
11
+ import { Calendar } from "./Calendar";
12
+ import { Input, Trilling } from "./Input";
13
+ import { ActionButton } from "./ActionButton";
14
+ import { Group } from "./Input";
15
+ import { DateRange } from "react-day-picker";
16
+ import Picker, { PickerValue } from "torch-react-mobile-picker";
8
17
 
9
- import { applyTimeToDateValue, formatDateValueToString, TimePickerValue } from '../utils/dateFormat';
10
- import { InputField } from './InputField';
18
+ import {
19
+ applyTimeToDateValue,
20
+ formatDateValueToString,
21
+ TimePickerValue,
22
+ } from "../utils/dateFormat";
23
+ import { InputField } from "./InputField";
11
24
 
12
- export type CalendarProps = React.ComponentProps<typeof Calender>
25
+ export type CalendarProps = React.ComponentProps<typeof Calendar>;
13
26
 
14
27
  interface DatePickerProps extends HTMLAttributes<HTMLInputElement> {
15
28
  mode?: "single" | "multiple" | "range";
@@ -22,75 +35,91 @@ interface DatePickerProps extends HTMLAttributes<HTMLInputElement> {
22
35
  dateFormat?: string;
23
36
  calendarProps?: CalendarProps;
24
37
  timePicker?: boolean;
38
+ value?: any;
25
39
  }
26
40
 
27
- export const DatePicker = forwardRef(({
28
- size = "M",
29
- min,
30
- max,
31
- selected,
32
- mode = "single",
33
- onChange,
34
- showWeekNumber = false,
35
- captionLayout = "dropdown",
36
- dateFormat = "yyyy/MM/dd",
37
- calendarProps,
38
- timePicker = false,
39
- children,
40
- ...props
41
- }: DatePickerProps, ref: ForwardedRef<HTMLInputElement>) => {
41
+ export const DatePicker = forwardRef(
42
+ (
43
+ {
44
+ size = "M",
45
+ min,
46
+ max,
47
+ mode = "single",
48
+ onChange,
49
+ showWeekNumber = false,
50
+ captionLayout = "dropdown",
51
+ dateFormat = "yyyy/MM/dd",
52
+ calendarProps,
53
+ timePicker = false,
54
+ children,
55
+ value = new Date(),
56
+ ...props
57
+ }: DatePickerProps,
58
+ ref: ForwardedRef<HTMLInputElement>
59
+ ) => {
60
+ const initialDate =
61
+ mode == "multiple"
62
+ ? [value]
63
+ : mode == "range"
64
+ ? { from: value, to: value }
65
+ : value;
66
+ const [date, setDate] = useState<Date[] | Date | DateRange | undefined>(initialDate);
67
+ const [pickerValue, setPickerValue] = useState<TimePickerValue>({
68
+ hour: (date instanceof Date ? date.getHours() : 12).toString(),
69
+ minute: (date instanceof Date ? date.getMinutes() : 0)
70
+ .toString()
71
+ .padStart(2, "0"),
72
+ time: date instanceof Date && date.getHours() < 12 ? "AM" : "PM",
73
+ });
74
+ const [isOpen, setIsOpen] = useState(false);
42
75
 
43
- const fallbackDate = mode == "multiple" ? [new Date()] : mode == "range" ? { from: new Date(), to: new Date() } : new Date();
44
- const [date, setDate] = useState<Date[] | Date | DateRange | undefined>(selected || fallbackDate);
45
- const [pickerValue, setPickerValue] = useState<TimePickerValue>({
46
- hour: "12",
47
- minute: "00",
48
- time: "AM"
49
- });
50
- const [isOpen, setIsOpen] = useState(false);
76
+ // Call the onChange function when the date or picker value changes
77
+ useEffect(() => {
78
+ onChange?.({
79
+ target: {
80
+ value: date,
81
+ },
82
+ } as any);
83
+ }, [date, pickerValue]);
51
84
 
52
- // Call the onChange function when the date or picker value changes
53
- useEffect(() => {
54
- onChange?.({
55
- target: {
56
- value: date
57
- }
58
- } as any);
59
- }, [date, pickerValue]);
60
-
61
- // Disable body scroll when popover is open
62
- useEffect(() => {
63
- if (isOpen) {
64
- // Save the current overflow style
65
- const originalOverflow = document.body.style.overflow;
66
- document.body.style.overflow = 'hidden';
85
+ // Disable body scroll when popover is open
86
+ useEffect(() => {
87
+ if (isOpen) {
88
+ // Save the current overflow style
89
+ const originalOverflow = document.body.style.overflow;
90
+ document.body.style.overflow = "hidden";
67
91
 
68
- // Cleanup function to restore original overflow style
69
- return () => {
70
- document.body.style.overflow = originalOverflow;
71
- };
72
- }
73
- }, [isOpen]);
74
- const formattedValue = formatDateValueToString(date, pickerValue, dateFormat);
92
+ // Cleanup function to restore original overflow style
93
+ return () => {
94
+ document.body.style.overflow = originalOverflow;
95
+ };
96
+ }
97
+ }, [isOpen]);
98
+ const formattedValue = formatDateValueToString(
99
+ date,
100
+ pickerValue,
101
+ dateFormat
102
+ );
75
103
 
76
- return (
77
- <Popover onOpenChange={setIsOpen}>
78
- <PopoverTrigger asChild >
79
- {/* Clone the children element and pass the formatted value to the input element */}
80
- {
81
- isValidElement(children) ?
104
+ return (
105
+ <Popover onOpenChange={setIsOpen}>
106
+ <PopoverTrigger asChild>
107
+ {/* Clone the children element and pass the formatted value to the input element */}
108
+ {isValidElement(children) ? (
82
109
  cloneElement(children as React.ReactElement<HTMLInputElement>, {
83
- value: (children as React.ReactElement<HTMLInputElement>).props.value ?? formattedValue,
110
+ value:
111
+ (children as React.ReactElement<HTMLInputElement>).props
112
+ .value ?? formattedValue,
84
113
  type: "input",
85
- readOnly: true
114
+ readOnly: true,
86
115
  })
87
- :
116
+ ) : (
88
117
  /* If the children is not a valid element, Show the default input */
89
118
  <InputField
90
119
  readOnly
91
120
  type="input"
92
121
  childrenSide={
93
- <ActionButton type='button' size={"M"}>
122
+ <ActionButton type="button" size={"M"}>
94
123
  <i className="ri-calendar-event-fill"></i>
95
124
  </ActionButton>
96
125
  }
@@ -98,39 +127,41 @@ export const DatePicker = forwardRef(({
98
127
  value={formattedValue}
99
128
  ref={ref}
100
129
  />
101
- }
102
- </PopoverTrigger >
103
- <PopoverContent data-theme="dark" className='!h-fit max-h-[fit-content] p-0 border-none rounded-[12px] flex flex-col sm:flex-row'>
104
- <Calender
105
- {...calendarProps}
106
- captionLayout={captionLayout}
107
- showWeekNumber={showWeekNumber}
108
- mode={mode as any}
109
- selected={date as any}
110
- onSelect={(e: any) => {
111
- setDate(applyTimeToDateValue(e, pickerValue));
112
- }}
113
- min={mode != "single" ? min : undefined}
114
- max={mode != "single" ? max : undefined}
115
- />
116
- {timePicker && (
117
- <TimePicker
118
- value={pickerValue}
119
- onChange={(value: TimePickerValue) => {
120
- setPickerValue(value);
121
- setDate(applyTimeToDateValue(date, value));
130
+ )}
131
+ </PopoverTrigger>
132
+ <PopoverContent
133
+ data-theme="dark"
134
+ className="!h-fit max-h-[fit-content] p-0 border-none rounded-[12px] flex flex-col sm:flex-row "
135
+ >
136
+ <Calendar
137
+ {...calendarProps}
138
+ captionLayout={captionLayout}
139
+ showWeekNumber={showWeekNumber}
140
+ mode={mode as any}
141
+ selected={date as any}
142
+ onSelect={(e: any) => {
143
+ setDate(applyTimeToDateValue(e, pickerValue));
122
144
  }}
145
+ min={mode != "single" ? min : undefined}
146
+ max={mode != "single" ? max : undefined}
123
147
  />
124
- )}
125
- </PopoverContent>
126
- </Popover>
127
- )
128
- });
148
+ {timePicker && (
149
+ <TimePicker
150
+ value={pickerValue}
151
+ onChange={(value: TimePickerValue) => {
152
+ setPickerValue(value);
153
+ setDate(applyTimeToDateValue(date, value));
154
+ }}
155
+ />
156
+ )}
157
+ </PopoverContent>
158
+ </Popover>
159
+ );
160
+ }
161
+ );
129
162
 
130
163
  DatePicker.displayName = "DatePicker";
131
164
 
132
-
133
-
134
165
  interface TimePickerProps {
135
166
  value: TimePickerValue;
136
167
  onChange: (value: TimePickerValue) => void;
@@ -138,7 +169,7 @@ interface TimePickerProps {
138
169
 
139
170
  const TimePicker = ({ value, onChange }: TimePickerProps) => {
140
171
  return (
141
- <div className='relative w-full sm:w-[189px]'>
172
+ <div className="relative w-full sm:w-[189px]" data-theme="dark">
142
173
  <Picker
143
174
  className="flex-1"
144
175
  selectContainerClassName="bg-background-system-body-tertiary z-[-1] rounded-[8px]"
@@ -148,11 +179,18 @@ const TimePicker = ({ value, onChange }: TimePickerProps) => {
148
179
  }}
149
180
  wheelMode="normal"
150
181
  >
151
- <span className='absolute left-0 top-1/2 -translate-y-1/2 w-full h-[20px] flex justify-center items-center text-white text-[30px] pr-[55px] pb-[5px]' >:</span>
182
+ <span className="absolute left-0 top-1/2 -translate-y-1/2 w-full h-[20px] flex justify-center items-center text-white text-[30px] pr-[55px] pb-[5px]">
183
+ :
184
+ </span>
152
185
  <Picker.Column name="hour">
153
186
  {Array.from({ length: 12 }, (_, i) => i + 1).map((hour) => (
154
187
  <Picker.Item key={hour + "hour"} value={hour}>
155
- <div className="typography-display-small-semibold flex gap-1 whitespace-nowrap text-content-presentation-global-primary pl-[25px]"> <p className='text-content-presentation-global-primary'>{hour}</p></div>
188
+ <div className="typography-display-small-semibold flex gap-1 whitespace-nowrap text-content-presentation-global-primary pl-[25px]">
189
+ {" "}
190
+ <p className="text-content-presentation-global-primary">
191
+ {hour}
192
+ </p>
193
+ </div>
156
194
  </Picker.Item>
157
195
  ))}
158
196
  </Picker.Column>
@@ -160,7 +198,7 @@ const TimePicker = ({ value, onChange }: TimePickerProps) => {
160
198
  {Array.from({ length: 60 }, (_, i) => i).map((minute) => (
161
199
  <Picker.Item key={minute + "minute"} value={minute}>
162
200
  <div className="typography-display-small-semibold text-content-presentation-global-primary">
163
- {minute.toString().padStart(2, '0')}
201
+ {minute.toString().padStart(2, "0")}
164
202
  </div>
165
203
  </Picker.Item>
166
204
  ))}
@@ -168,11 +206,13 @@ const TimePicker = ({ value, onChange }: TimePickerProps) => {
168
206
  <Picker.Column name="time">
169
207
  {["AM", "PM"].map((time) => (
170
208
  <Picker.Item key={time + "time"} value={time}>
171
- <div className="typography-display-small-semibold text-content-presentation-global-primary pr-[30px]">{time}</div>
209
+ <div className="typography-display-small-semibold text-content-presentation-global-primary pr-[30px]">
210
+ {time}
211
+ </div>
172
212
  </Picker.Item>
173
213
  ))}
174
214
  </Picker.Column>
175
215
  </Picker>
176
216
  </div>
177
- )
178
- }
217
+ );
218
+ };
@@ -183,8 +183,8 @@ export const GroupStyles = cva(
183
183
  ],
184
184
  },
185
185
  size: {
186
- S: ["h-[30px]", "rounded-[6px] [&_input]:h-[30px] [&_div[data-role='icon']]:text-[16px]"],
187
- M: ["h-[40px]", "rounded-[8px] [&_input]:h-[40px] [&_div[data-role='icon']]:text-[18px] [&_div[data-role='icon']]:px-[2px]"],
186
+ S: ["h-[30px] min-h-[30px]", "rounded-[6px] [&_input]:h-[30px] [&_div[data-role='icon']]:text-[16px]"],
187
+ M: ["h-[40px] min-h-[40px]", "rounded-[8px] [&_input]:h-[40px] [&_div[data-role='icon']]:text-[18px] [&_div[data-role='icon']]:px-[2px]"],
188
188
  },
189
189
  error: {
190
190
  true: [
@@ -91,7 +91,7 @@ const SimpleSelectValue = ({
91
91
  onChange={handleInputChange}
92
92
  className={cn([
93
93
  "bg-transparent",
94
- "text-white",
94
+ "text-content-system-global-primary",
95
95
  "h-[24px]",
96
96
  "border-none",
97
97
  "outline-none",
@@ -1,187 +1,187 @@
1
- import { cloneElement, ComponentProps, forwardRef, isValidElement, useState, useEffect } from 'react';
2
- import { getDaysInMonth } from 'date-fns';
3
- import Picker from 'torch-react-mobile-picker';
4
- import { Popover, PopoverContent, PopoverTrigger } from './Popover';
5
- import { InputField } from './InputField';
6
- import { ActionButton } from './ActionButton';
7
- import { formatDateValueToString } from '@/utils/dateFormat';
8
-
9
- function getDayArray(year: number, month: number): string[] {
10
- const dayCount = getDaysInMonth(new Date(year, month - 1));
11
- return Array.from({ length: dayCount }, (_, i) => String(i + 1).padStart(2, '0'));
12
- }
13
-
14
- interface SlideDatePickerProps extends Omit<ComponentProps<typeof InputField>, 'onChange'> {
15
- onChange?: (e: any) => void;
16
- theme?: "dark" | "light" | "default";
17
- dateFormat?: string;
18
- }
19
-
20
- type SlideVlaues = {
21
- year: string,
22
- month: string,
23
- day: string,
24
- hour?: string,
25
- minute?: string,
26
- time?: string
27
- }
28
-
29
- export const SlideDatePicker = forwardRef<HTMLInputElement, SlideDatePickerProps>((
30
- {
31
- theme = "dark",
32
- onChange,
33
- dateFormat = "yyyy/MM/dd",
34
- children,
35
- ...props
36
- }, forwardedRef) => {
37
-
38
- const today = new Date();
39
- const defaultPickerValue = {
40
- year: String(today.getFullYear()),
41
- month: String(today.getMonth() + 1).padStart(2, '0'),
42
- day: String(today.getDate()).padStart(2, '0'),
43
- hour: "00",
44
- minute: "00",
45
- time: "AM"
46
- };
47
-
48
- const [pickerValue, setPickerValue] = useState<SlideVlaues>(defaultPickerValue);
49
- const [date, setDate] = useState<Date>(new Date());
50
- const [isOpen, setIsOpen] = useState(false);
51
- const currentYear = new Date().getFullYear();
52
- const years = Array.from({ length: 200 }, (_, i) => `${currentYear - 100 + i}`);
53
- const months = Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, ''));
54
- const days = getDayArray(Number(pickerValue.year), Number(pickerValue.month));
55
- const monthsNames = [
56
- "January", "February", "March", "April", "May", "June",
57
- "July", "August", "September", "October", "November", "December",
58
- ];
59
-
60
- const handlePickerChange = (newValue: SlideVlaues, key: string) => {
61
- let { year, month, day, hour, minute, time } = newValue;
62
-
63
- if (key === 'year' || key === 'month') {
64
- const newDayArray = getDayArray(Number(year), Number(month));
65
- day = newDayArray.includes(day as string) ? day : newDayArray[newDayArray.length - 1];
66
- }
67
-
68
- const updatedValue = { year, month, day, hour, minute, time };
69
- setPickerValue(updatedValue);
70
-
71
- // Create a Date object from the updated value
72
- const newDate = new Date(
73
- Number(updatedValue.year),
74
- Number(updatedValue.month) - 1, // Month is 0-indexed in JavaScript Date
75
- Number(updatedValue.day)
76
- );
77
- setDate(newDate);
78
-
79
- // Call the onChange callback with the Date object
80
- if (onChange) {
81
- onChange({
82
- target: {
83
- value: newDate
84
- }
85
- } as any);
86
- }
87
- };
88
-
89
- const formattedValue = formatDateValueToString(date, {
90
- hour: pickerValue.hour ?? defaultPickerValue.hour,
91
- minute: pickerValue.minute ?? defaultPickerValue.minute,
92
- time: pickerValue.time ?? defaultPickerValue.time
93
- }, dateFormat);
94
-
95
- // Disable body scroll when popover is open
96
- useEffect(() => {
97
- if (isOpen) {
98
- // Save the current overflow style
99
- const originalOverflow = document.body.style.overflow;
100
- document.body.style.overflow = 'hidden';
101
-
102
- // Cleanup function to restore original overflow style
103
- return () => {
104
- document.body.style.overflow = originalOverflow;
105
- };
106
- }
107
- }, [isOpen]);
108
-
109
- useEffect(() => {
110
- if (onChange) {
111
- onChange({
112
- target: {
113
- value: date
114
- }
115
- } as any);
116
- }
117
- }, [formattedValue]);
118
-
119
- return (
120
- <Popover onOpenChange={setIsOpen}>
121
- <PopoverTrigger asChild data-theme={theme} className='w-full flex-1' >
122
- {
123
- isValidElement(children) ?
124
- cloneElement(children as React.ReactElement<HTMLInputElement>, {
125
- value: (children as React.ReactElement<HTMLInputElement>).props.value ?? formattedValue,
126
- type: "input",
127
- readOnly: true
128
- })
129
- :
130
- /* If the children is not a valid element, Show the default input */
131
- <InputField
132
- readOnly
133
- type="input"
134
- childrenSide={
135
- <ActionButton type='button' size={"M"}>
136
- <i className="ri-calendar-event-fill"></i>
137
- </ActionButton>
138
- }
139
- {...props}
140
- value={formattedValue}
141
- ref={forwardedRef}
142
- />
143
- }
144
- </PopoverTrigger>
145
- <PopoverContent data-theme={theme} dir="ltr" variant={props.variant} className="overflow-hidden w-[285px] flex justify-center items-center p-[6px] pt-[30px]">
146
- <div className="flex justify-evenly items-center w-full absolute top-0 py-[6px]">
147
- <p className="text-content-system-global-secondary typography-headers-medium-regular">Year</p>
148
- <div className="flex justify-center items-center self-center">
149
- <span className="h-[13px] w-[1px] bg-border-system-global-secondary rounded-[3px]"></span>
150
- <p className="text-content-system-global-secondary typography-headers-medium-regular px-[18px]">Month</p>
151
- <span className="h-[13px] w-[1px] bg-border-system-global-secondary rounded-[3px]"></span>
152
- </div>
153
- <p className="text-content-system-global-secondary typography-headers-medium-regular">Day</p>
154
- </div>
155
- <Picker
156
- className="flex-1"
157
- selectContainerClassName="bg-background-system-body-tertiary z-[-1] rounded-[8px]"
158
- value={pickerValue}
159
- onChange={handlePickerChange}
160
- wheelMode="natural"
161
- >
162
- <Picker.Column name="year" >
163
- {years.map((year) => (
164
- <Picker.Item key={year} value={year}>
165
- <div className="typography-display-small-semibold text-content-presentation-action-light-primary">{year}</div>
166
- </Picker.Item>
167
- ))}
168
- </Picker.Column>
169
- <Picker.Column name="month">
170
- {months.map((month, i) => (
171
- <Picker.Item key={month} value={month}>
172
- <div className="typography-display-small-semibold flex gap-1 whitespace-nowrap text-content-presentation-action-light-primary"> <p className='text-content-presentation-action-light-secondary'>{monthsNames[i].substring(0, 3)} - </p>{month}</div>
173
- </Picker.Item>
174
- ))}
175
- </Picker.Column>
176
- <Picker.Column name="day">
177
- {days.map((day) => (
178
- <Picker.Item key={day} value={day}>
179
- <div className="typography-display-small-semibold text-content-presentation-action-light-primary">{day}</div>
180
- </Picker.Item>
181
- ))}
182
- </Picker.Column>
183
- </Picker>
184
- </PopoverContent>
185
- </Popover>
186
- );
187
- });
1
+ import { cloneElement, ComponentProps, forwardRef, isValidElement, useState, useEffect } from 'react';
2
+ import { getDaysInMonth } from 'date-fns';
3
+ import Picker from 'torch-react-mobile-picker';
4
+ import { Popover, PopoverContent, PopoverTrigger } from './Popover';
5
+ import { InputField } from './InputField';
6
+ import { ActionButton } from './ActionButton';
7
+ import { formatDateValueToString } from '@/utils/dateFormat';
8
+
9
+ function getDayArray(year: number, month: number): string[] {
10
+ const dayCount = getDaysInMonth(new Date(year, month - 1));
11
+ return Array.from({ length: dayCount }, (_, i) => String(i + 1).padStart(2, '0'));
12
+ }
13
+
14
+ interface SlideDatePickerProps extends Omit<ComponentProps<typeof InputField>, 'onChange'> {
15
+ onChange?: (e: any) => void;
16
+ theme?: "dark" | "light" | "default";
17
+ dateFormat?: string;
18
+ }
19
+
20
+ type SlideVlaues = {
21
+ year: string,
22
+ month: string,
23
+ day: string,
24
+ hour?: string,
25
+ minute?: string,
26
+ time?: string
27
+ }
28
+
29
+ export const SlideDatePicker = forwardRef<HTMLInputElement, SlideDatePickerProps>((
30
+ {
31
+ theme = "dark",
32
+ onChange,
33
+ dateFormat = "yyyy/MM/dd",
34
+ children,
35
+ ...props
36
+ }, forwardedRef) => {
37
+
38
+ const today = new Date();
39
+ const defaultPickerValue = {
40
+ year: String(today.getFullYear()),
41
+ month: String(today.getMonth() + 1).padStart(2, '0'),
42
+ day: String(today.getDate()).padStart(2, '0'),
43
+ hour: "00",
44
+ minute: "00",
45
+ time: "AM"
46
+ };
47
+
48
+ const [pickerValue, setPickerValue] = useState<SlideVlaues>(defaultPickerValue);
49
+ const [date, setDate] = useState<Date>(new Date());
50
+ const [isOpen, setIsOpen] = useState(false);
51
+ const currentYear = new Date().getFullYear();
52
+ const years = Array.from({ length: 200 }, (_, i) => `${currentYear - 100 + i}`);
53
+ const months = Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, ''));
54
+ const days = getDayArray(Number(pickerValue.year), Number(pickerValue.month));
55
+ const monthsNames = [
56
+ "January", "February", "March", "April", "May", "June",
57
+ "July", "August", "September", "October", "November", "December",
58
+ ];
59
+
60
+ const handlePickerChange = (newValue: SlideVlaues, key: string) => {
61
+ let { year, month, day, hour, minute, time } = newValue;
62
+
63
+ if (key === 'year' || key === 'month') {
64
+ const newDayArray = getDayArray(Number(year), Number(month));
65
+ day = newDayArray.includes(day as string) ? day : newDayArray[newDayArray.length - 1];
66
+ }
67
+
68
+ const updatedValue = { year, month, day, hour, minute, time };
69
+ setPickerValue(updatedValue);
70
+
71
+ // Create a Date object from the updated value
72
+ const newDate = new Date(
73
+ Number(updatedValue.year),
74
+ Number(updatedValue.month) - 1, // Month is 0-indexed in JavaScript Date
75
+ Number(updatedValue.day)
76
+ );
77
+ setDate(newDate);
78
+
79
+ // Call the onChange callback with the Date object
80
+ if (onChange) {
81
+ onChange({
82
+ target: {
83
+ value: newDate
84
+ }
85
+ } as any);
86
+ }
87
+ };
88
+
89
+ const formattedValue = formatDateValueToString(date, {
90
+ hour: pickerValue.hour ?? defaultPickerValue.hour,
91
+ minute: pickerValue.minute ?? defaultPickerValue.minute,
92
+ time: pickerValue.time ?? defaultPickerValue.time
93
+ }, dateFormat);
94
+
95
+ // Disable body scroll when popover is open
96
+ useEffect(() => {
97
+ if (isOpen) {
98
+ // Save the current overflow style
99
+ const originalOverflow = document.body.style.overflow;
100
+ document.body.style.overflow = 'hidden';
101
+
102
+ // Cleanup function to restore original overflow style
103
+ return () => {
104
+ document.body.style.overflow = originalOverflow;
105
+ };
106
+ }
107
+ }, [isOpen]);
108
+
109
+ useEffect(() => {
110
+ if (onChange) {
111
+ onChange({
112
+ target: {
113
+ value: date
114
+ }
115
+ } as any);
116
+ }
117
+ }, [formattedValue]);
118
+
119
+ return (
120
+ <Popover onOpenChange={setIsOpen}>
121
+ <PopoverTrigger asChild data-theme={theme} className='w-full flex-1' >
122
+ {
123
+ isValidElement(children) ?
124
+ cloneElement(children as React.ReactElement<HTMLInputElement>, {
125
+ value: (children as React.ReactElement<HTMLInputElement>).props.value ?? formattedValue,
126
+ type: "input",
127
+ readOnly: true
128
+ })
129
+ :
130
+ /* If the children is not a valid element, Show the default input */
131
+ <InputField
132
+ readOnly
133
+ type="input"
134
+ childrenSide={
135
+ <ActionButton type='button' size={"M"}>
136
+ <i className="ri-calendar-event-fill"></i>
137
+ </ActionButton>
138
+ }
139
+ {...props}
140
+ value={formattedValue}
141
+ ref={forwardedRef}
142
+ />
143
+ }
144
+ </PopoverTrigger>
145
+ <PopoverContent data-theme={theme} dir="ltr" variant={props.variant} className="overflow-hidden w-[285px] flex justify-center items-center p-[6px] pt-[30px]">
146
+ <div className="flex justify-evenly items-center w-full absolute top-0 py-[6px]">
147
+ <p className="text-content-system-global-secondary typography-headers-medium-regular">Year</p>
148
+ <div className="flex justify-center items-center self-center">
149
+ <span className="h-[13px] w-[1px] bg-border-system-global-secondary rounded-[3px]"></span>
150
+ <p className="text-content-system-global-secondary typography-headers-medium-regular px-[18px]">Month</p>
151
+ <span className="h-[13px] w-[1px] bg-border-system-global-secondary rounded-[3px]"></span>
152
+ </div>
153
+ <p className="text-content-system-global-secondary typography-headers-medium-regular">Day</p>
154
+ </div>
155
+ <Picker
156
+ className="flex-1"
157
+ selectContainerClassName="bg-background-system-body-tertiary z-[-1] rounded-[8px]"
158
+ value={pickerValue}
159
+ onChange={handlePickerChange}
160
+ wheelMode="natural"
161
+ >
162
+ <Picker.Column name="year" >
163
+ {years.map((year) => (
164
+ <Picker.Item key={year} value={year}>
165
+ <div className="typography-display-small-semibold text-content-presentation-action-light-primary">{year}</div>
166
+ </Picker.Item>
167
+ ))}
168
+ </Picker.Column>
169
+ <Picker.Column name="month">
170
+ {months.map((month, i) => (
171
+ <Picker.Item key={month} value={month}>
172
+ <div className="typography-display-small-semibold flex gap-1 whitespace-nowrap text-content-presentation-action-light-primary"> <p className='text-content-presentation-action-light-secondary'>{monthsNames[i].substring(0, 3)} - </p>{month}</div>
173
+ </Picker.Item>
174
+ ))}
175
+ </Picker.Column>
176
+ <Picker.Column name="day">
177
+ {days.map((day) => (
178
+ <Picker.Item key={day} value={day}>
179
+ <div className="typography-display-small-semibold text-content-presentation-action-light-primary">{day}</div>
180
+ </Picker.Item>
181
+ ))}
182
+ </Picker.Column>
183
+ </Picker>
184
+ </PopoverContent>
185
+ </Popover>
186
+ );
187
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "torch-glare",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "files": [