kmod-cli 1.0.10
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.
- package/README.md +53 -0
- package/bin/gen-components.js +68 -0
- package/bin/index.js +153 -0
- package/component-templates/components/access-denied.tsx +130 -0
- package/component-templates/components/breadcumb.tsx +42 -0
- package/component-templates/components/count-down.tsx +94 -0
- package/component-templates/components/count-input.tsx +221 -0
- package/component-templates/components/date-range-calendar/button.tsx +61 -0
- package/component-templates/components/date-range-calendar/calendar.tsx +132 -0
- package/component-templates/components/date-range-calendar/date-input.tsx +259 -0
- package/component-templates/components/date-range-calendar/date-range-picker.tsx +594 -0
- package/component-templates/components/date-range-calendar/label.tsx +31 -0
- package/component-templates/components/date-range-calendar/popover.tsx +32 -0
- package/component-templates/components/date-range-calendar/select.tsx +125 -0
- package/component-templates/components/date-range-calendar/switch.tsx +30 -0
- package/component-templates/components/datetime-picker/button.tsx +61 -0
- package/component-templates/components/datetime-picker/calendar.tsx +156 -0
- package/component-templates/components/datetime-picker/datetime-picker.tsx +75 -0
- package/component-templates/components/datetime-picker/input.tsx +20 -0
- package/component-templates/components/datetime-picker/label.tsx +18 -0
- package/component-templates/components/datetime-picker/period-input.tsx +62 -0
- package/component-templates/components/datetime-picker/popover.tsx +32 -0
- package/component-templates/components/datetime-picker/select.tsx +125 -0
- package/component-templates/components/datetime-picker/time-picker-input.tsx +131 -0
- package/component-templates/components/datetime-picker/time-picker-utils.tsx +204 -0
- package/component-templates/components/datetime-picker/time-picker.tsx +59 -0
- package/component-templates/components/gradient-outline.tsx +233 -0
- package/component-templates/components/gradient-svg.tsx +157 -0
- package/component-templates/components/grid-layout.tsx +69 -0
- package/component-templates/components/hydrate-guard.tsx +40 -0
- package/component-templates/components/image.tsx +92 -0
- package/component-templates/components/loader-slash-gradient.tsx +85 -0
- package/component-templates/components/masonry-gallery.tsx +221 -0
- package/component-templates/components/modal.tsx +110 -0
- package/component-templates/components/multi-select.tsx +447 -0
- package/component-templates/components/non-hydration.tsx +27 -0
- package/component-templates/components/portal.tsx +34 -0
- package/component-templates/components/segments-circle.tsx +235 -0
- package/component-templates/components/single-select.tsx +248 -0
- package/component-templates/components/stroke-circle.tsx +57 -0
- package/component-templates/components/table/column-table.tsx +15 -0
- package/component-templates/components/table/data-table.tsx +339 -0
- package/component-templates/components/table/readme.tsx +95 -0
- package/component-templates/components/table/table.tsx +60 -0
- package/component-templates/components/text-hover-effect.tsx +120 -0
- package/component-templates/components/timout-loader.tsx +52 -0
- package/component-templates/components/toast.tsx +994 -0
- package/component-templates/configs/config.ts +33 -0
- package/component-templates/configs/feature-config.tsx +432 -0
- package/component-templates/configs/keys.ts +7 -0
- package/component-templates/core/api-service.ts +202 -0
- package/component-templates/core/calculate.ts +18 -0
- package/component-templates/core/idb.ts +166 -0
- package/component-templates/core/storage.ts +213 -0
- package/component-templates/hooks/count-down.ts +38 -0
- package/component-templates/hooks/fade-on-scroll.ts +52 -0
- package/component-templates/hooks/safe-action.ts +59 -0
- package/component-templates/hooks/spam-guard.ts +31 -0
- package/component-templates/lib/utils.ts +6 -0
- package/component-templates/providers/feature-guard.tsx +432 -0
- package/component-templates/queries/query.tsx +775 -0
- package/component-templates/utils/colors/color-by-text.ts +307 -0
- package/component-templates/utils/colors/stripe-effect.ts +100 -0
- package/component-templates/utils/hash/hash-aes.ts +35 -0
- package/components.json +348 -0
- package/package.json +60 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
CheckIcon,
|
|
7
|
+
ChevronDownIcon,
|
|
8
|
+
} from '@radix-ui/react-icons';
|
|
9
|
+
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
10
|
+
|
|
11
|
+
import { cn } from '../../lib/utils';
|
|
12
|
+
|
|
13
|
+
const Select = SelectPrimitive.Root
|
|
14
|
+
|
|
15
|
+
const SelectGroup = SelectPrimitive.Group
|
|
16
|
+
|
|
17
|
+
const SelectValue = SelectPrimitive.Value
|
|
18
|
+
|
|
19
|
+
const SelectTrigger = React.forwardRef<
|
|
20
|
+
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
21
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
|
22
|
+
>(({ className, children, ...props }, ref) => (
|
|
23
|
+
<SelectPrimitive.Trigger
|
|
24
|
+
ref={ref}
|
|
25
|
+
className={cn(
|
|
26
|
+
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
|
27
|
+
className
|
|
28
|
+
)}
|
|
29
|
+
{...props}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
<SelectPrimitive.Icon asChild>
|
|
33
|
+
<ChevronDownIcon className="h-4 w-4 opacity-50" />
|
|
34
|
+
</SelectPrimitive.Icon>
|
|
35
|
+
</SelectPrimitive.Trigger>
|
|
36
|
+
))
|
|
37
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
|
38
|
+
|
|
39
|
+
const SelectContent = React.forwardRef<
|
|
40
|
+
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
41
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
|
42
|
+
>(({ className, children, position = 'popper', ...props }, ref) => (
|
|
43
|
+
<SelectPrimitive.Portal>
|
|
44
|
+
<SelectPrimitive.Content
|
|
45
|
+
ref={ref}
|
|
46
|
+
className={cn(
|
|
47
|
+
'relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
48
|
+
position === 'popper' &&
|
|
49
|
+
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
|
50
|
+
className
|
|
51
|
+
)}
|
|
52
|
+
position={position}
|
|
53
|
+
{...props}
|
|
54
|
+
>
|
|
55
|
+
<SelectPrimitive.Viewport
|
|
56
|
+
className={cn(
|
|
57
|
+
'p-1',
|
|
58
|
+
position === 'popper' &&
|
|
59
|
+
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
|
|
60
|
+
)}
|
|
61
|
+
>
|
|
62
|
+
{children}
|
|
63
|
+
</SelectPrimitive.Viewport>
|
|
64
|
+
</SelectPrimitive.Content>
|
|
65
|
+
</SelectPrimitive.Portal>
|
|
66
|
+
))
|
|
67
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName
|
|
68
|
+
|
|
69
|
+
const SelectLabel = React.forwardRef<
|
|
70
|
+
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
71
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
|
72
|
+
>(({ className, ...props }, ref) => (
|
|
73
|
+
<SelectPrimitive.Label
|
|
74
|
+
ref={ref}
|
|
75
|
+
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
))
|
|
79
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
|
80
|
+
|
|
81
|
+
const SelectItem = React.forwardRef<
|
|
82
|
+
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
83
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
|
84
|
+
>(({ className, children, ...props }, ref) => (
|
|
85
|
+
<SelectPrimitive.Item
|
|
86
|
+
ref={ref}
|
|
87
|
+
className={cn(
|
|
88
|
+
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
89
|
+
className
|
|
90
|
+
)}
|
|
91
|
+
{...props}
|
|
92
|
+
>
|
|
93
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
94
|
+
<SelectPrimitive.ItemIndicator>
|
|
95
|
+
<CheckIcon className="h-4 w-4" />
|
|
96
|
+
</SelectPrimitive.ItemIndicator>
|
|
97
|
+
</span>
|
|
98
|
+
|
|
99
|
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
100
|
+
</SelectPrimitive.Item>
|
|
101
|
+
))
|
|
102
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName
|
|
103
|
+
|
|
104
|
+
const SelectSeparator = React.forwardRef<
|
|
105
|
+
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
106
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
|
107
|
+
>(({ className, ...props }, ref) => (
|
|
108
|
+
<SelectPrimitive.Separator
|
|
109
|
+
ref={ref}
|
|
110
|
+
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
|
111
|
+
{...props}
|
|
112
|
+
/>
|
|
113
|
+
))
|
|
114
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
|
115
|
+
|
|
116
|
+
export {
|
|
117
|
+
Select,
|
|
118
|
+
SelectContent,
|
|
119
|
+
SelectGroup,
|
|
120
|
+
SelectItem,
|
|
121
|
+
SelectLabel,
|
|
122
|
+
SelectSeparator,
|
|
123
|
+
SelectTrigger,
|
|
124
|
+
SelectValue,
|
|
125
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../lib/utils';
|
|
4
|
+
import { Input } from './input';
|
|
5
|
+
import {
|
|
6
|
+
getArrowByType,
|
|
7
|
+
getDateByType,
|
|
8
|
+
Period,
|
|
9
|
+
setDateByType,
|
|
10
|
+
TimePickerType,
|
|
11
|
+
} from './time-picker-utils';
|
|
12
|
+
|
|
13
|
+
export interface TimePickerInputProps
|
|
14
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
15
|
+
picker: TimePickerType;
|
|
16
|
+
date: Date | undefined;
|
|
17
|
+
setDate: (date: Date | undefined) => void;
|
|
18
|
+
period?: Period;
|
|
19
|
+
onRightFocus?: () => void;
|
|
20
|
+
onLeftFocus?: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const TimePickerInput = React.forwardRef<
|
|
24
|
+
HTMLInputElement,
|
|
25
|
+
TimePickerInputProps
|
|
26
|
+
>(
|
|
27
|
+
(
|
|
28
|
+
{
|
|
29
|
+
className,
|
|
30
|
+
type = "tel",
|
|
31
|
+
value,
|
|
32
|
+
id,
|
|
33
|
+
name,
|
|
34
|
+
date = new Date(new Date().setHours(0, 0, 0, 0)),
|
|
35
|
+
setDate,
|
|
36
|
+
onChange,
|
|
37
|
+
onKeyDown,
|
|
38
|
+
picker,
|
|
39
|
+
period,
|
|
40
|
+
onLeftFocus,
|
|
41
|
+
onRightFocus,
|
|
42
|
+
...props
|
|
43
|
+
},
|
|
44
|
+
ref
|
|
45
|
+
) => {
|
|
46
|
+
const [flag, setFlag] = React.useState<boolean>(false);
|
|
47
|
+
const [prevIntKey, setPrevIntKey] = React.useState<string>("0");
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* allow the user to enter the second digit within 2 seconds
|
|
51
|
+
* otherwise start again with entering first digit
|
|
52
|
+
*/
|
|
53
|
+
React.useEffect(() => {
|
|
54
|
+
if (flag) {
|
|
55
|
+
const timer = setTimeout(() => {
|
|
56
|
+
setFlag(false);
|
|
57
|
+
}, 2000);
|
|
58
|
+
|
|
59
|
+
return () => clearTimeout(timer);
|
|
60
|
+
}
|
|
61
|
+
}, [flag]);
|
|
62
|
+
|
|
63
|
+
const calculatedValue = React.useMemo(() => {
|
|
64
|
+
return getDateByType(date, picker);
|
|
65
|
+
}, [date, picker]);
|
|
66
|
+
|
|
67
|
+
const calculateNewValue = (key: string) => {
|
|
68
|
+
/*
|
|
69
|
+
* If picker is '12hours' and the first digit is 0, then the second digit is automatically set to 1.
|
|
70
|
+
* The second entered digit will break the condition and the value will be set to 10-12.
|
|
71
|
+
*/
|
|
72
|
+
if (picker === "12hours") {
|
|
73
|
+
if (flag && calculatedValue.slice(1, 2) === "1" && prevIntKey === "0")
|
|
74
|
+
return "0" + key;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return !flag ? "0" + key : calculatedValue.slice(1, 2) + key;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
81
|
+
if (e.key === "Tab") return;
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
if (e.key === "ArrowRight") onRightFocus?.();
|
|
84
|
+
if (e.key === "ArrowLeft") onLeftFocus?.();
|
|
85
|
+
if (["ArrowUp", "ArrowDown"].includes(e.key)) {
|
|
86
|
+
const step = e.key === "ArrowUp" ? 1 : -1;
|
|
87
|
+
const newValue = getArrowByType(calculatedValue, step, picker);
|
|
88
|
+
if (flag) setFlag(false);
|
|
89
|
+
const tempDate = new Date(date);
|
|
90
|
+
setDate(setDateByType(tempDate, newValue, picker, period));
|
|
91
|
+
}
|
|
92
|
+
if (e.key >= "0" && e.key <= "9") {
|
|
93
|
+
if (picker === "12hours") setPrevIntKey(e.key);
|
|
94
|
+
|
|
95
|
+
const newValue = calculateNewValue(e.key);
|
|
96
|
+
if (flag) onRightFocus?.();
|
|
97
|
+
setFlag((prev) => !prev);
|
|
98
|
+
const tempDate = new Date(date);
|
|
99
|
+
setDate(setDateByType(tempDate, newValue, picker, period));
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Input
|
|
105
|
+
ref={ref}
|
|
106
|
+
id={id || picker}
|
|
107
|
+
name={name || picker}
|
|
108
|
+
className={cn(
|
|
109
|
+
"w-[48px] text-center font-mono shadow-none text-base tabular-nums caret-transparent focus:!border focus:!border-primary focus:text-accent-foreground [&::-webkit-inner-spin-button]:appearance-none",
|
|
110
|
+
className
|
|
111
|
+
)}
|
|
112
|
+
value={value || calculatedValue}
|
|
113
|
+
onChange={(e) => {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
onChange?.(e);
|
|
116
|
+
}}
|
|
117
|
+
type={type}
|
|
118
|
+
inputMode="decimal"
|
|
119
|
+
onKeyDown={(e) => {
|
|
120
|
+
onKeyDown?.(e);
|
|
121
|
+
handleKeyDown(e);
|
|
122
|
+
}}
|
|
123
|
+
{...props}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
TimePickerInput.displayName = "TimePickerInput";
|
|
130
|
+
|
|
131
|
+
export { TimePickerInput };
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* regular expression to check for valid hour format (01-23)
|
|
3
|
+
*/
|
|
4
|
+
export function isValidHour(value: string) {
|
|
5
|
+
return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* regular expression to check for valid 12 hour format (01-12)
|
|
10
|
+
*/
|
|
11
|
+
export function isValid12Hour(value: string) {
|
|
12
|
+
return /^(0[1-9]|1[0-2])$/.test(value);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* regular expression to check for valid minute format (00-59)
|
|
17
|
+
*/
|
|
18
|
+
export function isValidMinuteOrSecond(value: string) {
|
|
19
|
+
return /^[0-5][0-9]$/.test(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type GetValidNumberConfig = { max: number; min?: number; loop?: boolean };
|
|
23
|
+
|
|
24
|
+
export function getValidNumber(
|
|
25
|
+
value: string,
|
|
26
|
+
{ max, min = 0, loop = false }: GetValidNumberConfig
|
|
27
|
+
) {
|
|
28
|
+
let numericValue = parseInt(value, 10);
|
|
29
|
+
|
|
30
|
+
if (!isNaN(numericValue)) {
|
|
31
|
+
if (!loop) {
|
|
32
|
+
if (numericValue > max) numericValue = max;
|
|
33
|
+
if (numericValue < min) numericValue = min;
|
|
34
|
+
} else {
|
|
35
|
+
if (numericValue > max) numericValue = min;
|
|
36
|
+
if (numericValue < min) numericValue = max;
|
|
37
|
+
}
|
|
38
|
+
return numericValue.toString().padStart(2, "0");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return "00";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getValidHour(value: string) {
|
|
45
|
+
if (isValidHour(value)) return value;
|
|
46
|
+
return getValidNumber(value, { max: 23 });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function getValid12Hour(value: string) {
|
|
50
|
+
if (isValid12Hour(value)) return value;
|
|
51
|
+
return getValidNumber(value, { min: 1, max: 12 });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function getValidMinuteOrSecond(value: string) {
|
|
55
|
+
if (isValidMinuteOrSecond(value)) return value;
|
|
56
|
+
return getValidNumber(value, { max: 59 });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type GetValidArrowNumberConfig = {
|
|
60
|
+
min: number;
|
|
61
|
+
max: number;
|
|
62
|
+
step: number;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export function getValidArrowNumber(
|
|
66
|
+
value: string,
|
|
67
|
+
{ min, max, step }: GetValidArrowNumberConfig
|
|
68
|
+
) {
|
|
69
|
+
let numericValue = parseInt(value, 10);
|
|
70
|
+
if (!isNaN(numericValue)) {
|
|
71
|
+
numericValue += step;
|
|
72
|
+
return getValidNumber(String(numericValue), { min, max, loop: true });
|
|
73
|
+
}
|
|
74
|
+
return "00";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function getValidArrowHour(value: string, step: number) {
|
|
78
|
+
return getValidArrowNumber(value, { min: 0, max: 23, step });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function getValidArrow12Hour(value: string, step: number) {
|
|
82
|
+
return getValidArrowNumber(value, { min: 1, max: 12, step });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function getValidArrowMinuteOrSecond(value: string, step: number) {
|
|
86
|
+
return getValidArrowNumber(value, { min: 0, max: 59, step });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function setMinutes(date: Date, value: string) {
|
|
90
|
+
const minutes = getValidMinuteOrSecond(value);
|
|
91
|
+
date.setMinutes(parseInt(minutes, 10));
|
|
92
|
+
return date;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function setSeconds(date: Date, value: string) {
|
|
96
|
+
const seconds = getValidMinuteOrSecond(value);
|
|
97
|
+
date.setSeconds(parseInt(seconds, 10));
|
|
98
|
+
return date;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function setHours(date: Date, value: string) {
|
|
102
|
+
const hours = getValidHour(value);
|
|
103
|
+
date.setHours(parseInt(hours, 10));
|
|
104
|
+
return date;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function set12Hours(date: Date, value: string, period: Period) {
|
|
108
|
+
const hours = parseInt(getValid12Hour(value), 10);
|
|
109
|
+
const convertedHours = convert12HourTo24Hour(hours, period);
|
|
110
|
+
date.setHours(convertedHours);
|
|
111
|
+
return date;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type TimePickerType = "minutes" | "seconds" | "hours" | "12hours";
|
|
115
|
+
export type Period = "AM" | "PM";
|
|
116
|
+
|
|
117
|
+
export function setDateByType(
|
|
118
|
+
date: Date,
|
|
119
|
+
value: string,
|
|
120
|
+
type: TimePickerType,
|
|
121
|
+
period?: Period
|
|
122
|
+
) {
|
|
123
|
+
switch (type) {
|
|
124
|
+
case "minutes":
|
|
125
|
+
return setMinutes(date, value);
|
|
126
|
+
case "seconds":
|
|
127
|
+
return setSeconds(date, value);
|
|
128
|
+
case "hours":
|
|
129
|
+
return setHours(date, value);
|
|
130
|
+
case "12hours": {
|
|
131
|
+
if (!period) return date;
|
|
132
|
+
return set12Hours(date, value, period);
|
|
133
|
+
}
|
|
134
|
+
default:
|
|
135
|
+
return date;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function getDateByType(date: Date, type: TimePickerType) {
|
|
140
|
+
switch (type) {
|
|
141
|
+
case "minutes":
|
|
142
|
+
return getValidMinuteOrSecond(String(date.getMinutes()));
|
|
143
|
+
case "seconds":
|
|
144
|
+
return getValidMinuteOrSecond(String(date.getSeconds()));
|
|
145
|
+
case "hours":
|
|
146
|
+
return getValidHour(String(date.getHours()));
|
|
147
|
+
case "12hours":
|
|
148
|
+
const hours = display12HourValue(date.getHours());
|
|
149
|
+
return getValid12Hour(String(hours));
|
|
150
|
+
default:
|
|
151
|
+
return "00";
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function getArrowByType(
|
|
156
|
+
value: string,
|
|
157
|
+
step: number,
|
|
158
|
+
type: TimePickerType
|
|
159
|
+
) {
|
|
160
|
+
switch (type) {
|
|
161
|
+
case "minutes":
|
|
162
|
+
return getValidArrowMinuteOrSecond(value, step);
|
|
163
|
+
case "seconds":
|
|
164
|
+
return getValidArrowMinuteOrSecond(value, step);
|
|
165
|
+
case "hours":
|
|
166
|
+
return getValidArrowHour(value, step);
|
|
167
|
+
case "12hours":
|
|
168
|
+
return getValidArrow12Hour(value, step);
|
|
169
|
+
default:
|
|
170
|
+
return "00";
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* handles value change of 12-hour input
|
|
176
|
+
* 12:00 PM is 12:00
|
|
177
|
+
* 12:00 AM is 00:00
|
|
178
|
+
*/
|
|
179
|
+
export function convert12HourTo24Hour(hour: number, period: Period) {
|
|
180
|
+
if (period === "PM") {
|
|
181
|
+
if (hour <= 11) {
|
|
182
|
+
return hour + 12;
|
|
183
|
+
} else {
|
|
184
|
+
return hour;
|
|
185
|
+
}
|
|
186
|
+
} else if (period === "AM") {
|
|
187
|
+
if (hour === 12) return 0;
|
|
188
|
+
return hour;
|
|
189
|
+
}
|
|
190
|
+
return hour;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* time is stored in the 24-hour form,
|
|
195
|
+
* but needs to be displayed to the user
|
|
196
|
+
* in its 12-hour representation
|
|
197
|
+
*/
|
|
198
|
+
export function display12HourValue(hours: number) {
|
|
199
|
+
if (hours === 0 || hours === 12) return "12";
|
|
200
|
+
if (hours >= 22) return `${hours - 12}`;
|
|
201
|
+
if (hours % 12 > 9) return `${hours}`;
|
|
202
|
+
return `0${hours % 12}`;
|
|
203
|
+
}
|
|
204
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
import { Label } from './label';
|
|
6
|
+
import { TimePickerInput } from './time-picker-input';
|
|
7
|
+
|
|
8
|
+
interface TimePickerProps {
|
|
9
|
+
date: Date | undefined;
|
|
10
|
+
setDate: (date: Date | undefined) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function TimePicker({ date, setDate }: TimePickerProps) {
|
|
14
|
+
const minuteRef = React.useRef<HTMLInputElement>(null);
|
|
15
|
+
const hourRef = React.useRef<HTMLInputElement>(null);
|
|
16
|
+
const secondRef = React.useRef<HTMLInputElement>(null);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="flex items-end gap-2">
|
|
20
|
+
<div className="grid gap-1 text-center">
|
|
21
|
+
<Label htmlFor="hours" className="text-xs">
|
|
22
|
+
Hours
|
|
23
|
+
</Label>
|
|
24
|
+
<TimePickerInput
|
|
25
|
+
picker="hours"
|
|
26
|
+
date={date}
|
|
27
|
+
setDate={setDate}
|
|
28
|
+
ref={hourRef}
|
|
29
|
+
onRightFocus={() => minuteRef.current?.focus()}
|
|
30
|
+
/>
|
|
31
|
+
</div>
|
|
32
|
+
<div className="grid gap-1 text-center">
|
|
33
|
+
<Label htmlFor="minutes" className="text-xs">
|
|
34
|
+
Minutes
|
|
35
|
+
</Label>
|
|
36
|
+
<TimePickerInput
|
|
37
|
+
picker="minutes"
|
|
38
|
+
date={date}
|
|
39
|
+
setDate={setDate}
|
|
40
|
+
ref={minuteRef}
|
|
41
|
+
onLeftFocus={() => hourRef.current?.focus()}
|
|
42
|
+
onRightFocus={() => secondRef.current?.focus()}
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
45
|
+
<div className="grid gap-1 text-center">
|
|
46
|
+
<Label htmlFor="seconds" className="text-xs">
|
|
47
|
+
Seconds
|
|
48
|
+
</Label>
|
|
49
|
+
<TimePickerInput
|
|
50
|
+
picker="seconds"
|
|
51
|
+
date={date}
|
|
52
|
+
setDate={setDate}
|
|
53
|
+
ref={secondRef}
|
|
54
|
+
onLeftFocus={() => minuteRef.current?.focus()}
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|