uibee 2.5.11 → 2.7.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/dist/src/components/index.d.ts +5 -3
- package/dist/src/components/index.js +5 -3
- package/dist/src/components/inputs/checkbox.d.ts +13 -0
- package/dist/src/components/inputs/checkbox.js +17 -0
- package/dist/src/components/inputs/input.d.ts +11 -9
- package/dist/src/components/inputs/input.js +76 -9
- package/dist/src/components/inputs/radio.d.ts +14 -0
- package/dist/src/components/inputs/radio.js +17 -0
- package/dist/src/components/inputs/range.d.ts +17 -0
- package/dist/src/components/inputs/range.js +20 -0
- package/dist/src/components/inputs/select.d.ts +11 -11
- package/dist/src/components/inputs/select.js +54 -50
- package/dist/src/components/inputs/shared/dateTimePickerPopup.d.ts +8 -0
- package/dist/src/components/inputs/shared/dateTimePickerPopup.js +117 -0
- package/dist/src/components/inputs/shared/fieldWrapper.d.ts +12 -0
- package/dist/src/components/inputs/shared/fieldWrapper.js +7 -0
- package/dist/src/components/inputs/shared/index.d.ts +5 -0
- package/dist/src/components/inputs/shared/index.js +5 -0
- package/dist/src/components/inputs/shared/inputError.d.ts +6 -0
- package/dist/src/components/inputs/shared/inputError.js +6 -0
- package/dist/src/components/inputs/shared/inputInfo.d.ts +5 -0
- package/dist/src/components/inputs/shared/inputInfo.js +5 -0
- package/dist/src/components/inputs/shared/inputLabel.d.ts +9 -0
- package/dist/src/components/inputs/shared/inputLabel.js +4 -0
- package/dist/src/components/inputs/shared/selectionWrapper.d.ts +13 -0
- package/dist/src/components/inputs/shared/selectionWrapper.js +7 -0
- package/dist/src/components/inputs/switch.d.ts +10 -7
- package/dist/src/components/inputs/switch.js +13 -5
- package/dist/src/components/inputs/textarea.d.ts +15 -0
- package/dist/src/components/inputs/textarea.js +14 -0
- package/dist/src/components/toggle/language.js +1 -1
- package/dist/src/components/toggle/theme.js +1 -1
- package/dist/src/globals.css +397 -162
- package/dist/src/hooks/index.d.ts +1 -0
- package/dist/src/hooks/index.js +1 -0
- package/dist/src/hooks/useClickOutside.d.ts +1 -0
- package/dist/src/hooks/useClickOutside.js +20 -0
- package/dist/src/utils/index.d.ts +0 -3
- package/dist/src/utils/index.js +0 -3
- package/eslint.config.js +1 -0
- package/package.json +5 -3
- package/src/components/index.ts +5 -3
- package/src/components/inputs/checkbox.tsx +66 -0
- package/src/components/inputs/input.tsx +137 -35
- package/src/components/inputs/radio.tsx +67 -0
- package/src/components/inputs/range.tsx +84 -0
- package/src/components/inputs/select.tsx +137 -172
- package/src/components/inputs/shared/dateTimePickerPopup.tsx +219 -0
- package/src/components/inputs/shared/fieldWrapper.tsx +44 -0
- package/src/components/inputs/shared/index.ts +5 -0
- package/src/components/inputs/shared/inputError.tsx +21 -0
- package/src/components/inputs/shared/inputInfo.tsx +17 -0
- package/src/components/inputs/shared/inputLabel.tsx +19 -0
- package/src/components/inputs/shared/selectionWrapper.tsx +47 -0
- package/src/components/inputs/switch.tsx +48 -25
- package/src/components/inputs/textarea.tsx +65 -0
- package/src/components/logo/logo.tsx +1 -1
- package/src/components/toggle/language.tsx +1 -1
- package/src/components/toggle/theme.tsx +1 -1
- package/src/globals.css +36 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useClickOutside.ts +27 -0
- package/src/types/utils.d.ts +0 -32
- package/src/utils/index.ts +0 -3
- package/dist/src/components/inputs/erase.d.ts +0 -3
- package/dist/src/components/inputs/erase.js +0 -5
- package/dist/src/components/inputs/label.d.ts +0 -10
- package/dist/src/components/inputs/label.js +0 -13
- package/dist/src/components/inputs/markdown.d.ts +0 -15
- package/dist/src/components/inputs/markdown.js +0 -32
- package/dist/src/components/inputs/tag.d.ts +0 -11
- package/dist/src/components/inputs/tag.js +0 -44
- package/dist/src/components/inputs/tooltip.d.ts +0 -4
- package/dist/src/components/inputs/tooltip.js +0 -4
- package/dist/src/utils/cookies/cookies.d.ts +0 -4
- package/dist/src/utils/cookies/cookies.js +0 -36
- package/dist/src/utils/discord/discordAlert.d.ts +0 -2
- package/dist/src/utils/discord/discordAlert.js +0 -32
- package/dist/src/utils/sql/alertSlowQuery.d.ts +0 -2
- package/dist/src/utils/sql/alertSlowQuery.js +0 -25
- package/src/components/inputs/erase.tsx +0 -13
- package/src/components/inputs/label.tsx +0 -31
- package/src/components/inputs/markdown.tsx +0 -129
- package/src/components/inputs/tag.tsx +0 -137
- package/src/components/inputs/tooltip.tsx +0 -12
- package/src/utils/cookies/cookies.ts +0 -43
- package/src/utils/discord/discordAlert.ts +0 -44
- package/src/utils/sql/alertSlowQuery.ts +0 -37
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export { default as Input } from './inputs/input';
|
|
2
|
-
export { default as
|
|
3
|
-
export { default as
|
|
4
|
-
export { default as Textarea } from './inputs/markdown';
|
|
2
|
+
export { default as Textarea } from './inputs/textarea';
|
|
3
|
+
export { default as Checkbox } from './inputs/checkbox';
|
|
5
4
|
export { default as Select } from './inputs/select';
|
|
5
|
+
export { default as Switch } from './inputs/switch';
|
|
6
|
+
export { default as Radio } from './inputs/radio';
|
|
7
|
+
export { default as Range } from './inputs/range';
|
|
6
8
|
export { default as Logo } from './logo/logo';
|
|
7
9
|
export { default as LogoSmall } from './logo/logoSmall';
|
|
8
10
|
export { default as ThemeToggle } from './toggle/theme';
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// Input components
|
|
2
2
|
export { default as Input } from './inputs/input';
|
|
3
|
-
export { default as
|
|
4
|
-
export { default as
|
|
5
|
-
export { default as Textarea } from './inputs/markdown';
|
|
3
|
+
export { default as Textarea } from './inputs/textarea';
|
|
4
|
+
export { default as Checkbox } from './inputs/checkbox';
|
|
6
5
|
export { default as Select } from './inputs/select';
|
|
6
|
+
export { default as Switch } from './inputs/switch';
|
|
7
|
+
export { default as Radio } from './inputs/radio';
|
|
8
|
+
export { default as Range } from './inputs/range';
|
|
7
9
|
// Logos
|
|
8
10
|
export { default as Logo } from './logo/logo';
|
|
9
11
|
export { default as LogoSmall } from './logo/logoSmall';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ChangeEvent } from 'react';
|
|
2
|
+
export type CheckboxProps = {
|
|
3
|
+
label?: string;
|
|
4
|
+
name: string;
|
|
5
|
+
checked?: boolean;
|
|
6
|
+
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
7
|
+
className?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
info?: string;
|
|
11
|
+
required?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export default function Checkbox({ label, name, checked, onChange, className, disabled, error, info, required, }: CheckboxProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Check } from 'lucide-react';
|
|
3
|
+
import { SelectionWrapper } from './shared';
|
|
4
|
+
export default function Checkbox({ label, name, checked, onChange, className, disabled, error, info, required, }) {
|
|
5
|
+
return (_jsx(SelectionWrapper, { label: label, name: name, required: required, info: info, error: error, className: className, disabled: disabled, children: _jsxs("div", { className: 'relative flex items-center', children: [_jsx("input", { id: name, name: name, type: 'checkbox', checked: checked, onChange: onChange, disabled: disabled, required: required, className: `
|
|
6
|
+
peer appearance-none h-5 w-5 rounded border border-login-500 bg-login-500/50
|
|
7
|
+
checked:bg-login checked:border-login
|
|
8
|
+
focus:outline-none focus:ring-2 focus:ring-login/50
|
|
9
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
10
|
+
cursor-pointer transition-all duration-200
|
|
11
|
+
${error ? 'border-red-500' : ''}
|
|
12
|
+
` }), _jsx(Check, { className: `
|
|
13
|
+
absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2
|
|
14
|
+
w-3.5 h-3.5 pointer-events-none text-white opacity-0
|
|
15
|
+
peer-checked:opacity-100 transition-opacity duration-200
|
|
16
|
+
` })] }) }));
|
|
17
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type InputProps = {
|
|
1
|
+
import { type ChangeEvent, type JSX } from 'react';
|
|
2
|
+
export type InputProps = {
|
|
3
|
+
label?: string;
|
|
3
4
|
name: string;
|
|
4
|
-
type
|
|
5
|
-
|
|
5
|
+
type?: React.HTMLInputTypeAttribute;
|
|
6
|
+
placeholder?: string;
|
|
6
7
|
value?: string | number;
|
|
7
|
-
|
|
8
|
+
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
9
|
+
error?: string;
|
|
8
10
|
className?: string;
|
|
9
|
-
|
|
11
|
+
disabled?: boolean;
|
|
10
12
|
required?: boolean;
|
|
11
|
-
|
|
13
|
+
icon?: JSX.Element;
|
|
14
|
+
info?: string;
|
|
12
15
|
};
|
|
13
|
-
export default function Input({ name, type,
|
|
14
|
-
export {};
|
|
16
|
+
export default function Input({ label, name, type, placeholder, value, onChange, error, className, disabled, required, icon, info, }: InputProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,11 +1,78 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import { Calendar, Clock } from 'lucide-react';
|
|
4
|
+
import { FieldWrapper } from './shared';
|
|
5
|
+
import DateTimePickerPopup from './shared/dateTimePickerPopup';
|
|
6
|
+
import useClickOutside from '../../hooks/useClickOutside';
|
|
7
|
+
export default function Input({ label, name, type = 'text', placeholder, value, onChange, error, className, disabled, required, icon, info, }) {
|
|
8
|
+
const localRef = useRef(null);
|
|
9
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
10
|
+
const containerRef = useClickOutside(() => setIsOpen(false));
|
|
11
|
+
const isDateType = ['date', 'datetime-local', 'time'].includes(type);
|
|
12
|
+
const handleIconClick = () => {
|
|
13
|
+
if (isDateType && !disabled) {
|
|
14
|
+
setIsOpen(!isOpen);
|
|
15
|
+
}
|
|
16
|
+
else if (localRef.current && !disabled) {
|
|
17
|
+
localRef.current.focus();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const handleDateChange = (date) => {
|
|
21
|
+
if (!onChange)
|
|
22
|
+
return;
|
|
23
|
+
const pad = (n) => n.toString().padStart(2, '0');
|
|
24
|
+
const yyyy = date.getFullYear();
|
|
25
|
+
const MM = pad(date.getMonth() + 1);
|
|
26
|
+
const dd = pad(date.getDate());
|
|
27
|
+
const hh = pad(date.getHours());
|
|
28
|
+
const mm = pad(date.getMinutes());
|
|
29
|
+
let newValue = '';
|
|
30
|
+
if (type === 'date')
|
|
31
|
+
newValue = `${dd}/${MM}/${yyyy}`;
|
|
32
|
+
else if (type === 'time')
|
|
33
|
+
newValue = `${hh}:${mm}`;
|
|
34
|
+
else if (type === 'datetime-local')
|
|
35
|
+
newValue = `${dd}/${MM}/${yyyy} ${hh}:${mm}`;
|
|
36
|
+
const event = {
|
|
37
|
+
target: {
|
|
38
|
+
name,
|
|
39
|
+
value: newValue,
|
|
40
|
+
type,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
onChange(event);
|
|
44
|
+
};
|
|
45
|
+
let displayIcon = icon;
|
|
46
|
+
if (!displayIcon && isDateType) {
|
|
47
|
+
if (type === 'time') {
|
|
48
|
+
displayIcon = _jsx(Clock, { className: 'w-4 h-4' });
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
displayIcon = _jsx(Calendar, { className: 'w-4 h-4' });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const getDateValue = () => {
|
|
55
|
+
if (!value)
|
|
56
|
+
return null;
|
|
57
|
+
if (type === 'time') {
|
|
58
|
+
const date = new Date(`2000-01-01T${value}`);
|
|
59
|
+
return isNaN(date.getTime()) ? null : date;
|
|
60
|
+
}
|
|
61
|
+
const date = new Date(value);
|
|
62
|
+
return isNaN(date.getTime()) ? null : date;
|
|
63
|
+
};
|
|
64
|
+
return (_jsx(FieldWrapper, { label: label, name: name, required: required, info: info, error: error, className: className, children: _jsxs("div", { className: 'relative flex items-center', ref: containerRef, children: [displayIcon && (_jsx("div", { className: `
|
|
65
|
+
absolute left-3 text-login-200
|
|
66
|
+
${isDateType && !disabled ? 'cursor-pointer hover:text-login-text' : 'pointer-events-none'}
|
|
67
|
+
`, onClick: handleIconClick, children: displayIcon })), _jsx("input", { ref: localRef, id: name, name: name, type: isDateType ? 'text' : type, placeholder: placeholder, value: value, onChange: onChange, disabled: disabled, required: required, readOnly: isDateType, onClick: () => isDateType && !disabled && setIsOpen(true), title: label, "aria-invalid": !!error, "aria-describedby": error ? `${name}-error` : undefined, className: `
|
|
68
|
+
w-full rounded-md bg-login-500/50 border border-login-500
|
|
69
|
+
text-login-text placeholder-login-200
|
|
70
|
+
focus:outline-none focus:border-login focus:ring-1 focus:ring-login
|
|
71
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
72
|
+
py-2 ${displayIcon ? 'pl-10 pr-3' : 'px-3'}
|
|
73
|
+
transition-all duration-200
|
|
74
|
+
input-reset
|
|
75
|
+
${error ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : ''}
|
|
76
|
+
${isDateType && !disabled ? 'cursor-pointer' : ''}
|
|
77
|
+
` }), isOpen && isDateType && !disabled && (_jsx(DateTimePickerPopup, { value: getDateValue(), onChange: handleDateChange, type: type, onClose: () => setIsOpen(false) }))] }) }));
|
|
11
78
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ChangeEvent } from 'react';
|
|
2
|
+
export type RadioProps = {
|
|
3
|
+
label?: string;
|
|
4
|
+
name: string;
|
|
5
|
+
value: string | number;
|
|
6
|
+
checked?: boolean;
|
|
7
|
+
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
8
|
+
className?: string;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
info?: string;
|
|
12
|
+
required?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export default function Radio({ label, name, value, checked, onChange, className, disabled, error, info, required, }: RadioProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SelectionWrapper } from './shared';
|
|
3
|
+
export default function Radio({ label, name, value, checked, onChange, className, disabled, error, info, required, }) {
|
|
4
|
+
const id = `${name}-${value}`;
|
|
5
|
+
return (_jsx(SelectionWrapper, { label: label, name: id, required: required, info: info, error: error, className: className, disabled: disabled, children: _jsxs("div", { className: 'relative flex items-center', children: [_jsx("input", { id: id, name: name, type: 'radio', value: value, checked: checked, onChange: onChange, disabled: disabled, required: required, className: `
|
|
6
|
+
peer appearance-none h-5 w-5 rounded-full border border-login-500 bg-login-500/50
|
|
7
|
+
checked:bg-login checked:border-login
|
|
8
|
+
focus:outline-none focus:ring-2 focus:ring-login/50
|
|
9
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
10
|
+
cursor-pointer transition-all duration-200
|
|
11
|
+
${error ? 'border-red-500' : ''}
|
|
12
|
+
` }), _jsx("div", { className: `
|
|
13
|
+
absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2
|
|
14
|
+
w-2 h-2 rounded-full bg-white pointer-events-none opacity-0
|
|
15
|
+
peer-checked:opacity-100 transition-opacity duration-200
|
|
16
|
+
` })] }) }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ChangeEvent } from 'react';
|
|
2
|
+
export type RangeProps = {
|
|
3
|
+
label?: string;
|
|
4
|
+
name: string;
|
|
5
|
+
min?: number;
|
|
6
|
+
max?: number;
|
|
7
|
+
step?: number;
|
|
8
|
+
value?: number;
|
|
9
|
+
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
10
|
+
error?: string;
|
|
11
|
+
className?: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
required?: boolean;
|
|
14
|
+
info?: string;
|
|
15
|
+
showValue?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export default function Range({ label, name, min, max, step, value, onChange, error, className, disabled, required, info, showValue, }: RangeProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { FieldWrapper } from './shared';
|
|
3
|
+
export default function Range({ label, name, min = 0, max = 100, step = 1, value = 0, onChange, error, className, disabled, required, info, showValue = true, }) {
|
|
4
|
+
return (_jsx(FieldWrapper, { label: label, name: name, required: required, info: info, error: error, className: className, children: _jsxs("div", { className: 'flex items-center gap-4', children: [_jsx("input", { id: name, name: name, type: 'range', min: min, max: max, step: step, value: value, onChange: onChange, disabled: disabled, required: required, title: label, "aria-invalid": !!error, "aria-describedby": error ? `${name}-error` : undefined, className: `
|
|
5
|
+
flex-1 h-2 bg-login-500 rounded-lg appearance-none cursor-pointer
|
|
6
|
+
accent-login
|
|
7
|
+
[&::-webkit-slider-thumb]:appearance-none
|
|
8
|
+
[&::-webkit-slider-thumb]:w-4
|
|
9
|
+
[&::-webkit-slider-thumb]:h-4
|
|
10
|
+
[&::-webkit-slider-thumb]:rounded-full
|
|
11
|
+
[&::-webkit-slider-thumb]:bg-login
|
|
12
|
+
[&::-moz-range-thumb]:w-4
|
|
13
|
+
[&::-moz-range-thumb]:h-4
|
|
14
|
+
[&::-moz-range-thumb]:rounded-full
|
|
15
|
+
[&::-moz-range-thumb]:bg-login
|
|
16
|
+
[&::-moz-range-thumb]:border-none
|
|
17
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
18
|
+
${error ? 'accent-red-500 [&::-webkit-slider-thumb]:bg-red-500 [&::-moz-range-thumb]:bg-red-500' : ''}
|
|
19
|
+
` }), showValue && (_jsx("span", { className: 'text-login-text text-sm font-medium min-w-10 text-right', children: value }))] }) }));
|
|
20
|
+
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
type Option = {
|
|
1
|
+
export type Option = {
|
|
2
2
|
value: string | number;
|
|
3
3
|
label: string;
|
|
4
|
-
image?: string;
|
|
5
4
|
};
|
|
6
|
-
type SelectProps = {
|
|
5
|
+
export type SelectProps = {
|
|
6
|
+
label?: string;
|
|
7
7
|
name: string;
|
|
8
|
-
|
|
9
|
-
value: string | number;
|
|
10
|
-
setValue: (_: string | number) => void;
|
|
8
|
+
value?: string | number | null;
|
|
9
|
+
onChange?: (value: string | number | null) => void;
|
|
11
10
|
options: Option[];
|
|
11
|
+
error?: string;
|
|
12
12
|
className?: string;
|
|
13
|
-
|
|
13
|
+
disabled?: boolean;
|
|
14
14
|
required?: boolean;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
info?: string;
|
|
17
|
+
clearable?: boolean;
|
|
17
18
|
};
|
|
18
|
-
export default function Select({
|
|
19
|
-
export {};
|
|
19
|
+
export default function Select({ label, name, value, onChange, options, error, className, disabled, required, placeholder, info, clearable, }: SelectProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,53 +1,57 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
export default function Select({
|
|
8
|
-
const [
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
import { useClickOutside } from '../../hooks';
|
|
5
|
+
import { ChevronDown, X } from 'lucide-react';
|
|
6
|
+
import { FieldWrapper } from './shared';
|
|
7
|
+
export default function Select({ label, name, value, onChange, options, error, className, disabled, required, placeholder = 'Select an option', info, clearable = true, }) {
|
|
8
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
9
|
+
const [selectedOption, setSelectedOption] = useState(options.find(opt => opt.value === value));
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
setSelectedOption(options.find(opt => opt.value === value));
|
|
12
|
+
}, [value, options]);
|
|
13
|
+
const containerRef = useClickOutside(() => setIsOpen(false));
|
|
14
|
+
const handleSelect = (option) => {
|
|
15
|
+
if (disabled)
|
|
16
|
+
return;
|
|
17
|
+
setSelectedOption(option);
|
|
18
|
+
setIsOpen(false);
|
|
19
|
+
if (onChange) {
|
|
20
|
+
onChange(option.value);
|
|
17
21
|
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
'
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
22
|
+
};
|
|
23
|
+
const handleClear = (e) => {
|
|
24
|
+
e.stopPropagation();
|
|
25
|
+
if (disabled)
|
|
26
|
+
return;
|
|
27
|
+
setSelectedOption(undefined);
|
|
28
|
+
if (onChange) {
|
|
29
|
+
onChange(null);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
return (_jsxs(FieldWrapper, { label: label, name: name, required: required, info: info, error: error, className: className, children: [_jsxs("div", { className: 'relative', ref: containerRef, children: [_jsxs("button", { type: 'button', onClick: () => !disabled && setIsOpen(!isOpen), disabled: disabled, "aria-haspopup": 'listbox', "aria-expanded": isOpen, "aria-labelledby": label ? undefined : name, className: `
|
|
33
|
+
w-full rounded-md bg-login-500/50 border border-login-500
|
|
34
|
+
text-login-text text-left
|
|
35
|
+
focus:outline-none focus:border-login focus:ring-1 focus:ring-login
|
|
36
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
37
|
+
py-2 pl-3 pr-10
|
|
38
|
+
transition-all duration-200
|
|
39
|
+
flex items-center justify-between
|
|
40
|
+
${error ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : ''}
|
|
41
|
+
${!selectedOption ? 'text-login-200' : ''}
|
|
42
|
+
`, title: label, children: [_jsx("span", { className: 'truncate', children: selectedOption ? selectedOption.label : placeholder }), _jsxs("div", { className: 'absolute inset-y-0 right-0 flex items-center px-2 gap-1', children: [clearable && selectedOption && !disabled && (_jsx("div", { role: 'button', onClick: handleClear, className: `
|
|
43
|
+
p-1 hover:bg-login-500 rounded-full text-login-200
|
|
44
|
+
hover:text-red-400 transition-colors cursor-pointer
|
|
45
|
+
`, title: 'Clear selection', children: _jsx(X, { className: 'w-3 h-3' }) })), _jsx("div", { className: `
|
|
46
|
+
text-login-200 pointer-events-none
|
|
47
|
+
transition-transform duration-200
|
|
48
|
+
${isOpen ? 'rotate-180' : ''}
|
|
49
|
+
`, children: _jsx(ChevronDown, { className: 'w-4 h-4' }) })] })] }), isOpen && (_jsx("div", { className: `
|
|
50
|
+
absolute z-50 w-full mt-1 bg-login-600 border border-login-500
|
|
51
|
+
rounded-md shadow-lg max-h-60 overflow-auto noscroll
|
|
52
|
+
`, children: options.length > 0 ? (_jsx("ul", { className: 'py-1', role: 'listbox', children: options.map((option) => (_jsx("li", { role: 'option', "aria-selected": selectedOption?.value === option.value, children: _jsx("button", { type: 'button', onClick: () => handleSelect(option), className: `
|
|
53
|
+
w-full text-left px-3 py-2 text-sm
|
|
54
|
+
hover:bg-login-500 transition-colors duration-150
|
|
55
|
+
${selectedOption?.value === option.value ? 'bg-login-500 text-login' : 'text-login-text'}
|
|
56
|
+
`, children: option.label }) }, option.value))) })) : (_jsx("div", { className: 'px-3 py-2 text-sm text-login-200', children: "No options available" })) }))] }), _jsx("input", { type: 'hidden', name: name, value: selectedOption?.value || '', required: required })] }));
|
|
53
57
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type DateTimePickerPopupProps = {
|
|
2
|
+
value: Date | null;
|
|
3
|
+
onChange: (date: Date) => void;
|
|
4
|
+
type: 'date' | 'time' | 'datetime-local';
|
|
5
|
+
onClose?: () => void;
|
|
6
|
+
};
|
|
7
|
+
export default function DateTimePickerPopup({ value, onChange, type, onClose, }: DateTimePickerPopupProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
|
4
|
+
const DAYS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
|
|
5
|
+
const MONTHS = [
|
|
6
|
+
'January', 'February', 'March', 'April', 'May', 'June',
|
|
7
|
+
'July', 'August', 'September', 'October', 'November', 'December'
|
|
8
|
+
];
|
|
9
|
+
export default function DateTimePickerPopup({ value, onChange, type, onClose, }) {
|
|
10
|
+
const [currentDate, setCurrentDate] = useState(new Date());
|
|
11
|
+
const [timeInput, setTimeInput] = useState({
|
|
12
|
+
hours: value ? value.getHours().toString() : '0',
|
|
13
|
+
minutes: value ? value.getMinutes().toString() : '0',
|
|
14
|
+
});
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (value) {
|
|
17
|
+
setCurrentDate(value);
|
|
18
|
+
setTimeInput(prev => ({
|
|
19
|
+
hours: prev.hours === '' && value.getHours() === 0 ? '' : value.getHours().toString(),
|
|
20
|
+
minutes: prev.minutes === '' && value.getMinutes() === 0 ? '' : value.getMinutes().toString(),
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
}, [value]);
|
|
24
|
+
const handleDateSelect = (day) => {
|
|
25
|
+
const newDate = new Date(currentDate);
|
|
26
|
+
newDate.setDate(day);
|
|
27
|
+
if (value) {
|
|
28
|
+
newDate.setHours(value.getHours());
|
|
29
|
+
newDate.setMinutes(value.getMinutes());
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
newDate.setHours(0, 0, 0, 0);
|
|
33
|
+
}
|
|
34
|
+
onChange(newDate);
|
|
35
|
+
if (type === 'date' && onClose) {
|
|
36
|
+
onClose();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const handleTimeChange = (timeUnit, val) => {
|
|
40
|
+
const newDate = value ? new Date(value) : new Date();
|
|
41
|
+
if (!value) {
|
|
42
|
+
newDate.setHours(0, 0, 0, 0);
|
|
43
|
+
}
|
|
44
|
+
if (timeUnit === 'hours') {
|
|
45
|
+
if (val < 0 || val > 23)
|
|
46
|
+
return;
|
|
47
|
+
newDate.setHours(val);
|
|
48
|
+
}
|
|
49
|
+
if (timeUnit === 'minutes') {
|
|
50
|
+
if (val < 0 || val > 59)
|
|
51
|
+
return;
|
|
52
|
+
newDate.setMinutes(val);
|
|
53
|
+
}
|
|
54
|
+
onChange(newDate);
|
|
55
|
+
};
|
|
56
|
+
const onTimeInputChange = (unit, val) => {
|
|
57
|
+
if (val === '') {
|
|
58
|
+
setTimeInput(prev => ({ ...prev, [unit]: '' }));
|
|
59
|
+
handleTimeChange(unit, 0);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!/^\d+$/.test(val))
|
|
63
|
+
return;
|
|
64
|
+
const num = parseInt(val);
|
|
65
|
+
if (unit === 'hours' && num > 23)
|
|
66
|
+
return;
|
|
67
|
+
if (unit === 'minutes' && num > 59)
|
|
68
|
+
return;
|
|
69
|
+
setTimeInput(prev => ({ ...prev, [unit]: val }));
|
|
70
|
+
handleTimeChange(unit, num);
|
|
71
|
+
};
|
|
72
|
+
const onTimeInputBlur = (unit) => {
|
|
73
|
+
if (timeInput[unit] === '') {
|
|
74
|
+
const num = unit === 'hours' ? (value?.getHours() ?? 0) : (value?.getMinutes() ?? 0);
|
|
75
|
+
setTimeInput(prev => ({ ...prev, [unit]: num.toString() }));
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const getDaysInMonth = (year, month) => new Date(year, month + 1, 0).getDate();
|
|
79
|
+
const getFirstDayOfMonth = (year, month) => new Date(year, month, 1).getDay();
|
|
80
|
+
const renderCalendar = () => {
|
|
81
|
+
const year = currentDate.getFullYear();
|
|
82
|
+
const month = currentDate.getMonth();
|
|
83
|
+
const daysInMonth = getDaysInMonth(year, month);
|
|
84
|
+
const firstDay = getFirstDayOfMonth(year, month);
|
|
85
|
+
const days = [];
|
|
86
|
+
for (let i = 0; i < firstDay; i++) {
|
|
87
|
+
days.push(_jsx("div", { className: 'w-8 h-8' }, `empty-${i}`));
|
|
88
|
+
}
|
|
89
|
+
for (let i = 1; i <= daysInMonth; i++) {
|
|
90
|
+
const isSelected = value &&
|
|
91
|
+
value.getDate() === i &&
|
|
92
|
+
value.getMonth() === month &&
|
|
93
|
+
value.getFullYear() === year;
|
|
94
|
+
const isToday = new Date().getDate() === i &&
|
|
95
|
+
new Date().getMonth() === month &&
|
|
96
|
+
new Date().getFullYear() === year;
|
|
97
|
+
days.push(_jsx("button", { type: 'button', onClick: () => handleDateSelect(i), className: `
|
|
98
|
+
w-8 h-8 flex items-center justify-center rounded-full text-sm
|
|
99
|
+
hover:bg-login-500 transition-colors
|
|
100
|
+
${isSelected ? 'bg-login! text-white! hover:bg-login!' : ''}
|
|
101
|
+
${!isSelected && isToday ? 'text-login! font-bold' : ''}
|
|
102
|
+
${!isSelected && !isToday ? 'text-login-text!' : ''}
|
|
103
|
+
`, children: i }, i));
|
|
104
|
+
}
|
|
105
|
+
return (_jsxs("div", { className: 'p-2', children: [_jsxs("div", { className: 'flex items-center justify-between mb-2', children: [_jsx("button", { type: 'button', onClick: () => setCurrentDate(new Date(year, month - 1)), className: 'p-1 hover:bg-login-500 rounded-full text-login-text', children: _jsx(ChevronLeft, { className: 'w-4 h-4' }) }), _jsxs("span", { className: 'font-medium text-login-text', children: [MONTHS[month], " ", year] }), _jsx("button", { type: 'button', onClick: () => setCurrentDate(new Date(year, month + 1)), className: 'p-1 hover:bg-login-500 rounded-full text-login-text', children: _jsx(ChevronRight, { className: 'w-4 h-4' }) })] }), _jsx("div", { className: 'grid grid-cols-7 gap-1 mb-1', children: DAYS.map(d => (_jsx("div", { className: 'w-8 text-center text-xs text-login-200 font-medium', children: d }, d))) }), _jsx("div", { className: 'grid grid-cols-7 gap-1', children: days })] }));
|
|
106
|
+
};
|
|
107
|
+
const renderTimePicker = () => {
|
|
108
|
+
return (_jsxs("div", { className: 'p-2 border-t border-login-500 flex justify-center gap-2', children: [_jsxs("div", { className: 'flex flex-col items-center', children: [_jsx("label", { className: 'text-xs text-login-200 mb-1', children: "Hour" }), _jsx("input", { type: 'text', inputMode: 'numeric', value: timeInput.hours, onChange: (e) => onTimeInputChange('hours', e.target.value), onBlur: () => onTimeInputBlur('hours'), className: `
|
|
109
|
+
w-16 p-1 bg-login-500 rounded text-center text-login-text
|
|
110
|
+
border border-login-500 focus:border-login outline-none
|
|
111
|
+
` })] }), _jsx("div", { className: 'flex items-end pb-2 text-login-text', children: ":" }), _jsxs("div", { className: 'flex flex-col items-center', children: [_jsx("label", { className: 'text-xs text-login-200 mb-1', children: "Minute" }), _jsx("input", { type: 'text', inputMode: 'numeric', value: timeInput.minutes, onChange: (e) => onTimeInputChange('minutes', e.target.value), onBlur: () => onTimeInputBlur('minutes'), className: `
|
|
112
|
+
w-16 p-1 bg-login-500 rounded text-center text-login-text
|
|
113
|
+
border border-login-500 focus:border-login outline-none
|
|
114
|
+
` })] })] }));
|
|
115
|
+
};
|
|
116
|
+
return (_jsxs("div", { className: 'absolute top-full left-0 z-50 mt-1 bg-login-600 border border-login-500 rounded-md shadow-lg p-1 min-w-70', children: [type !== 'time' && renderCalendar(), (type === 'time' || type === 'datetime-local') && renderTimePicker()] }));
|
|
117
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
interface FieldWrapperProps {
|
|
3
|
+
label?: string;
|
|
4
|
+
name: string;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
info?: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
export default function FieldWrapper({ label, name, required, info, error, children, className, }: FieldWrapperProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import InputLabel from './inputLabel';
|
|
3
|
+
import InputInfo from './inputInfo';
|
|
4
|
+
import InputError from './inputError';
|
|
5
|
+
export default function FieldWrapper({ label, name, required, info, error, children, className, }) {
|
|
6
|
+
return (_jsxs("div", { className: `flex flex-col gap-1 w-full relative ${className || ''}`, children: [(label || info) && (_jsxs("div", { className: 'flex items-center justify-between mb-1', children: [label && (_jsx(InputLabel, { label: label, name: name, required: required, className: 'ml-1' })), info && _jsx(InputInfo, { info: info })] })), children, _jsx(InputError, { error: error, id: `${name}-error` })] }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as FieldWrapper } from './fieldWrapper';
|
|
2
|
+
export { default as SelectionWrapper } from './selectionWrapper';
|
|
3
|
+
export { default as InputLabel } from './inputLabel';
|
|
4
|
+
export { default as InputInfo } from './inputInfo';
|
|
5
|
+
export { default as InputError } from './inputError';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as FieldWrapper } from './fieldWrapper';
|
|
2
|
+
export { default as SelectionWrapper } from './selectionWrapper';
|
|
3
|
+
export { default as InputLabel } from './inputLabel';
|
|
4
|
+
export { default as InputInfo } from './inputInfo';
|
|
5
|
+
export { default as InputError } from './inputError';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
export default function InputError({ error, id }) {
|
|
3
|
+
if (!error)
|
|
4
|
+
return _jsx("div", { className: 'h-4' });
|
|
5
|
+
return (_jsx("div", { className: 'h-4', children: _jsx("span", { id: id, className: 'text-xs text-red-500 ml-1 truncate block', role: 'alert', title: error, children: error }) }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { CircleHelp } from 'lucide-react';
|
|
3
|
+
export default function InputInfo({ info }) {
|
|
4
|
+
return (_jsx("div", { className: 'text-login-200 hover:text-login-text transition-colors', "aria-label": info, title: info, children: _jsx(CircleHelp, { className: 'w-4 h-4' }) }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface InputLabelProps {
|
|
2
|
+
label: string;
|
|
3
|
+
name: string;
|
|
4
|
+
required?: boolean;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
export default function InputLabel({ label, name, required, disabled, className }: InputLabelProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export default function InputLabel({ label, name, required, disabled, className }) {
|
|
3
|
+
return (_jsxs("label", { htmlFor: name, className: `text-sm font-medium text-login-text ${disabled ? 'opacity-50 cursor-not-allowed' : ''} ${className || ''}`, title: label, children: [label, " ", required && _jsx("span", { className: 'text-red-500', children: "*" })] }));
|
|
4
|
+
}
|