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,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Dropdown as Dd } from "flowbite-react";
|
|
4
|
+
import { Header, type HeaderProps } from "./header";
|
|
5
|
+
import { Item, type ItemProps } from "./item";
|
|
6
|
+
|
|
7
|
+
interface DropdownProps {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
placement?:
|
|
10
|
+
| "top"
|
|
11
|
+
| "top-start"
|
|
12
|
+
| "top-end"
|
|
13
|
+
| "bottom"
|
|
14
|
+
| "bottom-start"
|
|
15
|
+
| "bottom-end";
|
|
16
|
+
dismissOnClick?: boolean;
|
|
17
|
+
trigger?: React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface DropdownComponent extends React.FC<DropdownProps> {
|
|
21
|
+
Header: React.FC<HeaderProps>;
|
|
22
|
+
Item: React.FC<ItemProps>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const Dropdown: DropdownComponent = ({
|
|
26
|
+
children,
|
|
27
|
+
placement,
|
|
28
|
+
dismissOnClick,
|
|
29
|
+
trigger,
|
|
30
|
+
}) => {
|
|
31
|
+
const childrenList = React.Children.toArray(children);
|
|
32
|
+
const header = childrenList.find((child) => {
|
|
33
|
+
return (child as React.ReactElement).type === Dropdown.Header;
|
|
34
|
+
});
|
|
35
|
+
const itemList = childrenList.filter((child) => {
|
|
36
|
+
return (child as React.ReactElement).type === Dropdown.Item;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Dd
|
|
41
|
+
label=""
|
|
42
|
+
className="px-1 rounded-md border-default bg-secondary-bg z-20"
|
|
43
|
+
placement={placement || "bottom-end"}
|
|
44
|
+
dismissOnClick={dismissOnClick || true}
|
|
45
|
+
theme={{
|
|
46
|
+
floating: {
|
|
47
|
+
header: "px-4 py-2 text-sm !text-default",
|
|
48
|
+
item: {
|
|
49
|
+
base: `min-w-40 w-full my-0.5 rounded-md text-sm text-left text-default ${
|
|
50
|
+
childrenList.length > 0 && !header && itemList.length === 0
|
|
51
|
+
? ""
|
|
52
|
+
: "px-4 py-2 hover:bg-secondary-light"
|
|
53
|
+
}`,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
}}
|
|
57
|
+
renderTrigger={() => <div>{trigger}</div>}
|
|
58
|
+
>
|
|
59
|
+
{header}
|
|
60
|
+
{itemList}
|
|
61
|
+
{childrenList.length > 0 && !header && itemList.length === 0 && (
|
|
62
|
+
<Dd.Item className="grid cursor-default">{children}</Dd.Item>
|
|
63
|
+
)}
|
|
64
|
+
</Dd>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
Dropdown.Header = Header;
|
|
69
|
+
Dropdown.Item = Item;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Dropdown as Dd } from "flowbite-react";
|
|
4
|
+
|
|
5
|
+
export interface ItemProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
active?: boolean;
|
|
8
|
+
onClick?: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Item: React.FC<ItemProps> = ({ children, active, onClick }) => {
|
|
12
|
+
return (
|
|
13
|
+
<Dd.Item
|
|
14
|
+
className={`${
|
|
15
|
+
active && "bg-primary-transparent text-primary pointer-events-none"
|
|
16
|
+
}`}
|
|
17
|
+
onClick={onClick}
|
|
18
|
+
>
|
|
19
|
+
{children}
|
|
20
|
+
</Dd.Item>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Text, type TextProps } from "./input/text";
|
|
4
|
+
import { Number, type NumberProps } from "./input/number";
|
|
5
|
+
import { Email, type EmailProps } from "./input/email";
|
|
6
|
+
import { Search, type SearchProps } from "./input/search";
|
|
7
|
+
import { Checkbox, type CheckboxProps } from "./input/checkbox";
|
|
8
|
+
import { Radio, type RadioProps } from "./input/radio";
|
|
9
|
+
import { Range, type RangeProps } from "./input/range";
|
|
10
|
+
import { Time, type TimeProps } from "./input/time";
|
|
11
|
+
import { File, type FileProps } from "./input/file";
|
|
12
|
+
import { Color, type ColorProps } from "./input/color";
|
|
13
|
+
import { Password, type PasswordProps } from "./input/password";
|
|
14
|
+
import { PhoneNumber, type PhoneNumberProps } from "./other/phoneNumber";
|
|
15
|
+
import { Secret, type SecretProps } from "./other/secret";
|
|
16
|
+
import { Pin, type PinProps } from "./other/pin";
|
|
17
|
+
import { Textarea, type TextareaProps } from "./other/textarea";
|
|
18
|
+
import { Select, type SelectProps } from "./other/select";
|
|
19
|
+
import {
|
|
20
|
+
MultipleSelect,
|
|
21
|
+
type MultipleSelectProps,
|
|
22
|
+
} from "./other/multipleSelect";
|
|
23
|
+
import { Autocomplete, type AutocompleteProps } from "./other/autocomplete";
|
|
24
|
+
|
|
25
|
+
interface FormProps extends React.FormHTMLAttributes<HTMLFormElement> {
|
|
26
|
+
children: React.ReactNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface FormComponent extends React.FC<FormProps> {
|
|
30
|
+
Text: React.FC<TextProps>;
|
|
31
|
+
Number: React.FC<NumberProps>;
|
|
32
|
+
Email: React.FC<EmailProps>;
|
|
33
|
+
Search: React.FC<SearchProps>;
|
|
34
|
+
Checkbox: React.FC<CheckboxProps>;
|
|
35
|
+
Radio: React.FC<RadioProps>;
|
|
36
|
+
Range: React.FC<RangeProps>;
|
|
37
|
+
Time: React.FC<TimeProps>;
|
|
38
|
+
File: React.FC<FileProps>;
|
|
39
|
+
Color: React.FC<ColorProps>;
|
|
40
|
+
Password: React.FC<PasswordProps>;
|
|
41
|
+
PhoneNumber: React.FC<PhoneNumberProps>;
|
|
42
|
+
Secret: React.FC<SecretProps>;
|
|
43
|
+
Pin: React.FC<PinProps>;
|
|
44
|
+
Textarea: React.FC<TextareaProps>;
|
|
45
|
+
Select: React.FC<SelectProps>;
|
|
46
|
+
MultipleSelect: React.FC<MultipleSelectProps>;
|
|
47
|
+
Autocomplete: React.FC<AutocompleteProps>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const Form: FormComponent = ({ children, onSubmit, ...rest }) => {
|
|
51
|
+
return (
|
|
52
|
+
<form
|
|
53
|
+
onSubmit={(e) => {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
onSubmit!(e);
|
|
56
|
+
}}
|
|
57
|
+
{...rest}
|
|
58
|
+
>
|
|
59
|
+
{children}
|
|
60
|
+
</form>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
Form.Text = Text;
|
|
65
|
+
Form.Number = Number;
|
|
66
|
+
Form.Email = Email;
|
|
67
|
+
Form.Search = Search;
|
|
68
|
+
Form.Checkbox = Checkbox;
|
|
69
|
+
Form.Radio = Radio;
|
|
70
|
+
Form.Range = Range;
|
|
71
|
+
Form.Time = Time;
|
|
72
|
+
Form.File = File;
|
|
73
|
+
Form.Color = Color;
|
|
74
|
+
Form.Password = Password;
|
|
75
|
+
Form.PhoneNumber = PhoneNumber;
|
|
76
|
+
Form.Secret = Secret;
|
|
77
|
+
Form.Pin = Pin;
|
|
78
|
+
Form.Textarea = Textarea;
|
|
79
|
+
Form.Select = Select;
|
|
80
|
+
Form.MultipleSelect = MultipleSelect;
|
|
81
|
+
Form.Autocomplete = Autocomplete;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
export interface CheckboxProps
|
|
5
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
6
|
+
options?: {
|
|
7
|
+
disableHover?: boolean;
|
|
8
|
+
};
|
|
9
|
+
label?: string;
|
|
10
|
+
theme?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Checkbox: React.FC<CheckboxProps> = ({
|
|
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="checkbox"
|
|
33
|
+
className={`border-default border rounded 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,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { useState, useEffect, useRef } from "react";
|
|
3
|
+
import { Icon } from "@iconify/react";
|
|
4
|
+
|
|
5
|
+
export interface ColorProps
|
|
6
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
7
|
+
options?: {
|
|
8
|
+
width?: "full" | "fit";
|
|
9
|
+
height?: "short" | "medium" | "high";
|
|
10
|
+
};
|
|
11
|
+
label?: string;
|
|
12
|
+
onClipboardClick?: (value: string) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Color: React.FC<ColorProps> = ({
|
|
16
|
+
options,
|
|
17
|
+
label,
|
|
18
|
+
onClipboardClick,
|
|
19
|
+
...rest
|
|
20
|
+
}) => {
|
|
21
|
+
const [tempValue, setTempValue] = useState("");
|
|
22
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
23
|
+
const width = options?.width === "fit" ? "w-fit" : "w-full";
|
|
24
|
+
const height =
|
|
25
|
+
options?.height === "short"
|
|
26
|
+
? "py-1"
|
|
27
|
+
: options?.height === "high"
|
|
28
|
+
? "py-2"
|
|
29
|
+
: "py-1.5";
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (rest.value) {
|
|
33
|
+
setTempValue(rest.value as string);
|
|
34
|
+
return;
|
|
35
|
+
} else if (rest.defaultValue) {
|
|
36
|
+
setTempValue(rest.defaultValue as string);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}, [rest.value, rest.defaultValue]);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
rest.onChange &&
|
|
43
|
+
rest.onChange({
|
|
44
|
+
target: { value: tempValue } as HTMLInputElement,
|
|
45
|
+
} as React.ChangeEvent<HTMLInputElement>);
|
|
46
|
+
}, [tempValue]);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<label className={`grid gap-2 ${width}`}>
|
|
50
|
+
{label && (
|
|
51
|
+
<span
|
|
52
|
+
className={`text-sm select-none ${
|
|
53
|
+
rest.required && "after:content-['*'] after:ml-1 after:text-danger"
|
|
54
|
+
}`}
|
|
55
|
+
>
|
|
56
|
+
{label}
|
|
57
|
+
</span>
|
|
58
|
+
)}
|
|
59
|
+
<div className="flex gap-4">
|
|
60
|
+
<div className="relative flex flex-col">
|
|
61
|
+
<button
|
|
62
|
+
type="button"
|
|
63
|
+
className={`h-full px-4 border-default border rounded-md text-slate-400 cursor-pointer 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}`}
|
|
64
|
+
style={{ backgroundColor: tempValue }}
|
|
65
|
+
onClick={() => {
|
|
66
|
+
inputRef.current?.click();
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
<Icon
|
|
70
|
+
icon="mingcute:color-picker-line"
|
|
71
|
+
className={`text-xl ${tempValue && "invisible"}`}
|
|
72
|
+
/>
|
|
73
|
+
</button>
|
|
74
|
+
<input
|
|
75
|
+
ref={inputRef}
|
|
76
|
+
type="color"
|
|
77
|
+
className="absolute -bottom-2.5 -z-10"
|
|
78
|
+
value={tempValue}
|
|
79
|
+
onChange={(e) => setTempValue(e.target.value.toUpperCase())}
|
|
80
|
+
{...rest}
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
<div className="relative flex-1 flex items-center">
|
|
84
|
+
<input
|
|
85
|
+
type="text"
|
|
86
|
+
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}`}
|
|
87
|
+
placeholder={rest.placeholder}
|
|
88
|
+
value={tempValue}
|
|
89
|
+
onChange={(e) => setTempValue(e.target.value.toUpperCase())}
|
|
90
|
+
/>
|
|
91
|
+
<div
|
|
92
|
+
className="absolute right-4 p-1 rounded-full text-xl text-slate-400 cursor-pointer hover:bg-secondary-light"
|
|
93
|
+
onClick={(e) => {
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
onClipboardClick && onClipboardClick(tempValue);
|
|
96
|
+
}}
|
|
97
|
+
>
|
|
98
|
+
<Icon icon="fluent:clipboard-text-ltr-32-regular" />
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</label>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
export interface EmailProps
|
|
5
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
6
|
+
options?: {
|
|
7
|
+
width?: "full" | "fit";
|
|
8
|
+
height?: "short" | "medium" | "high";
|
|
9
|
+
};
|
|
10
|
+
label?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Email: React.FC<EmailProps> = ({ options, label, ...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={`grid gap-2 ${width}`}>
|
|
24
|
+
{label && (
|
|
25
|
+
<span
|
|
26
|
+
className={`text-sm select-none ${
|
|
27
|
+
rest.required && "after:content-['*'] after:ml-1 after:text-danger"
|
|
28
|
+
}`}
|
|
29
|
+
>
|
|
30
|
+
{label}
|
|
31
|
+
</span>
|
|
32
|
+
)}
|
|
33
|
+
<input
|
|
34
|
+
type="email"
|
|
35
|
+
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}`}
|
|
36
|
+
{...rest}
|
|
37
|
+
/>
|
|
38
|
+
</label>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { useState, useEffect, useRef } from "react";
|
|
3
|
+
import { Icon } from "@iconify/react";
|
|
4
|
+
|
|
5
|
+
export interface FileProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
6
|
+
options?: {
|
|
7
|
+
variant?: "simple" | "drag&drop";
|
|
8
|
+
buttonOnly?: boolean;
|
|
9
|
+
maxFileSize?: number;
|
|
10
|
+
width?: "full" | "fit";
|
|
11
|
+
height?: "short" | "medium" | "high";
|
|
12
|
+
};
|
|
13
|
+
label?: string;
|
|
14
|
+
files?: any;
|
|
15
|
+
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const File: React.FC<FileProps> = ({
|
|
19
|
+
options,
|
|
20
|
+
label,
|
|
21
|
+
files,
|
|
22
|
+
onChange,
|
|
23
|
+
...rest
|
|
24
|
+
}) => {
|
|
25
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
26
|
+
const [tempValue, setTempValue] = useState<any>();
|
|
27
|
+
const width = options?.width === "fit" ? "w-fit" : "w-full";
|
|
28
|
+
const height =
|
|
29
|
+
options?.height === "short"
|
|
30
|
+
? "py-1"
|
|
31
|
+
: options?.height === "high"
|
|
32
|
+
? "py-2"
|
|
33
|
+
: "py-1.5";
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (files) {
|
|
37
|
+
setTempValue(files);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}, [files]);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
onChange &&
|
|
44
|
+
onChange({
|
|
45
|
+
target: { files: tempValue } as any,
|
|
46
|
+
} as React.ChangeEvent<HTMLInputElement>);
|
|
47
|
+
}, [tempValue]);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
className={`grid gap-2 ${width}`}
|
|
52
|
+
onClick={() => {
|
|
53
|
+
inputRef.current?.click();
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
{label && (
|
|
57
|
+
<span
|
|
58
|
+
className={`text-sm select-none ${
|
|
59
|
+
rest.required && "after:content-['*'] after:ml-1 after:text-danger"
|
|
60
|
+
}`}
|
|
61
|
+
>
|
|
62
|
+
{label}
|
|
63
|
+
</span>
|
|
64
|
+
)}
|
|
65
|
+
{options?.variant !== "drag&drop" ? (
|
|
66
|
+
<div className="w-full flex items-center gap-4">
|
|
67
|
+
<button
|
|
68
|
+
type="button"
|
|
69
|
+
className="p-2 rounded-full hover:bg-secondary-light"
|
|
70
|
+
disabled={rest.disabled}
|
|
71
|
+
>
|
|
72
|
+
<Icon icon="mi:attachment" className="text-xl" />
|
|
73
|
+
</button>
|
|
74
|
+
{!options?.buttonOnly && (
|
|
75
|
+
<input
|
|
76
|
+
type="text"
|
|
77
|
+
className={`flex-1 px-4 border-default border rounded-md bg-secondary-bg cursor-pointer caret-transparent 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 disabled:cursor-default ${height}`}
|
|
78
|
+
placeholder={rest.placeholder}
|
|
79
|
+
disabled={rest.disabled}
|
|
80
|
+
value={
|
|
81
|
+
tempValue
|
|
82
|
+
? `${tempValue?.name} (${
|
|
83
|
+
tempValue?.size < 1000000
|
|
84
|
+
? `${tempValue?.size / 1000} KB`
|
|
85
|
+
: `${tempValue?.size / 1000000} MB`
|
|
86
|
+
})`
|
|
87
|
+
: ""
|
|
88
|
+
}
|
|
89
|
+
/>
|
|
90
|
+
)}
|
|
91
|
+
</div>
|
|
92
|
+
) : (
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
95
|
+
className="relative flex flex-col justify-center items-center gap-8 w-full min-h-60 h-full px-4 py-1.5 border border-dashed rounded-md bg-secondary-bg disabled:bg-secondary-light"
|
|
96
|
+
onDragEnter={(e) => {
|
|
97
|
+
if (!rest.disabled) {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
e.stopPropagation();
|
|
100
|
+
e.currentTarget.classList.add(
|
|
101
|
+
"border-primary",
|
|
102
|
+
"bg-secondary-light"
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}}
|
|
106
|
+
onDragLeave={(e) => {
|
|
107
|
+
if (!rest.disabled) {
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
e.stopPropagation();
|
|
110
|
+
e.currentTarget.classList.remove(
|
|
111
|
+
"border-primary",
|
|
112
|
+
"bg-secondary-light"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}}
|
|
116
|
+
onDragOver={(e) => {
|
|
117
|
+
if (!rest.disabled) {
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
e.stopPropagation();
|
|
120
|
+
}
|
|
121
|
+
}}
|
|
122
|
+
onDrop={(e) => {
|
|
123
|
+
if (!rest.disabled) {
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
e.stopPropagation();
|
|
126
|
+
e.currentTarget.classList.remove(
|
|
127
|
+
"border-primary",
|
|
128
|
+
"bg-secondary-light"
|
|
129
|
+
);
|
|
130
|
+
setTempValue(e.dataTransfer.files[0]);
|
|
131
|
+
}
|
|
132
|
+
}}
|
|
133
|
+
disabled={rest.disabled}
|
|
134
|
+
>
|
|
135
|
+
<Icon
|
|
136
|
+
icon={tempValue ? "mdi:file-outline" : "mynaui:upload"}
|
|
137
|
+
className={`text-5xl pointer-events-none ${
|
|
138
|
+
tempValue ? "" : "text-slate-400"
|
|
139
|
+
}`}
|
|
140
|
+
/>
|
|
141
|
+
<div className="flex flex-col gap-2 items-center pointer-events-none">
|
|
142
|
+
{tempValue ? (
|
|
143
|
+
<span>{tempValue?.name}</span>
|
|
144
|
+
) : (
|
|
145
|
+
<div className="flex gap-2 text-slate-400">
|
|
146
|
+
<span className="underline underline-offset-2">
|
|
147
|
+
Click to upload
|
|
148
|
+
</span>
|
|
149
|
+
<span>or drag and drop</span>
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
<span
|
|
153
|
+
className={`text-sm font-light ${
|
|
154
|
+
tempValue ? "" : "text-slate-400"
|
|
155
|
+
}`}
|
|
156
|
+
>
|
|
157
|
+
{tempValue
|
|
158
|
+
? `${
|
|
159
|
+
tempValue?.size < 1000000
|
|
160
|
+
? `${tempValue?.size / 1000} KB`
|
|
161
|
+
: `${tempValue?.size / 1000000} MB`
|
|
162
|
+
}`
|
|
163
|
+
: `Maximum file size ${options?.maxFileSize} MB`}
|
|
164
|
+
</span>
|
|
165
|
+
</div>
|
|
166
|
+
{tempValue && (
|
|
167
|
+
<div
|
|
168
|
+
className="absolute top-4 right-4 p-1 rounded-full hover:bg-secondary-light"
|
|
169
|
+
onClick={() => {
|
|
170
|
+
setTempValue(null);
|
|
171
|
+
}}
|
|
172
|
+
>
|
|
173
|
+
<Icon icon="pajamas:close" />
|
|
174
|
+
</div>
|
|
175
|
+
)}
|
|
176
|
+
</button>
|
|
177
|
+
)}
|
|
178
|
+
<input
|
|
179
|
+
ref={inputRef}
|
|
180
|
+
type="file"
|
|
181
|
+
className="hidden"
|
|
182
|
+
onChange={(e) => {
|
|
183
|
+
setTempValue(e.target.files![0]);
|
|
184
|
+
}}
|
|
185
|
+
{...rest}
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
189
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { useState, useEffect } from "react";
|
|
3
|
+
import { Icon } from "@iconify/react";
|
|
4
|
+
|
|
5
|
+
export interface NumberProps
|
|
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 Number: React.FC<NumberProps> = ({ options, label, ...rest }) => {
|
|
15
|
+
const [tempValue, setTempValue] = useState("");
|
|
16
|
+
const width = options?.width === "fit" ? "w-fit" : "w-full";
|
|
17
|
+
const height =
|
|
18
|
+
options?.height === "short"
|
|
19
|
+
? "py-1"
|
|
20
|
+
: options?.height === "high"
|
|
21
|
+
? "py-2"
|
|
22
|
+
: "py-1.5";
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (rest.value) {
|
|
26
|
+
setTempValue(rest.value as string);
|
|
27
|
+
return;
|
|
28
|
+
} else if (rest.defaultValue) {
|
|
29
|
+
setTempValue(rest.defaultValue as string);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
}, [rest.value, rest.defaultValue]);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
rest.onChange &&
|
|
36
|
+
rest.onChange({
|
|
37
|
+
target: { value: tempValue } as HTMLInputElement,
|
|
38
|
+
} as React.ChangeEvent<HTMLInputElement>);
|
|
39
|
+
}, [tempValue]);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<label className={`grid gap-2 ${width}`}>
|
|
43
|
+
{label && (
|
|
44
|
+
<span
|
|
45
|
+
className={`text-sm select-none ${
|
|
46
|
+
rest.required && "after:content-['*'] after:ml-1 after:text-danger"
|
|
47
|
+
}`}
|
|
48
|
+
>
|
|
49
|
+
{label}
|
|
50
|
+
</span>
|
|
51
|
+
)}
|
|
52
|
+
<div className="relative flex items-center">
|
|
53
|
+
<input
|
|
54
|
+
type="number"
|
|
55
|
+
className={`w-full px-4 border-default border rounded-md bg-secondary-bg [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none 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}`}
|
|
56
|
+
value={tempValue}
|
|
57
|
+
onChange={(e) => setTempValue(e.target.value)}
|
|
58
|
+
{...rest}
|
|
59
|
+
/>
|
|
60
|
+
<div className="absolute right-4 flex gap-2">
|
|
61
|
+
<button
|
|
62
|
+
type="button"
|
|
63
|
+
className="p-0.5 rounded-md shadow bg-primary-transparent hover:bg-primary-light text-primary disabled:bg-secondary disabled:text-white"
|
|
64
|
+
disabled={rest.disabled}
|
|
65
|
+
onClick={() => {
|
|
66
|
+
setTempValue(
|
|
67
|
+
(
|
|
68
|
+
(parseInt(tempValue) || 0) - ((rest.step as number) || 1)
|
|
69
|
+
).toString()
|
|
70
|
+
);
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
<Icon icon="ic:round-minus" className="text-lg" />
|
|
74
|
+
</button>
|
|
75
|
+
<button
|
|
76
|
+
type="button"
|
|
77
|
+
className="p-0.5 rounded-md shadow bg-primary-transparent hover:bg-primary-light text-primary disabled:bg-secondary disabled:text-white"
|
|
78
|
+
disabled={rest.disabled}
|
|
79
|
+
onClick={() => {
|
|
80
|
+
setTempValue(
|
|
81
|
+
(
|
|
82
|
+
(parseInt(tempValue) || 0) + ((rest.step as number) || 1)
|
|
83
|
+
).toString()
|
|
84
|
+
);
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
<Icon icon="ic:round-plus" className="text-lg" />
|
|
88
|
+
</button>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</label>
|
|
92
|
+
);
|
|
93
|
+
};
|