next-helios-fe 1.0.0
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/@types/index.d.ts +2 -0
- package/dist/components/button/index.d.ts +15 -0
- package/dist/components/calendar/big-calendar/event.d.ts +7 -0
- package/dist/components/calendar/big-calendar/index.d.ts +14 -0
- package/dist/components/calendar/big-calendar/toolbar.d.ts +8 -0
- package/dist/components/calendar/calendar/index.d.ts +11 -0
- package/dist/components/chart/index.d.ts +17 -0
- package/dist/components/chip/index.d.ts +12 -0
- package/dist/components/content-container/accordion/index.d.ts +10 -0
- package/dist/components/content-container/accordion/item.d.ts +6 -0
- package/dist/components/content-container/card.d.ts +10 -0
- package/dist/components/content-container/carousel.d.ts +10 -0
- package/dist/components/content-container/drawer.d.ts +8 -0
- package/dist/components/content-container/modal/index.d.ts +21 -0
- package/dist/components/content-container/sortable/index.d.ts +14 -0
- package/dist/components/content-container/sortable/item.d.ts +8 -0
- package/dist/components/content-container/sortable/knob.d.ts +5 -0
- package/dist/components/content-container/tab/index.d.ts +17 -0
- package/dist/components/content-container/tab/window.d.ts +5 -0
- package/dist/components/data-tree/index.d.ts +6 -0
- package/dist/components/dialog/index.d.ts +16 -0
- package/dist/components/dropdown/header.d.ts +5 -0
- package/dist/components/dropdown/index.d.ts +15 -0
- package/dist/components/dropdown/item.d.ts +7 -0
- package/dist/components/form/index.d.ts +44 -0
- package/dist/components/form/input/checkbox.d.ts +9 -0
- package/dist/components/form/input/color.d.ts +10 -0
- package/dist/components/form/input/email.d.ts +9 -0
- package/dist/components/form/input/file.d.ts +14 -0
- package/dist/components/form/input/number.d.ts +9 -0
- package/dist/components/form/input/password.d.ts +9 -0
- package/dist/components/form/input/radio.d.ts +9 -0
- package/dist/components/form/input/range.d.ts +9 -0
- package/dist/components/form/input/search.d.ts +8 -0
- package/dist/components/form/input/text.d.ts +9 -0
- package/dist/components/form/input/time.d.ts +9 -0
- package/dist/components/form/other/autocomplete.d.ts +15 -0
- package/dist/components/form/other/multipleSelect.d.ts +23 -0
- package/dist/components/form/other/phoneNumber.d.ts +10 -0
- package/dist/components/form/other/pin.d.ts +10 -0
- package/dist/components/form/other/secret.d.ts +11 -0
- package/dist/components/form/other/select.d.ts +15 -0
- package/dist/components/form/other/textarea.d.ts +9 -0
- package/dist/components/index.d.ts +19 -0
- package/dist/components/map/index.d.ts +13 -0
- package/dist/components/map/marker.d.ts +8 -0
- package/dist/components/syntax-highlighter/index.d.ts +9 -0
- package/dist/components/table/action.d.ts +5 -0
- package/dist/components/table/index.d.ts +24 -0
- package/dist/components/timeline/index.d.ts +10 -0
- package/dist/components/timeline/item.d.ts +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.LICENSE.txt +62 -0
- package/package.json +72 -0
- package/src/components/button/index.tsx +74 -0
- package/src/components/calendar/big-calendar/event.tsx +46 -0
- package/src/components/calendar/big-calendar/index.tsx +102 -0
- package/src/components/calendar/big-calendar/toolbar.tsx +98 -0
- package/src/components/calendar/calendar/index.tsx +26 -0
- package/src/components/chart/index.tsx +121 -0
- package/src/components/chip/index.tsx +47 -0
- package/src/components/content-container/accordion/index.tsx +28 -0
- package/src/components/content-container/accordion/item.tsx +40 -0
- package/src/components/content-container/card.tsx +21 -0
- package/src/components/content-container/carousel.tsx +35 -0
- package/src/components/content-container/drawer.tsx +78 -0
- package/src/components/content-container/modal/index.tsx +127 -0
- package/src/components/content-container/sortable/index.tsx +47 -0
- package/src/components/content-container/sortable/item.tsx +20 -0
- package/src/components/content-container/sortable/knob.tsx +11 -0
- package/src/components/content-container/tab/index.tsx +94 -0
- package/src/components/content-container/tab/window.tsx +10 -0
- package/src/components/data-tree/index.tsx +60 -0
- package/src/components/dialog/index.tsx +88 -0
- package/src/components/dropdown/header.tsx +11 -0
- package/src/components/dropdown/index.tsx +69 -0
- package/src/components/dropdown/item.tsx +22 -0
- package/src/components/form/index.tsx +81 -0
- package/src/components/form/input/checkbox.tsx +49 -0
- package/src/components/form/input/color.tsx +104 -0
- package/src/components/form/input/email.tsx +40 -0
- package/src/components/form/input/file.tsx +189 -0
- package/src/components/form/input/number.tsx +93 -0
- package/src/components/form/input/password.tsx +57 -0
- package/src/components/form/input/radio.tsx +49 -0
- package/src/components/form/input/range.tsx +67 -0
- package/src/components/form/input/search.tsx +50 -0
- package/src/components/form/input/text.tsx +39 -0
- package/src/components/form/input/time.tsx +315 -0
- package/src/components/form/other/autocomplete.tsx +199 -0
- package/src/components/form/other/multipleSelect.tsx +211 -0
- package/src/components/form/other/phoneNumber.tsx +1668 -0
- package/src/components/form/other/pin.tsx +56 -0
- package/src/components/form/other/secret.tsx +74 -0
- package/src/components/form/other/select.tsx +187 -0
- package/src/components/form/other/textarea.tsx +44 -0
- package/src/components/index.ts +29 -0
- package/src/components/map/index.tsx +72 -0
- package/src/components/map/marker.tsx +40 -0
- package/src/components/syntax-highlighter/index.tsx +45 -0
- package/src/components/table/action.tsx +22 -0
- package/src/components/table/index.tsx +431 -0
- package/src/components/timeline/index.tsx +28 -0
- package/src/components/timeline/item.tsx +25 -0
- package/src/index.css +1 -0
- package/src/index.ts +3 -0
- package/src/styles/big-calendar.scss +810 -0
- package/src/styles/calendar.scss +195 -0
- package/src/styles/index.css +2 -0
- package/tsconfig.json +17 -0
- package/webpack.config.js +35 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
import { Icon } from "@iconify/react";
|
|
4
|
+
|
|
5
|
+
export interface PasswordProps
|
|
6
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
7
|
+
options?: {
|
|
8
|
+
width?: "full" | "fit";
|
|
9
|
+
height?: "short" | "medium" | "high";
|
|
10
|
+
};
|
|
11
|
+
label?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const Password: React.FC<PasswordProps> = ({
|
|
15
|
+
options,
|
|
16
|
+
label,
|
|
17
|
+
...rest
|
|
18
|
+
}) => {
|
|
19
|
+
const [show, setShow] = useState(false);
|
|
20
|
+
const width = options?.width === "fit" ? "w-fit" : "w-full";
|
|
21
|
+
const height =
|
|
22
|
+
options?.height === "short"
|
|
23
|
+
? "py-1"
|
|
24
|
+
: options?.height === "high"
|
|
25
|
+
? "py-2"
|
|
26
|
+
: "py-1.5";
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<label className={`grid gap-2 ${width}`}>
|
|
30
|
+
{label && (
|
|
31
|
+
<span
|
|
32
|
+
className={`text-sm select-none ${
|
|
33
|
+
rest.required && "after:content-['*'] after:ml-1 after:text-danger"
|
|
34
|
+
}`}
|
|
35
|
+
>
|
|
36
|
+
{label}
|
|
37
|
+
</span>
|
|
38
|
+
)}
|
|
39
|
+
<div className="relative flex items-center">
|
|
40
|
+
<input
|
|
41
|
+
type={show ? "text" : "password"}
|
|
42
|
+
className={`w-full px-4 border-default border rounded-md bg-secondary-bg placeholder:duration-300 placeholder:translate-x-0 focus:placeholder:translate-x-1 placeholder:text-slate-300 focus:outline-none focus:ring-1 focus:ring-primary focus:shadow focus:shadow-primary focus:border-primary-dark disabled:bg-secondary-light disabled:text-slate-400 ${height}`}
|
|
43
|
+
{...rest}
|
|
44
|
+
/>
|
|
45
|
+
<div
|
|
46
|
+
className="absolute right-4 p-1 rounded-full text-xl text-slate-400 cursor-pointer hover:bg-secondary-light"
|
|
47
|
+
onClick={(e) => {
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
setShow(!show);
|
|
50
|
+
}}
|
|
51
|
+
>
|
|
52
|
+
<Icon icon={`flowbite:eye${show ? "" : "-slash"}-outline`} />
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</label>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
export interface RadioProps
|
|
5
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
6
|
+
options?: {
|
|
7
|
+
disableHover?: boolean;
|
|
8
|
+
};
|
|
9
|
+
label?: string;
|
|
10
|
+
theme?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Radio: React.FC<RadioProps> = ({
|
|
14
|
+
options,
|
|
15
|
+
label,
|
|
16
|
+
theme,
|
|
17
|
+
...rest
|
|
18
|
+
}) => {
|
|
19
|
+
return (
|
|
20
|
+
<label
|
|
21
|
+
className={`flex items-center w-fit ${
|
|
22
|
+
!rest.disabled && "cursor-pointer"
|
|
23
|
+
} ${!options?.disableHover ? "gap-2" : "gap-4"}`}
|
|
24
|
+
>
|
|
25
|
+
<div
|
|
26
|
+
className={`flex justify-center items-center ${
|
|
27
|
+
!options?.disableHover &&
|
|
28
|
+
"w-8 h-8 rounded-full hover:bg-secondary-light"
|
|
29
|
+
}`}
|
|
30
|
+
>
|
|
31
|
+
<input
|
|
32
|
+
type="radio"
|
|
33
|
+
className={`border-default border bg-secondary-bg cursor-pointer checked:bg-primary focus:outline-none focus:ring-0 focus:ring-offset-0 disabled:bg-secondary-light disabled:checked:bg-secondary-dark disabled:cursor-default ${
|
|
34
|
+
theme && "border-0"
|
|
35
|
+
}`}
|
|
36
|
+
style={{ backgroundColor: theme }}
|
|
37
|
+
{...rest}
|
|
38
|
+
/>
|
|
39
|
+
</div>
|
|
40
|
+
{label && (
|
|
41
|
+
<span
|
|
42
|
+
className={`text-sm select-none ${rest.disabled && "text-slate-400"}`}
|
|
43
|
+
>
|
|
44
|
+
{label}
|
|
45
|
+
</span>
|
|
46
|
+
)}
|
|
47
|
+
</label>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { useState, useEffect } from "react";
|
|
3
|
+
|
|
4
|
+
export interface RangeProps
|
|
5
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
6
|
+
options?: {
|
|
7
|
+
width?: "full" | "fit";
|
|
8
|
+
};
|
|
9
|
+
label?: string;
|
|
10
|
+
theme?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Range: React.FC<RangeProps> = ({
|
|
14
|
+
options,
|
|
15
|
+
label,
|
|
16
|
+
theme,
|
|
17
|
+
...rest
|
|
18
|
+
}) => {
|
|
19
|
+
const [tempValue, setTempValue] = useState("0");
|
|
20
|
+
const width = options?.width === "fit" ? "w-fit" : "w-full";
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (rest.value) {
|
|
24
|
+
setTempValue(rest.value as string);
|
|
25
|
+
return;
|
|
26
|
+
} else if (rest.defaultValue) {
|
|
27
|
+
setTempValue(rest.defaultValue as string);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}, [rest.value, rest.defaultValue]);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
rest.onChange &&
|
|
34
|
+
rest.onChange({
|
|
35
|
+
target: { value: tempValue } as HTMLInputElement,
|
|
36
|
+
} as React.ChangeEvent<HTMLInputElement>);
|
|
37
|
+
}, [tempValue]);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<label className={`grid gap-2 ${width}`}>
|
|
41
|
+
{label && (
|
|
42
|
+
<span
|
|
43
|
+
className={`text-sm select-none ${
|
|
44
|
+
rest.required && "after:content-['*'] after:ml-1 after:text-danger"
|
|
45
|
+
}`}
|
|
46
|
+
>
|
|
47
|
+
{label}
|
|
48
|
+
</span>
|
|
49
|
+
)}
|
|
50
|
+
<div className="flex gap-2">
|
|
51
|
+
<input
|
|
52
|
+
type="range"
|
|
53
|
+
className={`flex-1 border-default border cursor-pointer accent-primary focus:outline-none focus:ring-0 focus:ring-offset-0 disabled:accent-secondary-dark disabled:cursor-default ${
|
|
54
|
+
theme && "border-0"
|
|
55
|
+
}`}
|
|
56
|
+
style={{ accentColor: theme }}
|
|
57
|
+
value={tempValue}
|
|
58
|
+
onChange={(e) => setTempValue(e.target.value.toString())}
|
|
59
|
+
{...rest}
|
|
60
|
+
/>
|
|
61
|
+
<span className="w-7 text-right">
|
|
62
|
+
{rest.value || rest.defaultValue || tempValue}
|
|
63
|
+
</span>
|
|
64
|
+
</div>
|
|
65
|
+
</label>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Icon } from "@iconify/react";
|
|
4
|
+
|
|
5
|
+
export interface SearchProps
|
|
6
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
7
|
+
options?: {
|
|
8
|
+
width?: "full" | "fit";
|
|
9
|
+
height?: "short" | "medium" | "high";
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Search: React.FC<SearchProps> = ({ options, ...rest }) => {
|
|
14
|
+
const width = options?.width === "fit" ? "w-fit" : "w-full";
|
|
15
|
+
const height =
|
|
16
|
+
options?.height === "short"
|
|
17
|
+
? "py-1"
|
|
18
|
+
: options?.height === "high"
|
|
19
|
+
? "py-2"
|
|
20
|
+
: "py-1.5";
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<label className={`relative flex items-center ${width}`}>
|
|
24
|
+
<Icon
|
|
25
|
+
icon="ic:round-search"
|
|
26
|
+
className="absolute left-3 text-lg text-slate-400"
|
|
27
|
+
/>
|
|
28
|
+
<input
|
|
29
|
+
type="search"
|
|
30
|
+
className={`w-full px-9 pe-4 border-default border rounded-md bg-secondary-bg placeholder:duration-300 placeholder:translate-x-0 [&::-webkit-search-cancel-button]:appearance-none focus:placeholder:translate-x-1 placeholder:text-slate-300 focus:outline-none focus:ring-1 focus:ring-primary focus:shadow focus:shadow-primary focus:border-primary-dark disabled:bg-secondary-light disabled:text-slate-400 ${height}`}
|
|
31
|
+
{...rest}
|
|
32
|
+
/>
|
|
33
|
+
{rest.value && (
|
|
34
|
+
<div
|
|
35
|
+
className={
|
|
36
|
+
"absolute right-4 p-1 rounded-full text-xl text-slate-400 cursor-pointer hover:bg-secondary-light"
|
|
37
|
+
}
|
|
38
|
+
onClick={() => {
|
|
39
|
+
rest.onChange &&
|
|
40
|
+
rest.onChange({
|
|
41
|
+
target: { value: "" } as HTMLInputElement,
|
|
42
|
+
} as React.ChangeEvent<HTMLInputElement>);
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
45
|
+
<Icon icon="pajamas:close" />
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
48
|
+
</label>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
export interface TextProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
5
|
+
options?: {
|
|
6
|
+
width?: "full" | "fit";
|
|
7
|
+
height?: "short" | "medium" | "high";
|
|
8
|
+
};
|
|
9
|
+
label?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Text: React.FC<TextProps> = ({ options, label, ...rest }) => {
|
|
13
|
+
const width = options?.width === "fit" ? "w-fit" : "w-full";
|
|
14
|
+
const height =
|
|
15
|
+
options?.height === "short"
|
|
16
|
+
? "py-1"
|
|
17
|
+
: options?.height === "high"
|
|
18
|
+
? "py-2"
|
|
19
|
+
: "py-1.5";
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<label className={`grid gap-2 ${width}`}>
|
|
23
|
+
{label && (
|
|
24
|
+
<span
|
|
25
|
+
className={`text-sm select-none ${
|
|
26
|
+
rest.required && "after:content-['*'] after:ml-1 after:text-danger"
|
|
27
|
+
}`}
|
|
28
|
+
>
|
|
29
|
+
{label}
|
|
30
|
+
</span>
|
|
31
|
+
)}
|
|
32
|
+
<input
|
|
33
|
+
type="text"
|
|
34
|
+
className={`w-full px-4 border-default border rounded-md bg-secondary-bg placeholder:duration-300 placeholder:translate-x-0 focus:placeholder:translate-x-1 placeholder:text-slate-300 focus:outline-none focus:ring-1 focus:ring-primary focus:shadow focus:shadow-primary focus:border-primary-dark disabled:bg-secondary-light disabled:text-slate-400 ${height}`}
|
|
35
|
+
{...rest}
|
|
36
|
+
/>
|
|
37
|
+
</label>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { useState, useEffect, useRef } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import dayjs from "dayjs";
|
|
5
|
+
|
|
6
|
+
export interface TimeProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
7
|
+
options?: {
|
|
8
|
+
width?: "full" | "fit";
|
|
9
|
+
height?: "short" | "medium" | "high";
|
|
10
|
+
};
|
|
11
|
+
label?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const Time: React.FC<TimeProps> = ({ options, label, ...rest }) => {
|
|
15
|
+
const [tempValue, setTempValue] = useState(dayjs().format("hh:mm a"));
|
|
16
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
17
|
+
const [position, setPosition] = useState<{
|
|
18
|
+
top: number;
|
|
19
|
+
left: number;
|
|
20
|
+
} | null>(null);
|
|
21
|
+
const [dropdownWidth, setDropdownWidth] = useState<number>(0);
|
|
22
|
+
const triggerRef = useRef<HTMLDivElement>(null);
|
|
23
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
24
|
+
const hoursRef = useRef<HTMLDivElement>(null);
|
|
25
|
+
const minutesRef = useRef<HTMLDivElement>(null);
|
|
26
|
+
const width = options?.width === "fit" ? "w-fit" : "w-full";
|
|
27
|
+
const height =
|
|
28
|
+
options?.height === "short"
|
|
29
|
+
? "py-1"
|
|
30
|
+
: options?.height === "high"
|
|
31
|
+
? "py-2"
|
|
32
|
+
: "py-1.5";
|
|
33
|
+
const hours = [
|
|
34
|
+
"12",
|
|
35
|
+
"01",
|
|
36
|
+
"02",
|
|
37
|
+
"03",
|
|
38
|
+
"04",
|
|
39
|
+
"05",
|
|
40
|
+
"06",
|
|
41
|
+
"07",
|
|
42
|
+
"08",
|
|
43
|
+
"09",
|
|
44
|
+
"10",
|
|
45
|
+
"11",
|
|
46
|
+
];
|
|
47
|
+
const minutes = [
|
|
48
|
+
"00",
|
|
49
|
+
"01",
|
|
50
|
+
"02",
|
|
51
|
+
"03",
|
|
52
|
+
"04",
|
|
53
|
+
"05",
|
|
54
|
+
"06",
|
|
55
|
+
"07",
|
|
56
|
+
"08",
|
|
57
|
+
"09",
|
|
58
|
+
"10",
|
|
59
|
+
"11",
|
|
60
|
+
"12",
|
|
61
|
+
"13",
|
|
62
|
+
"14",
|
|
63
|
+
"15",
|
|
64
|
+
"16",
|
|
65
|
+
"17",
|
|
66
|
+
"18",
|
|
67
|
+
"19",
|
|
68
|
+
"20",
|
|
69
|
+
"21",
|
|
70
|
+
"22",
|
|
71
|
+
"23",
|
|
72
|
+
"24",
|
|
73
|
+
"25",
|
|
74
|
+
"26",
|
|
75
|
+
"27",
|
|
76
|
+
"28",
|
|
77
|
+
"29",
|
|
78
|
+
"30",
|
|
79
|
+
"31",
|
|
80
|
+
"32",
|
|
81
|
+
"33",
|
|
82
|
+
"34",
|
|
83
|
+
"35",
|
|
84
|
+
"36",
|
|
85
|
+
"37",
|
|
86
|
+
"38",
|
|
87
|
+
"39",
|
|
88
|
+
"40",
|
|
89
|
+
"41",
|
|
90
|
+
"42",
|
|
91
|
+
"43",
|
|
92
|
+
"44",
|
|
93
|
+
"45",
|
|
94
|
+
"46",
|
|
95
|
+
"47",
|
|
96
|
+
"48",
|
|
97
|
+
"49",
|
|
98
|
+
"50",
|
|
99
|
+
"51",
|
|
100
|
+
"52",
|
|
101
|
+
"53",
|
|
102
|
+
"54",
|
|
103
|
+
"55",
|
|
104
|
+
"56",
|
|
105
|
+
"57",
|
|
106
|
+
"58",
|
|
107
|
+
"59",
|
|
108
|
+
];
|
|
109
|
+
const ampm = ["am", "pm"];
|
|
110
|
+
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
const handleClickOutside = (e: MouseEvent) => {
|
|
113
|
+
if (
|
|
114
|
+
dropdownRef.current &&
|
|
115
|
+
!dropdownRef.current.contains(e.target as Node) &&
|
|
116
|
+
!triggerRef.current?.contains(e.target as Node)
|
|
117
|
+
) {
|
|
118
|
+
setIsOpen(false);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
123
|
+
return () => {
|
|
124
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
125
|
+
};
|
|
126
|
+
}, []);
|
|
127
|
+
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (triggerRef.current) {
|
|
130
|
+
const rect = triggerRef.current.getBoundingClientRect();
|
|
131
|
+
const dropdownHeight = dropdownRef.current?.offsetHeight || 0;
|
|
132
|
+
const windowHeight = window.innerHeight;
|
|
133
|
+
|
|
134
|
+
setPosition({
|
|
135
|
+
top: rect.bottom + window.scrollY + 10,
|
|
136
|
+
left: rect.left + window.scrollX,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
setDropdownWidth(rect.width);
|
|
140
|
+
|
|
141
|
+
if (rect.bottom + dropdownHeight > windowHeight) {
|
|
142
|
+
setPosition((prev) =>
|
|
143
|
+
prev
|
|
144
|
+
? { ...prev, top: rect.top + window.scrollY - dropdownHeight - 10 }
|
|
145
|
+
: null
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (isOpen) {
|
|
151
|
+
document.getElementById("body")!.style.overflow = "hidden";
|
|
152
|
+
} else {
|
|
153
|
+
document.getElementById("body")!.style.overflow = "auto";
|
|
154
|
+
}
|
|
155
|
+
}, [isOpen]);
|
|
156
|
+
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if (isOpen) {
|
|
159
|
+
const scrollToSelected = (
|
|
160
|
+
ref: React.RefObject<HTMLDivElement>,
|
|
161
|
+
value: string,
|
|
162
|
+
delay: number
|
|
163
|
+
) => {
|
|
164
|
+
const timeout = setTimeout(() => {
|
|
165
|
+
const selectedElement = ref.current?.querySelector(
|
|
166
|
+
`[data-value='${value}']`
|
|
167
|
+
);
|
|
168
|
+
selectedElement?.scrollIntoView({
|
|
169
|
+
block: "nearest",
|
|
170
|
+
});
|
|
171
|
+
}, delay);
|
|
172
|
+
return () => clearTimeout(timeout);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
scrollToSelected(hoursRef, tempValue.split(":")[0], 0);
|
|
176
|
+
scrollToSelected(minutesRef, tempValue.split(":")[1].split(" ")[0], 0);
|
|
177
|
+
}
|
|
178
|
+
}, [isOpen, tempValue]);
|
|
179
|
+
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
if (rest.value) {
|
|
182
|
+
setTempValue(rest.value as string);
|
|
183
|
+
return;
|
|
184
|
+
} else if (rest.defaultValue) {
|
|
185
|
+
setTempValue(rest.defaultValue as string);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
}, [rest.value, rest.defaultValue]);
|
|
189
|
+
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
rest.onChange &&
|
|
192
|
+
rest.onChange({
|
|
193
|
+
target: {
|
|
194
|
+
value: tempValue,
|
|
195
|
+
} as HTMLInputElement,
|
|
196
|
+
} as React.ChangeEvent<HTMLInputElement>);
|
|
197
|
+
}, [tempValue]);
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<label className={`grid gap-2 ${width}`}>
|
|
201
|
+
{label && (
|
|
202
|
+
<span
|
|
203
|
+
className={`text-sm select-none ${
|
|
204
|
+
rest.required && "after:content-['*'] after:ml-1 after:text-danger"
|
|
205
|
+
}`}
|
|
206
|
+
>
|
|
207
|
+
{label}
|
|
208
|
+
</span>
|
|
209
|
+
)}
|
|
210
|
+
<div className="relative" ref={triggerRef}>
|
|
211
|
+
<div
|
|
212
|
+
className="relative flex items-center cursor-pointer"
|
|
213
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
214
|
+
>
|
|
215
|
+
<input
|
|
216
|
+
type="text"
|
|
217
|
+
className={`accent-primary w-full px-4 border-default border rounded-md bg-secondary-bg placeholder:duration-300 placeholder:translate-x-0 focus:placeholder:translate-x-1 placeholder:text-slate-300 focus:outline-none focus:ring-1 focus:ring-primary focus:shadow focus:shadow-primary focus:border-primary-dark disabled:bg-secondary-light disabled:text-slate-400 ${height}`}
|
|
218
|
+
value={tempValue}
|
|
219
|
+
{...rest}
|
|
220
|
+
/>
|
|
221
|
+
</div>
|
|
222
|
+
{isOpen &&
|
|
223
|
+
position &&
|
|
224
|
+
createPortal(
|
|
225
|
+
<div
|
|
226
|
+
ref={dropdownRef}
|
|
227
|
+
className={`absolute grid grid-cols-3 gap-2 min-w-40 h-40 p-1 z-50 pointer-events-auto bg-secondary-bg shadow border rounded-md overflow-hidden`}
|
|
228
|
+
style={{
|
|
229
|
+
top: position.top,
|
|
230
|
+
left: position.left,
|
|
231
|
+
width: dropdownWidth,
|
|
232
|
+
}}
|
|
233
|
+
>
|
|
234
|
+
<div
|
|
235
|
+
ref={hoursRef}
|
|
236
|
+
className="h-full overflow-auto [&::-webkit-scrollbar]:hidden"
|
|
237
|
+
>
|
|
238
|
+
{hours.map((item, index) => (
|
|
239
|
+
<button
|
|
240
|
+
key={index}
|
|
241
|
+
type="button"
|
|
242
|
+
className={`w-full my-0.5 px-4 py-2 rounded-md text-sm text-left text-default ${
|
|
243
|
+
tempValue.split(":")[0] === item
|
|
244
|
+
? "bg-primary-transparent cursor-default"
|
|
245
|
+
: "hover:bg-secondary-light"
|
|
246
|
+
}`}
|
|
247
|
+
data-value={item}
|
|
248
|
+
onMouseDown={() => {
|
|
249
|
+
setTempValue(
|
|
250
|
+
`${item}:${tempValue.split(":")[1].split(" ")[0]} ${
|
|
251
|
+
tempValue.split(" ")[1]
|
|
252
|
+
}`
|
|
253
|
+
);
|
|
254
|
+
}}
|
|
255
|
+
>
|
|
256
|
+
{item}
|
|
257
|
+
</button>
|
|
258
|
+
))}
|
|
259
|
+
</div>
|
|
260
|
+
<div
|
|
261
|
+
ref={minutesRef}
|
|
262
|
+
className="h-full overflow-auto [&::-webkit-scrollbar]:hidden"
|
|
263
|
+
>
|
|
264
|
+
{minutes.map((item, index) => (
|
|
265
|
+
<button
|
|
266
|
+
key={index}
|
|
267
|
+
type="button"
|
|
268
|
+
className={`w-full my-0.5 px-4 py-2 rounded-md text-sm text-left text-default ${
|
|
269
|
+
tempValue.split(":")[1].split(" ")[0] === item
|
|
270
|
+
? "bg-primary-transparent cursor-default"
|
|
271
|
+
: "hover:bg-secondary-light "
|
|
272
|
+
}`}
|
|
273
|
+
data-value={item}
|
|
274
|
+
onMouseDown={() => {
|
|
275
|
+
setTempValue(
|
|
276
|
+
`${tempValue.split(":")[0]}:${item} ${
|
|
277
|
+
tempValue.split(" ")[1]
|
|
278
|
+
}`
|
|
279
|
+
);
|
|
280
|
+
}}
|
|
281
|
+
>
|
|
282
|
+
{item}
|
|
283
|
+
</button>
|
|
284
|
+
))}
|
|
285
|
+
</div>
|
|
286
|
+
<div className="h-full overflow-auto [&::-webkit-scrollbar]:hidden">
|
|
287
|
+
{ampm.map((item, index) => (
|
|
288
|
+
<button
|
|
289
|
+
key={index}
|
|
290
|
+
type="button"
|
|
291
|
+
className={`w-full my-0.5 px-4 py-2 rounded-md text-sm text-left text-default ${
|
|
292
|
+
tempValue.split(" ")[1] === item
|
|
293
|
+
? "bg-primary-transparent cursor-default"
|
|
294
|
+
: "hover:bg-secondary-light "
|
|
295
|
+
}`}
|
|
296
|
+
data-value={item}
|
|
297
|
+
onMouseDown={() => {
|
|
298
|
+
setTempValue(
|
|
299
|
+
`${tempValue.split(":")[0]}:${
|
|
300
|
+
tempValue.split(":")[1].split(" ")[0]
|
|
301
|
+
} ${item}`
|
|
302
|
+
);
|
|
303
|
+
}}
|
|
304
|
+
>
|
|
305
|
+
{item}
|
|
306
|
+
</button>
|
|
307
|
+
))}
|
|
308
|
+
</div>
|
|
309
|
+
</div>,
|
|
310
|
+
document.body
|
|
311
|
+
)}
|
|
312
|
+
</div>
|
|
313
|
+
</label>
|
|
314
|
+
);
|
|
315
|
+
};
|