@mbao01/common 0.0.43 → 0.0.45

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.
Files changed (172) hide show
  1. package/dist/types/components/Chart/Chart.d.ts +2 -0
  2. package/dist/types/components/Chart/ChartContext.d.ts +2 -0
  3. package/dist/types/components/Chart/components/ChartLegend.d.ts +4 -0
  4. package/dist/types/components/Chart/components/ChartStyle.d.ts +2 -0
  5. package/dist/types/components/Chart/components/ChartTooltip.d.ts +4 -0
  6. package/dist/types/components/Chart/components/index.d.ts +3 -0
  7. package/dist/types/components/Chart/constants.d.ts +22 -0
  8. package/dist/types/components/Chart/helpers.d.ts +11 -0
  9. package/dist/types/components/Chart/hooks/index.d.ts +1 -0
  10. package/dist/types/components/Chart/hooks/useChart/index.d.ts +1 -0
  11. package/dist/types/components/Chart/hooks/useChart/useChart.d.ts +1 -0
  12. package/dist/types/components/Chart/index.d.ts +2 -0
  13. package/dist/types/components/Chart/stories/args/activeShapeArgs.d.ts +2 -0
  14. package/dist/types/components/Chart/stories/args/animationArgs.d.ts +12 -0
  15. package/dist/types/components/Chart/stories/args/areaArgs.d.ts +2 -0
  16. package/dist/types/components/Chart/stories/args/areaChartArgs.d.ts +2 -0
  17. package/dist/types/components/Chart/stories/args/barArgs.d.ts +5 -0
  18. package/dist/types/components/Chart/stories/args/barChartArgs.d.ts +2 -0
  19. package/dist/types/components/Chart/stories/args/cartesianChartArgs.d.ts +2 -0
  20. package/dist/types/components/Chart/stories/args/cartesianSharedArgs.d.ts +11 -0
  21. package/dist/types/components/Chart/stories/args/chartArgs.d.ts +6 -0
  22. package/dist/types/components/Chart/stories/args/dotArgs.d.ts +10 -0
  23. package/dist/types/components/Chart/stories/args/events.d.ts +804 -0
  24. package/dist/types/components/Chart/stories/args/labelListArgs.d.ts +2 -0
  25. package/dist/types/components/Chart/stories/args/legendArgs.d.ts +3 -0
  26. package/dist/types/components/Chart/stories/args/lineArgs.d.ts +2 -0
  27. package/dist/types/components/Chart/stories/args/lineChartArgs.d.ts +2 -0
  28. package/dist/types/components/Chart/stories/args/pieArgs.d.ts +2 -0
  29. package/dist/types/components/Chart/stories/args/pieChartArgs.d.ts +2 -0
  30. package/dist/types/components/Chart/stories/args/polarChartArgs.d.ts +2 -0
  31. package/dist/types/components/Chart/stories/args/polarSharedArgs.d.ts +2 -0
  32. package/dist/types/components/Chart/stories/args/radarArgs.d.ts +2 -0
  33. package/dist/types/components/Chart/stories/args/radarChartArgs.d.ts +2 -0
  34. package/dist/types/components/Chart/stories/args/radialBarArgs.d.ts +2 -0
  35. package/dist/types/components/Chart/stories/args/radialBarChartArgs.d.ts +2 -0
  36. package/dist/types/components/Chart/stories/args/rectangleArgs.d.ts +9 -0
  37. package/dist/types/components/Chart/stories/args/referenceSharedArgs.d.ts +4 -0
  38. package/dist/types/components/Chart/stories/args/sectorArgs.d.ts +7 -0
  39. package/dist/types/components/Chart/stories/args/sharedAxisArgs.d.ts +2 -0
  40. package/dist/types/components/Chart/stories/args/stylesArgs.d.ts +6 -0
  41. package/dist/types/components/Chart/stories/args/textArgs.d.ts +2 -0
  42. package/dist/types/components/Chart/stories/args/tooltipArgs.d.ts +4 -0
  43. package/dist/types/components/Chart/stories/args/types.d.ts +13 -0
  44. package/dist/types/components/Chart/stories/args/xAxisArgs.d.ts +2 -0
  45. package/dist/types/components/Chart/stories/args/yAxisArgs.d.ts +2 -0
  46. package/dist/types/components/Chart/stories/examples/AreaChart.d.ts +16 -0
  47. package/dist/types/components/Chart/stories/examples/BarChart.d.ts +20 -0
  48. package/dist/types/components/Chart/stories/examples/LineChart.d.ts +16 -0
  49. package/dist/types/components/Chart/stories/examples/PieChart.d.ts +19 -0
  50. package/dist/types/components/Chart/stories/examples/RadarChart.d.ts +24 -0
  51. package/dist/types/components/Chart/stories/examples/RadialChart.d.ts +17 -0
  52. package/dist/types/components/Chart/stories/examples/Tooltip.d.ts +15 -0
  53. package/dist/types/components/Chart/stories/helpers/index.d.ts +9 -0
  54. package/dist/types/components/Chart/types.d.ts +37 -0
  55. package/dist/types/components/DatetimePicker/DatetimeGrid.d.ts +26 -0
  56. package/dist/types/components/DatetimePicker/DatetimePicker.d.ts +13 -0
  57. package/dist/types/components/DatetimePicker/constants.d.ts +15 -0
  58. package/dist/types/components/DatetimePicker/index.d.ts +1 -0
  59. package/dist/types/components/DatetimePicker/types.d.ts +25 -0
  60. package/dist/types/components/Form/DatetimeInput/DatetimeCalendar.d.ts +5 -0
  61. package/dist/types/components/Form/DatetimeInput/DatetimeInput.d.ts +83 -0
  62. package/dist/types/components/Form/DatetimeInput/DatetimeInputContext.d.ts +2 -0
  63. package/dist/types/components/Form/DatetimeInput/NaturalLanguageInput.d.ts +5 -0
  64. package/dist/types/components/Form/DatetimeInput/TimePicker.d.ts +1 -0
  65. package/dist/types/components/Form/DatetimeInput/constants.d.ts +24 -0
  66. package/dist/types/components/Form/DatetimeInput/helpers.d.ts +27 -0
  67. package/dist/types/components/Form/DatetimeInput/hooks/index.d.ts +1 -0
  68. package/dist/types/components/Form/DatetimeInput/hooks/useDateInput/index.d.ts +1 -0
  69. package/dist/types/components/Form/DatetimeInput/hooks/useDateInput/useDateInput.d.ts +1 -0
  70. package/dist/types/components/Form/DatetimeInput/index.d.ts +1 -0
  71. package/dist/types/components/Form/DatetimeInput/types.d.ts +31 -0
  72. package/dist/types/components/Form/MultiSelect/MultiSelect.d.ts +46 -0
  73. package/dist/types/components/Form/MultiSelect/MultiSelectContext.d.ts +2 -0
  74. package/dist/types/components/Form/MultiSelect/constants.d.ts +19 -0
  75. package/dist/types/components/Form/MultiSelect/hooks/index.d.ts +1 -0
  76. package/dist/types/components/Form/MultiSelect/hooks/useMultiSelect/index.d.ts +1 -0
  77. package/dist/types/components/Form/MultiSelect/hooks/useMultiSelect/useMultiSelect.d.ts +1 -0
  78. package/dist/types/components/Form/MultiSelect/index.d.ts +1 -0
  79. package/dist/types/components/Form/MultiSelect/types.d.ts +31 -0
  80. package/dist/types/components/Form/TagsInput/TagsInput.d.ts +13 -0
  81. package/dist/types/components/Form/TagsInput/constants.d.ts +16 -0
  82. package/dist/types/components/Form/TagsInput/index.d.ts +1 -0
  83. package/dist/types/components/Form/TagsInput/types.d.ts +9 -0
  84. package/dist/types/components/Form/index.d.ts +2 -0
  85. package/dist/types/index.d.ts +1 -0
  86. package/package.json +7 -3
  87. package/src/components/Chart/Chart.tsx +26 -0
  88. package/src/components/Chart/ChartContext.tsx +4 -0
  89. package/src/components/Chart/components/ChartLegend.tsx +52 -0
  90. package/src/components/Chart/components/ChartStyle.tsx +32 -0
  91. package/src/components/Chart/components/ChartTooltip.tsx +126 -0
  92. package/src/components/Chart/components/index.ts +3 -0
  93. package/src/components/Chart/constants.ts +78 -0
  94. package/src/components/Chart/helpers.ts +27 -0
  95. package/src/components/Chart/hooks/index.ts +1 -0
  96. package/src/components/Chart/hooks/useChart/index.ts +1 -0
  97. package/src/components/Chart/hooks/useChart/useChart.ts +12 -0
  98. package/src/components/Chart/index.ts +2 -0
  99. package/src/components/Chart/stories/args/activeShapeArgs.ts +34 -0
  100. package/src/components/Chart/stories/args/animationArgs.ts +67 -0
  101. package/src/components/Chart/stories/args/areaArgs.ts +16 -0
  102. package/src/components/Chart/stories/args/areaChartArgs.ts +19 -0
  103. package/src/components/Chart/stories/args/barArgs.ts +123 -0
  104. package/src/components/Chart/stories/args/barChartArgs.ts +65 -0
  105. package/src/components/Chart/stories/args/cartesianChartArgs.ts +42 -0
  106. package/src/components/Chart/stories/args/cartesianSharedArgs.ts +136 -0
  107. package/src/components/Chart/stories/args/chartArgs.ts +244 -0
  108. package/src/components/Chart/stories/args/dotArgs.ts +46 -0
  109. package/src/components/Chart/stories/args/events.ts +343 -0
  110. package/src/components/Chart/stories/args/index.ts +178 -0
  111. package/src/components/Chart/stories/args/labelListArgs.ts +124 -0
  112. package/src/components/Chart/stories/args/legendArgs.ts +205 -0
  113. package/src/components/Chart/stories/args/lineArgs.ts +87 -0
  114. package/src/components/Chart/stories/args/lineChartArgs.ts +17 -0
  115. package/src/components/Chart/stories/args/pieArgs.ts +18 -0
  116. package/src/components/Chart/stories/args/pieChartArgs.ts +7 -0
  117. package/src/components/Chart/stories/args/polarChartArgs.ts +86 -0
  118. package/src/components/Chart/stories/args/polarSharedArgs.ts +42 -0
  119. package/src/components/Chart/stories/args/radarArgs.ts +26 -0
  120. package/src/components/Chart/stories/args/radarChartArgs.ts +26 -0
  121. package/src/components/Chart/stories/args/radialBarArgs.ts +36 -0
  122. package/src/components/Chart/stories/args/radialBarChartArgs.ts +16 -0
  123. package/src/components/Chart/stories/args/rectangleArgs.ts +35 -0
  124. package/src/components/Chart/stories/args/referenceSharedArgs.ts +75 -0
  125. package/src/components/Chart/stories/args/sectorArgs.ts +106 -0
  126. package/src/components/Chart/stories/args/sharedAxisArgs.ts +332 -0
  127. package/src/components/Chart/stories/args/stylesArgs.ts +258 -0
  128. package/src/components/Chart/stories/args/textArgs.ts +97 -0
  129. package/src/components/Chart/stories/args/tooltipArgs.ts +41 -0
  130. package/src/components/Chart/stories/args/types.ts +23 -0
  131. package/src/components/Chart/stories/args/xAxisArgs.ts +109 -0
  132. package/src/components/Chart/stories/args/yAxisArgs.ts +73 -0
  133. package/src/components/Chart/stories/examples/AreaChart.tsx +156 -0
  134. package/src/components/Chart/stories/examples/BarChart.tsx +425 -0
  135. package/src/components/Chart/stories/examples/LineChart.tsx +144 -0
  136. package/src/components/Chart/stories/examples/PieChart.tsx +238 -0
  137. package/src/components/Chart/stories/examples/RadarChart.tsx +261 -0
  138. package/src/components/Chart/stories/examples/RadialChart.tsx +239 -0
  139. package/src/components/Chart/stories/examples/Tooltip.tsx +68 -0
  140. package/src/components/Chart/stories/helpers/index.tsx +75 -0
  141. package/src/components/Chart/types.ts +39 -0
  142. package/src/components/DatetimePicker/DatetimeGrid.tsx +59 -0
  143. package/src/components/DatetimePicker/DatetimePicker.tsx +59 -0
  144. package/src/components/DatetimePicker/constants.ts +102 -0
  145. package/src/components/DatetimePicker/index.ts +1 -0
  146. package/src/components/DatetimePicker/types.ts +36 -0
  147. package/src/components/Form/DatetimeInput/DatetimeCalendar.tsx +68 -0
  148. package/src/components/Form/DatetimeInput/DatetimeInput.tsx +90 -0
  149. package/src/components/Form/DatetimeInput/DatetimeInputContext.tsx +4 -0
  150. package/src/components/Form/DatetimeInput/NaturalLanguageInput.tsx +73 -0
  151. package/src/components/Form/DatetimeInput/TimePicker.tsx +202 -0
  152. package/src/components/Form/DatetimeInput/constants.ts +135 -0
  153. package/src/components/Form/DatetimeInput/helpers.ts +93 -0
  154. package/src/components/Form/DatetimeInput/hooks/index.ts +1 -0
  155. package/src/components/Form/DatetimeInput/hooks/useDateInput/index.ts +1 -0
  156. package/src/components/Form/DatetimeInput/hooks/useDateInput/useDateInput.ts +10 -0
  157. package/src/components/Form/DatetimeInput/index.ts +1 -0
  158. package/src/components/Form/DatetimeInput/types.ts +36 -0
  159. package/src/components/Form/MultiSelect/MultiSelect.tsx +348 -0
  160. package/src/components/Form/MultiSelect/MultiSelectContext.tsx +4 -0
  161. package/src/components/Form/MultiSelect/constants.ts +103 -0
  162. package/src/components/Form/MultiSelect/hooks/index.ts +1 -0
  163. package/src/components/Form/MultiSelect/hooks/useMultiSelect/index.ts +1 -0
  164. package/src/components/Form/MultiSelect/hooks/useMultiSelect/useMultiSelect.ts +10 -0
  165. package/src/components/Form/MultiSelect/index.ts +1 -0
  166. package/src/components/Form/MultiSelect/types.ts +46 -0
  167. package/src/components/Form/TagsInput/TagsInput.tsx +278 -0
  168. package/src/components/Form/TagsInput/constants.ts +87 -0
  169. package/src/components/Form/TagsInput/index.ts +1 -0
  170. package/src/components/Form/TagsInput/types.ts +10 -0
  171. package/src/components/Form/index.ts +2 -0
  172. package/src/index.ts +1 -0
