neogestify-ui-components 2.2.2 → 2.3.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/LICENSE +21 -0
- package/README.md +803 -349
- package/dist/components/ElementLibraryBuilder/index.js +299 -120
- package/dist/components/ElementLibraryBuilder/index.js.map +1 -1
- package/dist/components/ElementLibraryBuilder/index.mjs +300 -121
- package/dist/components/ElementLibraryBuilder/index.mjs.map +1 -1
- package/dist/components/VenueMapEditor/index.js.map +1 -1
- package/dist/components/VenueMapEditor/index.mjs.map +1 -1
- package/dist/components/alerts/index.js +108 -51
- package/dist/components/alerts/index.js.map +1 -1
- package/dist/components/alerts/index.mjs +109 -52
- package/dist/components/alerts/index.mjs.map +1 -1
- package/dist/components/html/index.d.mts +101 -22
- package/dist/components/html/index.d.ts +101 -22
- package/dist/components/html/index.js +506 -166
- package/dist/components/html/index.js.map +1 -1
- package/dist/components/html/index.mjs +507 -167
- package/dist/components/html/index.mjs.map +1 -1
- package/dist/components/icons/index.d.mts +5 -1
- package/dist/components/icons/index.d.ts +5 -1
- package/dist/components/icons/index.js +16 -0
- package/dist/components/icons/index.js.map +1 -1
- package/dist/components/icons/index.mjs +13 -1
- package/dist/components/icons/index.mjs.map +1 -1
- package/dist/context/theme/index.js +59 -37
- package/dist/context/theme/index.js.map +1 -1
- package/dist/context/theme/index.mjs +59 -37
- package/dist/context/theme/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +510 -166
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +508 -168
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/html/Button.tsx +84 -38
- package/src/components/html/Form.tsx +33 -13
- package/src/components/html/Input.tsx +110 -31
- package/src/components/html/Loading.tsx +58 -33
- package/src/components/html/Modal.tsx +67 -20
- package/src/components/html/Select.tsx +92 -39
- package/src/components/html/Table.tsx +274 -50
- package/src/components/html/TextArea.tsx +81 -31
- package/src/components/icons/icons.tsx +32 -0
|
@@ -1,62 +1,109 @@
|
|
|
1
1
|
import { Button } from './Button';
|
|
2
2
|
import { CloseIcon } from '../icons/icons';
|
|
3
|
-
import React, { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
|
|
3
|
+
import React, { useEffect, useState, useRef, forwardRef, useImperativeHandle } from 'react';
|
|
4
|
+
|
|
5
|
+
type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
6
|
+
type ModalVariant = 'default' | 'danger' | 'success' | 'warning';
|
|
4
7
|
|
|
5
8
|
interface ModalProps {
|
|
6
9
|
onClose: () => void;
|
|
7
|
-
title:
|
|
10
|
+
title: React.ReactNode;
|
|
8
11
|
children: React.ReactNode;
|
|
9
12
|
footer?: React.ReactNode;
|
|
13
|
+
/** @deprecated Use size instead */
|
|
10
14
|
maxWidth?: string;
|
|
15
|
+
size?: ModalSize;
|
|
11
16
|
showCloseButton?: boolean;
|
|
12
17
|
zIndex?: number;
|
|
18
|
+
closeOnBackdrop?: boolean;
|
|
19
|
+
closeOnEsc?: boolean;
|
|
20
|
+
variant?: ModalVariant;
|
|
13
21
|
}
|
|
14
22
|
|
|
15
23
|
export interface ModalRef {
|
|
16
24
|
handleClose: () => void;
|
|
17
25
|
}
|
|
18
26
|
|
|
27
|
+
const SIZE_CLASS: Record<ModalSize, string> = {
|
|
28
|
+
sm: 'max-w-sm',
|
|
29
|
+
md: 'max-w-md',
|
|
30
|
+
lg: 'max-w-2xl',
|
|
31
|
+
xl: 'max-w-4xl',
|
|
32
|
+
full: 'max-w-[95vw] w-[95vw]',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const VARIANT_HEADER: Record<ModalVariant, string> = {
|
|
36
|
+
default: 'bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700',
|
|
37
|
+
danger: 'bg-red-50 dark:bg-red-900/30 border-b border-red-200 dark:border-red-800',
|
|
38
|
+
success: 'bg-green-50 dark:bg-green-900/30 border-b border-green-200 dark:border-green-800',
|
|
39
|
+
warning: 'bg-yellow-50 dark:bg-yellow-900/30 border-b border-yellow-200 dark:border-yellow-800',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const VARIANT_TITLE: Record<ModalVariant, string> = {
|
|
43
|
+
default: 'text-gray-900 dark:text-white',
|
|
44
|
+
danger: 'text-red-700 dark:text-red-300',
|
|
45
|
+
success: 'text-green-700 dark:text-green-300',
|
|
46
|
+
warning: 'text-yellow-700 dark:text-yellow-300',
|
|
47
|
+
};
|
|
48
|
+
|
|
19
49
|
export const Modal = forwardRef<ModalRef, ModalProps>(({
|
|
20
50
|
onClose,
|
|
21
51
|
title,
|
|
22
52
|
children,
|
|
23
53
|
footer,
|
|
24
|
-
maxWidth
|
|
54
|
+
maxWidth,
|
|
55
|
+
size,
|
|
25
56
|
showCloseButton = true,
|
|
26
|
-
zIndex = 50
|
|
57
|
+
zIndex = 50,
|
|
58
|
+
closeOnBackdrop = false,
|
|
59
|
+
closeOnEsc = false,
|
|
60
|
+
variant = 'default',
|
|
27
61
|
}, ref) => {
|
|
28
62
|
const [show, setShow] = useState(false);
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
setShow(true);
|
|
32
|
-
}, []);
|
|
63
|
+
const handleCloseRef = useRef<() => void>(() => {});
|
|
33
64
|
|
|
34
65
|
const handleClose = () => {
|
|
35
66
|
setShow(false);
|
|
36
|
-
setTimeout(() =>
|
|
37
|
-
onClose();
|
|
38
|
-
}, 300);
|
|
67
|
+
setTimeout(() => onClose(), 300);
|
|
39
68
|
};
|
|
40
69
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
70
|
+
handleCloseRef.current = handleClose;
|
|
71
|
+
|
|
72
|
+
useEffect(() => { setShow(true); }, []);
|
|
73
|
+
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (!closeOnEsc) return;
|
|
76
|
+
const handler = (e: KeyboardEvent) => {
|
|
77
|
+
if (e.key === 'Escape') handleCloseRef.current();
|
|
78
|
+
};
|
|
79
|
+
document.addEventListener('keydown', handler);
|
|
80
|
+
return () => document.removeEventListener('keydown', handler);
|
|
81
|
+
}, [closeOnEsc]);
|
|
82
|
+
|
|
83
|
+
useImperativeHandle(ref, () => ({ handleClose }));
|
|
84
|
+
|
|
85
|
+
const widthCls = size ? SIZE_CLASS[size] : (maxWidth ?? 'max-w-2xl');
|
|
86
|
+
|
|
87
|
+
const handleBackdropClick = (e: React.MouseEvent) => {
|
|
88
|
+
if (closeOnBackdrop && e.target === e.currentTarget) handleClose();
|
|
89
|
+
};
|
|
44
90
|
|
|
45
91
|
return (
|
|
46
92
|
<dialog
|
|
47
93
|
open={show}
|
|
48
|
-
className={`fixed inset-0 w-full h-full flex items-center justify-center p-4
|
|
94
|
+
className={`fixed inset-0 w-full h-full flex items-center justify-center p-4 transition-opacity duration-300 bg-gray-900/60 backdrop-blur-sm ${show ? 'opacity-100' : 'opacity-0'}`}
|
|
49
95
|
style={{ zIndex: zIndex - 10 }}
|
|
96
|
+
onClick={handleBackdropClick}
|
|
50
97
|
>
|
|
51
98
|
<article
|
|
52
|
-
className={`relative bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-2xl w-full ${
|
|
99
|
+
className={`relative bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-2xl w-full ${widthCls} max-h-[90vh] flex flex-col overflow-hidden`}
|
|
53
100
|
style={{ zIndex }}
|
|
54
101
|
>
|
|
55
|
-
<header className=
|
|
56
|
-
<h2 className=
|
|
102
|
+
<header className={`shrink-0 px-6 py-4 flex items-center justify-between ${VARIANT_HEADER[variant]}`}>
|
|
103
|
+
<h2 className={`text-2xl font-bold ${VARIANT_TITLE[variant]}`}>{title}</h2>
|
|
57
104
|
{showCloseButton && (
|
|
58
105
|
<Button
|
|
59
|
-
variant=
|
|
106
|
+
variant="icon"
|
|
60
107
|
onClick={handleClose}
|
|
61
108
|
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
|
62
109
|
>
|
|
@@ -77,4 +124,4 @@ export const Modal = forwardRef<ModalRef, ModalProps>(({
|
|
|
77
124
|
);
|
|
78
125
|
});
|
|
79
126
|
|
|
80
|
-
Modal.displayName = 'Modal';
|
|
127
|
+
Modal.displayName = 'Modal';
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { type SelectHTMLAttributes, type FC, type ReactNode } from 'react';
|
|
2
|
+
import { ChevronDownIcon } from '../icons/icons';
|
|
3
|
+
|
|
4
|
+
type SelectVariant = 'default' | 'outline' | 'filled' | 'minimal' | 'custom' | 'small';
|
|
5
|
+
type SelectSize = 'sm' | 'md' | 'lg';
|
|
2
6
|
|
|
3
7
|
interface Option {
|
|
4
8
|
value: string | number;
|
|
@@ -10,73 +14,122 @@ interface Option {
|
|
|
10
14
|
interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'size'> {
|
|
11
15
|
options: Option[];
|
|
12
16
|
placeholder?: string;
|
|
13
|
-
variant?:
|
|
14
|
-
|
|
17
|
+
variant?: SelectVariant;
|
|
18
|
+
size?: SelectSize;
|
|
19
|
+
error?: string | boolean;
|
|
15
20
|
helperText?: string;
|
|
16
21
|
label?: string | ReactNode;
|
|
22
|
+
icon?: ReactNode;
|
|
17
23
|
}
|
|
18
24
|
|
|
25
|
+
const SIZE_CLASSES: Record<SelectSize, string> = {
|
|
26
|
+
sm: 'py-1.5 text-xs',
|
|
27
|
+
md: 'py-2 text-sm',
|
|
28
|
+
lg: 'py-2.5 text-base',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const VARIANT_CLASSES: Record<Exclude<SelectVariant, 'small'>, string> = {
|
|
32
|
+
default: 'border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800',
|
|
33
|
+
outline: 'border-2 border-indigo-300 dark:border-indigo-600 bg-transparent',
|
|
34
|
+
filled: 'border border-gray-300 dark:border-gray-600 bg-gray-100 dark:bg-gray-700',
|
|
35
|
+
minimal: 'border-0 border-b border-gray-300 dark:border-gray-600 bg-transparent rounded-none focus:ring-0',
|
|
36
|
+
custom: '',
|
|
37
|
+
};
|
|
38
|
+
|
|
19
39
|
export const Select: FC<SelectProps> = ({
|
|
20
40
|
options,
|
|
21
41
|
placeholder,
|
|
22
42
|
variant = 'default',
|
|
43
|
+
size = 'md',
|
|
23
44
|
error = false,
|
|
24
45
|
helperText,
|
|
25
46
|
label,
|
|
47
|
+
icon,
|
|
26
48
|
className = '',
|
|
27
49
|
id,
|
|
28
50
|
...props
|
|
29
51
|
}) => {
|
|
30
52
|
const selectId = id || `select-${Math.random().toString(36).substring(2, 9)}`;
|
|
31
53
|
|
|
32
|
-
|
|
33
|
-
|
|
54
|
+
// backward compat: variant='small' → size='sm'
|
|
55
|
+
const effectiveSize: SelectSize = variant === 'small' ? 'sm' : size;
|
|
56
|
+
const effectiveVariant: Exclude<SelectVariant, 'small'> = variant === 'small' ? 'default' : variant;
|
|
34
57
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
58
|
+
const hasError = Boolean(error);
|
|
59
|
+
const errorMsg = typeof error === 'string' ? error : '';
|
|
38
60
|
|
|
39
|
-
|
|
40
|
-
|
|
61
|
+
// Compute defaultValue from options[].selected when no controlled value provided
|
|
62
|
+
const computedDefaultValue =
|
|
63
|
+
props.value === undefined && props.defaultValue === undefined
|
|
64
|
+
? options.find((o) => o.selected)?.value?.toString()
|
|
65
|
+
: undefined;
|
|
41
66
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
};
|
|
67
|
+
const baseCls =
|
|
68
|
+
'appearance-none relative block w-full pl-3 pr-9 placeholder-gray-500 dark:placeholder-gray-400 text-gray-900 dark:text-white rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus:border-indigo-500 focus:z-10 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200';
|
|
45
69
|
|
|
46
|
-
const
|
|
70
|
+
const errorCls = hasError
|
|
71
|
+
? 'border-red-300 dark:border-red-600 focus:ring-red-500 dark:focus:ring-red-400 focus:border-red-500'
|
|
72
|
+
: '';
|
|
73
|
+
|
|
74
|
+
const selectCls = [
|
|
75
|
+
baseCls,
|
|
76
|
+
SIZE_CLASSES[effectiveSize],
|
|
77
|
+
VARIANT_CLASSES[effectiveVariant],
|
|
78
|
+
errorCls,
|
|
79
|
+
icon ? 'pl-9' : '',
|
|
80
|
+
className,
|
|
81
|
+
].filter(Boolean).join(' ');
|
|
47
82
|
|
|
48
83
|
return (
|
|
49
84
|
<div className="space-y-1 w-full">
|
|
50
|
-
{label &&
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
85
|
+
{label && (
|
|
86
|
+
typeof label === 'string' ? (
|
|
87
|
+
<label htmlFor={selectId} className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
88
|
+
{label}
|
|
89
|
+
{props.required && <span className="ml-1 text-red-500" aria-hidden="true">*</span>}
|
|
90
|
+
</label>
|
|
91
|
+
) : label
|
|
56
92
|
)}
|
|
57
|
-
<
|
|
58
|
-
{
|
|
59
|
-
<
|
|
60
|
-
{
|
|
61
|
-
</
|
|
93
|
+
<div className="relative">
|
|
94
|
+
{icon && (
|
|
95
|
+
<div className="pointer-events-none absolute inset-y-0 left-0 z-10 flex items-center pl-3 text-gray-400 dark:text-gray-500">
|
|
96
|
+
{icon}
|
|
97
|
+
</div>
|
|
62
98
|
)}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
99
|
+
<select
|
|
100
|
+
id={selectId}
|
|
101
|
+
className={selectCls}
|
|
102
|
+
defaultValue={computedDefaultValue}
|
|
103
|
+
{...props}
|
|
104
|
+
>
|
|
105
|
+
{placeholder && (
|
|
106
|
+
<option value="" disabled className="bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400">
|
|
107
|
+
{placeholder}
|
|
108
|
+
</option>
|
|
109
|
+
)}
|
|
110
|
+
{options.map((option) => (
|
|
111
|
+
<option
|
|
112
|
+
key={option.value}
|
|
113
|
+
value={option.value}
|
|
114
|
+
disabled={option.disabled}
|
|
115
|
+
className={`bg-white dark:bg-gray-800 text-gray-900 dark:text-white${option.disabled ? ' opacity-50' : ''}`}
|
|
116
|
+
>
|
|
117
|
+
{option.label}
|
|
118
|
+
</option>
|
|
119
|
+
))}
|
|
120
|
+
</select>
|
|
121
|
+
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2.5 text-gray-400 dark:text-gray-500">
|
|
122
|
+
<ChevronDownIcon className="w-4 h-4" />
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
{errorMsg && (
|
|
126
|
+
<p className="text-sm text-red-600 dark:text-red-400" role="alert">{errorMsg}</p>
|
|
127
|
+
)}
|
|
128
|
+
{helperText && !errorMsg && (
|
|
129
|
+
<p className={`text-sm ${hasError ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`}>
|
|
77
130
|
{helperText}
|
|
78
131
|
</p>
|
|
79
132
|
)}
|
|
80
133
|
</div>
|
|
81
134
|
);
|
|
82
|
-
};
|
|
135
|
+
};
|