@@ -0,0 +1,68 @@
1
+ import { useCallback } from "react";
2
+ import { CalendarIcon } from "@radix-ui/react-icons";
3
+ import { cn } from "../../../utilities";
4
+ import { Button } from "../../Button";
5
+ import { Calendar } from "../../Calendar";
6
+ import { Popover } from "../../Popover";
7
+ import {
8
+ getDatetimeCalendarClasses,
9
+ getDatetimeCalendarIconClasses,
10
+ getDatetimeCalendarTriggerClasses,
11
+ } from "./constants";
12
+ import { parseDateTime } from "./helpers";
13
+ import { useDateInput } from "./hooks";
14
+ import { TimePicker } from "./TimePicker";
15
+ import { type DatetimeCalendarProps } from "./types";
16
+
17
+ export const DatetimeCalendar = ({
18
+ size,
19
+ disabled,
20
+ className,
21
+ ...props
22
+ }: DatetimeCalendarProps) => {
23
+ const { value, onDateChange, time } = useDateInput();
24
+
25
+ const handleSelect = useCallback(
26
+ (_: Date | undefined, triggerDate: Date) => {
27
+ const parsedDateTime = parseDateTime(triggerDate);
28
+
29
+ if (parsedDateTime) {
30
+ const [hours, minutes] = time.split(":");
31
+ parsedDateTime.setHours(parseInt(hours) || 0, parseInt(minutes) || 0);
32
+ onDateChange(parsedDateTime);
33
+ }
34
+ },
35
+ [time, onDateChange]
36
+ );
37
+
38
+ return (
39
+ <Popover>
40
+ <Popover.Trigger asChild>
41
+ <Button
42
+ variant="default"
43
+ disabled={disabled}
44
+ className={cn(getDatetimeCalendarTriggerClasses({ size }))}
45
+ >
46
+ <CalendarIcon className={getDatetimeCalendarIconClasses({ size })} />
47
+ <span className="sr-only">calendar</span>
48
+ </Button>
49
+ </Popover.Trigger>
50
+ <Popover.Content className="w-auto p-0" sideOffset={8}>
51
+ <div className="flex gap-1">
52
+ <Calendar
53
+ {...props}
54
+ autoFocus
55
+ disabled={disabled}
56
+ className={cn(getDatetimeCalendarClasses(), className)}
57
+ mode="single"
58
+ selected={value}
59
+ onSelect={handleSelect}
60
+ />
61
+ <TimePicker />
62
+ </div>
63
+ </Popover.Content>
64
+ </Popover>
65
+ );
66
+ };
67
+
68
+ DatetimeCalendar.displayName = "DatetimeCalendar";
@@ -0,0 +1,90 @@
1
+ "use client";
2
+
3
+ import { forwardRef, useCallback, useEffect, useState } from "react";
4
+ import type { DatetimeInputProps, TimeString } from "./types";
5
+ import { cn } from "../../../utilities";
6
+ import { getDatetimeInputContainerClasses } from "./constants";
7
+ import { DatetimeCalendar } from "./DatetimeCalendar";
8
+ import { DatetimeInputContext } from "./DatetimeInputContext";
9
+ import { getParsedTime, parseDateTime } from "./helpers";
10
+ import { NaturalLanguageInput } from "./NaturalLanguageInput";
11
+
12
+ export const DatetimeInput = forwardRef<HTMLInputElement, DatetimeInputProps>(
13
+ (
14
+ {
15
+ calendar,
16
+ className,
17
+ date,
18
+ defaultDate,
19
+ locale,
20
+ onDateChange,
21
+ placeholder,
22
+ outline,
23
+ disabled,
24
+ size,
25
+ variant,
26
+ wide,
27
+ ...props
28
+ },
29
+ ref
30
+ ) => {
31
+ const [_date, setDate] = useState<Date | undefined>(
32
+ defaultDate ? parseDateTime(defaultDate) : undefined
33
+ );
34
+ const [time, setTime] = useState<TimeString>(defaultDate ? getParsedTime(defaultDate) : "");
35
+ const isControlled = date !== undefined;
36
+
37
+ useEffect(() => {
38
+ // sync internal state with controlled value if provided
39
+ if (isControlled) {
40
+ setDate(date);
41
+ }
42
+ // eslint-disable-next-line react-hooks/exhaustive-deps
43
+ }, [date]);
44
+
45
+ const handleDateChange = useCallback((d: Date | undefined) => {
46
+ if (!isControlled) {
47
+ setDate(d);
48
+ }
49
+
50
+ onDateChange?.(d);
51
+ // eslint-disable-next-line react-hooks/exhaustive-deps
52
+ }, []);
53
+
54
+ const onTimeChange = useCallback((time: TimeString) => {
55
+ setTime(time);
56
+ }, []);
57
+
58
+ return (
59
+ <DatetimeInputContext.Provider
60
+ value={{
61
+ value: isControlled ? date : _date,
62
+ onDateChange: handleDateChange,
63
+ time,
64
+ onTimeChange,
65
+ }}
66
+ >
67
+ <div className="flex items-center justify-center flex-nowrap">
68
+ <div
69
+ className={cn(
70
+ getDatetimeInputContainerClasses({ outline, disabled, size, variant, wide }),
71
+ className
72
+ )}
73
+ >
74
+ <DatetimeCalendar size={size} disabled={disabled} defaultMonth={_date} {...calendar} />
75
+ <NaturalLanguageInput
76
+ ref={ref}
77
+ size={size}
78
+ locale={locale}
79
+ disabled={disabled}
80
+ placeholder={placeholder}
81
+ {...props}
82
+ />
83
+ </div>
84
+ </div>
85
+ </DatetimeInputContext.Provider>
86
+ );
87
+ }
88
+ );
89
+
90
+ DatetimeInput.displayName = "DatetimeInput";
@@ -0,0 +1,4 @@
1
+ import { createContext } from "react";
2
+ import { type DatetimeInputContextProps } from "./types";
3
+
4
+ export const DatetimeInputContext = createContext<DatetimeInputContextProps | null>(null);
@@ -0,0 +1,73 @@
1
+ import type { ChangeEvent, KeyboardEvent } from "react";
2
+ import { forwardRef, useCallback, useEffect, useState } from "react";
3
+ import { Input } from "../Input";
4
+ import { getNaturalLanguageInputClasses } from "./constants";
5
+ import { formatDateTime, getParsedTime, parseDateTime, setDateTime } from "./helpers";
6
+ import { useDateInput } from "./hooks";
7
+ import { type NaturalLanguageInputProps } from "./types";
8
+
9
+ export const NaturalLanguageInput = forwardRef<HTMLInputElement, NaturalLanguageInputProps>(
10
+ ({ placeholder = 'e.g. "tomorrow at 5pm" or "in 2 hours"', size, locale, ...props }, ref) => {
11
+ const { value, onDateChange, time, onTimeChange } = useDateInput();
12
+ const [inputValue, setInputValue] = useState<string>("");
13
+
14
+ useEffect(() => {
15
+ const timeVal = time || getParsedTime(new Date());
16
+ setInputValue(value ? formatDateTime(setDateTime(value, timeVal), locale) : "");
17
+ onTimeChange(timeVal);
18
+ }, [value, time, locale, onTimeChange]);
19
+
20
+ const handleParse = useCallback(
21
+ (e: ChangeEvent<HTMLInputElement>) => {
22
+ const input = e.currentTarget.value;
23
+ if (!input) {
24
+ onDateChange(undefined);
25
+ onTimeChange("");
26
+ return;
27
+ }
28
+
29
+ // parse the date string when the input field loses focus
30
+ const parsedDateTime = parseDateTime(input);
31
+ if (parsedDateTime) {
32
+ onDateChange(parsedDateTime);
33
+ setInputValue(formatDateTime(parsedDateTime, locale));
34
+ onTimeChange(getParsedTime(parsedDateTime));
35
+ }
36
+ },
37
+ [locale, onDateChange, onTimeChange]
38
+ );
39
+
40
+ const handleKeydown = useCallback(
41
+ (e: KeyboardEvent<HTMLInputElement>) => {
42
+ const parsedDateTime = parseDateTime(e.currentTarget.value);
43
+ switch (e.key) {
44
+ case "Enter":
45
+ if (parsedDateTime) {
46
+ onDateChange(parsedDateTime);
47
+ setInputValue(formatDateTime(parsedDateTime, locale));
48
+ onTimeChange(getParsedTime(parsedDateTime));
49
+ }
50
+ break;
51
+ }
52
+ },
53
+ [locale, onDateChange, onTimeChange]
54
+ );
55
+
56
+ return (
57
+ <Input
58
+ ref={ref}
59
+ type="text"
60
+ size={size}
61
+ placeholder={placeholder}
62
+ value={inputValue}
63
+ onChange={(e) => setInputValue(e.currentTarget.value)}
64
+ onKeyDown={handleKeydown}
65
+ onBlur={handleParse}
66
+ className={getNaturalLanguageInputClasses({ size })}
67
+ {...props}
68
+ />
69
+ );
70
+ }
71
+ );
72
+
73
+ NaturalLanguageInput.displayName = "NaturalLanguageInput";
@@ -0,0 +1,202 @@
1
+ import type { KeyboardEvent } from "react";
2
+ import { useCallback, useEffect, useMemo, useState } from "react";
3
+ import { cn } from "../../../utilities";
4
+ import { ScrollArea } from "../../ScrollArea";
5
+ import {
6
+ DEFAULT_SIZE,
7
+ getTimePickerClasses,
8
+ getTimePickerListClasses,
9
+ getTimePickerScrollAreaClasses,
10
+ } from "./constants";
11
+ import { parseDateTime, setDateTime } from "./helpers";
12
+ import { useDateInput } from "./hooks";
13
+ import { type TimeString } from "./types";
14
+
15
+ export const TimePicker = () => {
16
+ const { value, onDateChange, time, onTimeChange } = useDateInput();
17
+ const [activeIndex, setActiveIndex] = useState(-1);
18
+ const timestamp = 15;
19
+
20
+ const formateSelectedTime = useCallback(
21
+ (time: TimeString) => {
22
+ onTimeChange(time);
23
+
24
+ const newVal = parseDateTime(value ?? new Date());
25
+
26
+ if (!newVal) return;
27
+
28
+ onDateChange(setDateTime(newVal, time));
29
+ },
30
+ [value, onDateChange, onTimeChange]
31
+ );
32
+
33
+ const handleKeydown = useCallback(
34
+ (e: KeyboardEvent<HTMLDivElement>) => {
35
+ e.stopPropagation();
36
+
37
+ if (!document) return;
38
+
39
+ const moveNext = () => {
40
+ const nextIndex = activeIndex + 1 > DEFAULT_SIZE - 1 ? 0 : activeIndex + 1;
41
+
42
+ const currentElm = document.getElementById(`time-${nextIndex}`);
43
+
44
+ currentElm?.focus();
45
+
46
+ setActiveIndex(nextIndex);
47
+ };
48
+
49
+ const movePrev = () => {
50
+ const prevIndex = activeIndex - 1 < 0 ? DEFAULT_SIZE - 1 : activeIndex - 1;
51
+
52
+ const currentElm = document.getElementById(`time-${prevIndex}`);
53
+
54
+ currentElm?.focus();
55
+
56
+ setActiveIndex(prevIndex);
57
+ };
58
+
59
+ const setElement = () => {
60
+ const currentElm = document.getElementById(`time-${activeIndex}`);
61
+
62
+ if (!currentElm) return;
63
+
64
+ currentElm.focus();
65
+
66
+ formateSelectedTime((currentElm.textContent ?? "") as TimeString);
67
+ };
68
+
69
+ const reset = () => {
70
+ const currentElm = document.getElementById(`time-${activeIndex}`);
71
+ currentElm?.blur();
72
+ setActiveIndex(-1);
73
+ };
74
+
75
+ switch (e.key) {
76
+ case "ArrowUp":
77
+ movePrev();
78
+ break;
79
+
80
+ case "ArrowDown":
81
+ moveNext();
82
+ break;
83
+
84
+ case "Escape":
85
+ reset();
86
+ break;
87
+
88
+ case "Enter":
89
+ setElement();
90
+ break;
91
+ }
92
+ },
93
+ [activeIndex, formateSelectedTime]
94
+ );
95
+
96
+ const handleClick = useCallback(
97
+ (time: TimeString, currentIndex: number) => {
98
+ formateSelectedTime(time);
99
+ setActiveIndex(currentIndex);
100
+ },
101
+ [formateSelectedTime]
102
+ );
103
+
104
+ const currentTime = useMemo(() => {
105
+ const timeVal = time.split(" ")[0];
106
+ return {
107
+ hours: parseInt(timeVal.split(":")[0]),
108
+ minutes: parseInt(timeVal.split(":")[1]),
109
+ };
110
+ }, [time]);
111
+
112
+ useEffect(() => {
113
+ const getCurrentElementTime = () => {
114
+ const timeVal = time.split(" ")[0];
115
+ const hours = parseInt(timeVal.split(":")[0]);
116
+ const minutes = parseInt(timeVal.split(":")[1]);
117
+ const PM_AM = time.split(" ")[1];
118
+
119
+ const formatIndex = PM_AM === "AM" ? hours : hours === 12 ? hours : hours + 12;
120
+ const formattedHours = formatIndex;
121
+
122
+ for (let j = 0; j <= 3; j++) {
123
+ const diff = Math.abs(j * timestamp - minutes);
124
+ const selected =
125
+ PM_AM === (formattedHours >= 12 ? "PM" : "AM") &&
126
+ (minutes <= 53 ? diff < Math.ceil(timestamp / 2) : diff < timestamp);
127
+
128
+ if (selected) {
129
+ const trueIndex = activeIndex === -1 ? formattedHours * 4 + j : activeIndex;
130
+
131
+ setActiveIndex(trueIndex);
132
+
133
+ const currentElm = document.getElementById(`time-${trueIndex}`);
134
+ currentElm?.scrollIntoView({
135
+ block: "center",
136
+ behavior: "smooth",
137
+ });
138
+ }
139
+ }
140
+ };
141
+
142
+ getCurrentElementTime();
143
+ }, [time, activeIndex]);
144
+
145
+ const height = useMemo(() => {
146
+ if (!document) return;
147
+ const calendarElm = document.getElementById("calendar");
148
+ if (!calendarElm) return;
149
+ return calendarElm.style.height;
150
+ }, []);
151
+
152
+ return (
153
+ <div className="space-y-2 pr-3 py-3 relative ">
154
+ <h3 className="text-sm font-medium ">Time</h3>
155
+ <ScrollArea
156
+ onKeyDown={handleKeydown}
157
+ className={cn(getTimePickerScrollAreaClasses())}
158
+ style={{ height }}
159
+ >
160
+ <ul className={cn(getTimePickerListClasses())}>
161
+ {Array.from({ length: 24 }).map((_, i) => {
162
+ const PM_AM = i >= 12 ? "PM" : "AM";
163
+ const formatIndex = i > 12 ? i % 12 : i === 0 || i === 12 ? 12 : i;
164
+ return Array.from({ length: 4 }).map((_, part) => {
165
+ const diff = Math.abs(part * timestamp - currentTime.minutes);
166
+
167
+ const trueIndex = i * 4 + part;
168
+
169
+ // ? refactor : add the select of the default time on the current device (H:MM)
170
+ const isSelected =
171
+ (currentTime.hours === i || currentTime.hours === formatIndex) &&
172
+ time.split(" ")[1] === PM_AM &&
173
+ (currentTime.minutes <= 53 ? diff < Math.ceil(timestamp / 2) : diff < timestamp);
174
+
175
+ const isSuggested = !value && isSelected;
176
+
177
+ const currentValue = `${formatIndex}:${
178
+ part === 0 ? "00" : timestamp * part
179
+ } ${PM_AM}` as TimeString;
180
+
181
+ return (
182
+ <li
183
+ tabIndex={isSelected ? 0 : -1}
184
+ id={`time-${trueIndex}`}
185
+ key={`time-${trueIndex}`}
186
+ aria-label={currentValue}
187
+ className={cn(
188
+ getTimePickerClasses({ selected: isSelected, suggested: isSuggested })
189
+ )}
190
+ onClick={() => handleClick(currentValue, trueIndex)}
191
+ onFocus={() => isSuggested && setActiveIndex(trueIndex)}
192
+ >
193
+ {currentValue}
194
+ </li>
195
+ );
196
+ });
197
+ })}
198
+ </ul>
199
+ </ScrollArea>
200
+ </div>
201
+ );
202
+ };
@@ -0,0 +1,135 @@
1
+ import { cva } from "../../../libs";
2
+
3
+ export const DEFAULT_SIZE = 96;
4
+
5
+ export const getDatetimeInputContainerClasses = cva(
6
+ `h-10 flex items-center justify-between w-fit px-2 rounded-md transition-all gap-1
7
+ [&:has(input:focus)]:duration-100 [&:has(input:focus)]:outline [&:has(input:focus)]:outline-2 [&:has(input:focus)]:outline-offset-2 [&:has(input:focus)]:outline-base-content/20
8
+ [&:has(input:focus-within)]:duration-100 [&:has(input:focus-within)]:outline [&:has(input:focus-within)]:outline-2 [&:has(input:focus-within)]:outline-offset-2 [&:has(input:focus-within)]:outline-base-content/20
9
+ `,
10
+ {
11
+ variants: {
12
+ variant: {
13
+ default: "border-0",
14
+ accent: "border border-accent",
15
+ error: "border border-error",
16
+ ghost: "border border-ghost",
17
+ info: "border border-info",
18
+ primary: "border border-primary",
19
+ secondary: "border border-secondary",
20
+ success: "border border-success",
21
+ warning: "border border-warning",
22
+ },
23
+ outline: {
24
+ true: "border",
25
+ },
26
+ disabled: {
27
+ true: "border-base-300",
28
+ },
29
+ wide: {
30
+ true: "w-full",
31
+ },
32
+ size: {
33
+ xs: "h-6 leading-relaxed text-xs px-1",
34
+ sm: "h-8 leading-8 text-sm px-1",
35
+ md: "h-12 leading-loose text-sm px-2",
36
+ lg: "h-16 leading-loose text-lg px-3",
37
+ },
38
+ },
39
+ compoundVariants: [
40
+ {
41
+ size: undefined,
42
+ className: "min-h-fit h-10",
43
+ },
44
+ {
45
+ variant: undefined,
46
+ outline: true,
47
+ className: "border-neutral-content",
48
+ },
49
+ {
50
+ variant: "default",
51
+ outline: true,
52
+ className: "border-base-content",
53
+ },
54
+ ],
55
+ }
56
+ );
57
+
58
+ export const getDatetimeCalendarTriggerClasses = cva(
59
+ `size-8 rounded-md p-1 flex items-center justify-center font-normal
60
+ outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-base-content focus-visible:ring-inset
61
+ `,
62
+ {
63
+ variants: {
64
+ size: {
65
+ xs: "size-4 p-0 rounded-sm",
66
+ sm: "size-6",
67
+ md: "size-9",
68
+ lg: "size-10",
69
+ },
70
+ },
71
+ }
72
+ );
73
+
74
+ export const getDatetimeCalendarIconClasses = cva("", {
75
+ variants: {
76
+ size: {
77
+ xs: "size-3",
78
+ sm: "size-3",
79
+ md: "size-5",
80
+ lg: "size-6",
81
+ },
82
+ },
83
+ compoundVariants: [
84
+ {
85
+ size: undefined,
86
+ className: "size-4",
87
+ },
88
+ ],
89
+ });
90
+
91
+ export const getTimePickerClasses = cva(
92
+ "border border-neutral-content bg-base-100 shadow-sm cursor-pointer ring-0 py-2 h-8 px-3 w-full text-sm outline-0 inline-flex items-center justify-center whitespace-nowrap rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-base-content disabled:pointer-events-none disabled:opacity-50 hover:bg-base-200",
93
+ {
94
+ variants: {
95
+ selected: {
96
+ true: "bg-primary border-primary text-primary-content hover:border-neutral-content hover:text-base-content",
97
+ },
98
+ suggested: {
99
+ true: "bg-info border-info text-info-content hover:border-neutral-content hover:text-base-content",
100
+ },
101
+ },
102
+ }
103
+ );
104
+
105
+ export const getTimePickerListClasses = cva(
106
+ "flex items-center flex-col gap-1 h-full max-h-56 w-28 px-1 py-0.5"
107
+ );
108
+
109
+ export const getTimePickerScrollAreaClasses = cva(
110
+ "h-[90%] w-full focus-visible:outline-0 focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:border-0 py-0.5"
111
+ );
112
+
113
+ export const getNaturalLanguageInputClasses = cva(
114
+ "flex-1 border-none rounded bg-transparent outline-none ring-0 focus:outline-none focus:ring-0 focus-within:outline-none focus-within:ring-0 disabled:cursor-not-allowed disabled:opacity-50",
115
+ {
116
+ variants: {
117
+ size: {
118
+ xs: "h-4 px-1",
119
+ sm: "h-6 px-1",
120
+ md: "h-9 px-2",
121
+ lg: "h-11 px-3",
122
+ },
123
+ },
124
+ compoundVariants: [
125
+ {
126
+ size: undefined,
127
+ className: "h-8 px-2",
128
+ },
129
+ ],
130
+ }
131
+ );
132
+
133
+ export const getDatetimeCalendarClasses = cva(
134
+ "peer flex justify-end bg-transparent focus:outline-none focus:ring-0 focus-within:outline-none focus-within:ring-0 sm:text-sm disabled:cursor-not-allowed disabled:opacity-50"
135
+ );
@@ -0,0 +1,93 @@
1
+ import { parseDate } from "chrono-node";
2
+ import { type TimeString } from "./types";
3
+
4
+ /**
5
+ * Utility function that parses dates.
6
+ * Parses a given date string using the `chrono-node` library.
7
+ *
8
+ * @param str - A string representation of a date and time.
9
+ * @returns A `Date` object representing the parsed date and time, or `null` if the string could not be parsed.
10
+ */
11
+ export const parseDateTime = (str: Date | string) => {
12
+ if (str instanceof Date) {
13
+ return str;
14
+ }
15
+
16
+ return parseDate(str) ?? undefined;
17
+ };
18
+
19
+ /**
20
+ * Converts a given timestamp or the current date and time to a string representation in the local time zone.
21
+ * format: `HH:mm`, adjusted for the local time zone.
22
+ *
23
+ * @param timestamp {Date | string}
24
+ * @returns A string representation of the timestamp
25
+ */
26
+ export const getDateTimeLocal = (timestamp?: Date): string => {
27
+ const d = timestamp ? new Date(timestamp) : new Date();
28
+ if (d.toString() === "Invalid Date") return "";
29
+ return new Date(d.getTime() - d.getTimezoneOffset() * 60000)
30
+ .toISOString()
31
+ .split(":")
32
+ .slice(0, 2)
33
+ .join(":");
34
+ };
35
+
36
+ /**
37
+ * Formats a given date and time object or string into a human-readable string representation.
38
+ * "MMM D, YYYY h:mm A" (e.g. "Jan 1, 2023 12:00 PM").
39
+ *
40
+ * @param date - {Date | string}
41
+ * @returns A string representation of the date and time
42
+ */
43
+ export const formatDateTime = (date: Date | string, locale?: Intl.LocalesArgument) => {
44
+ return new Date(date).toLocaleTimeString(locale, {
45
+ month: "short",
46
+ day: "numeric",
47
+ year: "numeric",
48
+ hour: "numeric",
49
+ minute: "numeric",
50
+ hour12: true,
51
+ });
52
+ };
53
+
54
+ export const getParsedTime = (date: Date): TimeString => {
55
+ if (date) {
56
+ const PM_AM = date.getHours() >= 12 ? "PM" : "AM";
57
+ //fix the time format for this value
58
+
59
+ const PM_AM_hour = date.getHours();
60
+ const minute = date.getMinutes();
61
+
62
+ const hour =
63
+ PM_AM_hour > 12 ? PM_AM_hour % 12 : PM_AM_hour === 0 || PM_AM_hour === 12 ? 12 : PM_AM_hour;
64
+
65
+ return `${hour}:${minute ? minute : "00"} ${PM_AM}` as TimeString;
66
+ }
67
+
68
+ return "";
69
+ };
70
+
71
+ export const setDateTime = (date: Date, time: TimeString) => {
72
+ if (!time) {
73
+ return date;
74
+ }
75
+
76
+ const [HHMM, PM_AM] = time.split(" ");
77
+ const [HH, MM] = HHMM.split(":").map((v) => parseInt(v) || 0);
78
+
79
+ let hour = HH;
80
+ if (HH === 12) {
81
+ if (PM_AM === "AM") {
82
+ hour = 0;
83
+ }
84
+ } else if (HH < 12) {
85
+ if (PM_AM === "PM") {
86
+ hour += 12;
87
+ }
88
+ }
89
+ const newDate = new Date(date);
90
+ newDate.setHours(hour, MM);
91
+
92
+ return newDate;
93
+ };
@@ -0,0 +1 @@
1
+ export { useDateInput } from "./useDateInput";
@@ -0,0 +1 @@
1
+ export { useDateInput } from "./useDateInput";
@@ -0,0 +1,10 @@
1
+ import { useContext } from "react";
2
+ import { DatetimeInputContext } from "../../DatetimeInputContext";
3
+
4
+ export const useDateInput = () => {
5
+ const context = useContext(DatetimeInputContext);
6
+ if (!context) {
7
+ throw new Error("useDateInput must be used within SmartDateInputProvider");
8
+ }
9
+ return context;
10
+ };
@@ -0,0 +1 @@
1
+ export { DatetimeInput } from "./DatetimeInput";