uibee 2.7.17 → 2.8.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/inputs/checkbox.d.ts +4 -9
- package/dist/src/components/inputs/checkbox.js +3 -2
- package/dist/src/components/inputs/input.d.ts +4 -11
- package/dist/src/components/inputs/input.js +30 -8
- package/dist/src/components/inputs/radio.d.ts +4 -10
- package/dist/src/components/inputs/radio.js +4 -2
- package/dist/src/components/inputs/range.d.ts +3 -11
- package/dist/src/components/inputs/range.js +4 -2
- package/dist/src/components/inputs/shared/colorPickerPopup.d.ts +7 -0
- package/dist/src/components/inputs/shared/colorPickerPopup.js +185 -0
- package/dist/src/components/inputs/shared/index.d.ts +1 -0
- package/dist/src/components/inputs/shared/index.js +1 -0
- package/dist/src/components/inputs/switch.d.ts +4 -9
- package/dist/src/components/inputs/switch.js +4 -3
- package/dist/src/components/inputs/textarea.d.ts +4 -11
- package/dist/src/components/inputs/textarea.js +5 -3
- package/dist/src/globals.css +148 -0
- package/package.json +1 -1
- package/src/components/inputs/checkbox.tsx +9 -25
- package/src/components/inputs/input.tsx +47 -38
- package/src/components/inputs/radio.tsx +10 -28
- package/src/components/inputs/range.tsx +8 -29
- package/src/components/inputs/shared/colorPickerPopup.tsx +240 -0
- package/src/components/inputs/shared/index.ts +1 -0
- package/src/components/inputs/switch.tsx +10 -27
- package/src/components/inputs/textarea.tsx +11 -32
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
export type CheckboxProps = {
|
|
3
|
-
label?: string;
|
|
1
|
+
export type CheckboxProps = Omit<React.ComponentProps<'input'>, 'name'> & {
|
|
4
2
|
name: string;
|
|
5
|
-
|
|
6
|
-
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
7
|
-
className?: string;
|
|
8
|
-
disabled?: boolean;
|
|
3
|
+
label?: string;
|
|
9
4
|
error?: string;
|
|
10
5
|
info?: string;
|
|
11
|
-
|
|
6
|
+
className?: string;
|
|
12
7
|
};
|
|
13
|
-
export default function Checkbox(
|
|
8
|
+
export default function Checkbox(props: CheckboxProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Check } from 'lucide-react';
|
|
3
3
|
import { SelectionWrapper } from './shared';
|
|
4
|
-
export default function Checkbox(
|
|
5
|
-
|
|
4
|
+
export default function Checkbox(props) {
|
|
5
|
+
const { name, label, error, info, className, ...inputProps } = props;
|
|
6
|
+
return (_jsx(SelectionWrapper, { label: label, name: name, required: inputProps.required, info: info, error: error, className: className, disabled: inputProps.disabled, children: _jsxs("div", { className: 'relative flex items-center', children: [_jsx("input", { ...inputProps, id: name, name: name, type: 'checkbox', className: `
|
|
6
7
|
peer appearance-none h-5 w-5 rounded border border-login-500 bg-login-500/50
|
|
7
8
|
checked:bg-login checked:border-login
|
|
8
9
|
focus:outline-none focus:ring-2 focus:ring-login/50
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export type InputProps = {
|
|
3
|
-
label?: string;
|
|
1
|
+
import { type JSX } from 'react';
|
|
2
|
+
export type InputProps = Omit<React.ComponentProps<'input'>, 'name'> & {
|
|
4
3
|
name: string;
|
|
5
|
-
|
|
6
|
-
placeholder?: string;
|
|
7
|
-
value?: string | number;
|
|
8
|
-
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
4
|
+
label?: string;
|
|
9
5
|
error?: string;
|
|
10
6
|
className?: string;
|
|
11
|
-
disabled?: boolean;
|
|
12
|
-
required?: boolean;
|
|
13
7
|
icon?: JSX.Element;
|
|
14
8
|
info?: string;
|
|
15
|
-
multiple?: boolean;
|
|
16
9
|
};
|
|
17
|
-
export default function Input(
|
|
10
|
+
export default function Input(props: InputProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -3,21 +3,27 @@ import { useRef, useState } from 'react';
|
|
|
3
3
|
import { Calendar, Clock } from 'lucide-react';
|
|
4
4
|
import { FieldWrapper } from './shared';
|
|
5
5
|
import DateTimePickerPopup from './shared/dateTimePickerPopup';
|
|
6
|
+
import ColorPickerPopup from './shared/colorPickerPopup';
|
|
6
7
|
import useClickOutside from '../../hooks/useClickOutside';
|
|
7
|
-
export default function Input(
|
|
8
|
+
export default function Input(props) {
|
|
9
|
+
const { name, label, error, className, icon, info, ...inputProps } = props;
|
|
10
|
+
const { type = 'text', value } = inputProps;
|
|
8
11
|
const localRef = useRef(null);
|
|
9
12
|
const [isOpen, setIsOpen] = useState(false);
|
|
10
13
|
const containerRef = useClickOutside(() => setIsOpen(false));
|
|
11
14
|
const isDateType = ['date', 'datetime-local', 'time'].includes(type);
|
|
15
|
+
const isColorType = type === 'color';
|
|
16
|
+
const isClickableType = isDateType || isColorType;
|
|
12
17
|
function handleIconClick() {
|
|
13
|
-
if (
|
|
18
|
+
if (isClickableType && !inputProps.disabled) {
|
|
14
19
|
setIsOpen(!isOpen);
|
|
15
20
|
}
|
|
16
|
-
else if (localRef.current && !disabled) {
|
|
21
|
+
else if (localRef.current && !inputProps.disabled) {
|
|
17
22
|
localRef.current.focus();
|
|
18
23
|
}
|
|
19
24
|
}
|
|
20
25
|
function handleDateChange(date) {
|
|
26
|
+
const onChange = inputProps.onChange;
|
|
21
27
|
if (!onChange)
|
|
22
28
|
return;
|
|
23
29
|
const pad = (n) => n.toString().padStart(2, '0');
|
|
@@ -42,6 +48,19 @@ export default function Input({ label, name, type = 'text', placeholder, value,
|
|
|
42
48
|
};
|
|
43
49
|
onChange(event);
|
|
44
50
|
}
|
|
51
|
+
function handleColorChange(color) {
|
|
52
|
+
const onChange = inputProps.onChange;
|
|
53
|
+
if (!onChange)
|
|
54
|
+
return;
|
|
55
|
+
const event = {
|
|
56
|
+
target: {
|
|
57
|
+
name,
|
|
58
|
+
value: color,
|
|
59
|
+
type,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
onChange(event);
|
|
63
|
+
}
|
|
45
64
|
let displayIcon = icon;
|
|
46
65
|
if (!displayIcon && isDateType) {
|
|
47
66
|
if (type === 'time') {
|
|
@@ -51,6 +70,9 @@ export default function Input({ label, name, type = 'text', placeholder, value,
|
|
|
51
70
|
displayIcon = _jsx(Calendar, { className: 'w-4 h-4' });
|
|
52
71
|
}
|
|
53
72
|
}
|
|
73
|
+
else if (!displayIcon && isColorType) {
|
|
74
|
+
displayIcon = (_jsx("div", { className: 'w-4 h-4 rounded border border-login-200', style: { backgroundColor: value || '#000000' } }));
|
|
75
|
+
}
|
|
54
76
|
function getDateValue() {
|
|
55
77
|
if (!value)
|
|
56
78
|
return null;
|
|
@@ -61,10 +83,10 @@ export default function Input({ label, name, type = 'text', placeholder, value,
|
|
|
61
83
|
const date = new Date(value);
|
|
62
84
|
return isNaN(date.getTime()) ? null : date;
|
|
63
85
|
}
|
|
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: `
|
|
86
|
+
return (_jsx(FieldWrapper, { label: label, name: name, required: inputProps.required, info: info, error: error, className: className, children: _jsxs("div", { className: 'relative flex items-center', ref: containerRef, children: [displayIcon && (_jsx("div", { className: `
|
|
65
87
|
absolute left-3 text-login-200
|
|
66
|
-
${
|
|
67
|
-
`, onClick: handleIconClick, children: displayIcon })), _jsx("input", { ref: localRef, id: name, name: name, type:
|
|
88
|
+
${isClickableType && !inputProps.disabled ? 'cursor-pointer hover:text-login-text' : 'pointer-events-none'}
|
|
89
|
+
`, onClick: handleIconClick, children: displayIcon })), _jsx("input", { ...inputProps, ref: localRef, id: name, name: name, type: isClickableType ? 'text' : type, value: value, readOnly: isClickableType, onClick: () => isClickableType && !inputProps.disabled && setIsOpen(true), title: label, "aria-invalid": !!error, "aria-describedby": error ? `${name}-error` : undefined, className: `
|
|
68
90
|
w-full rounded-md bg-login-500/50 border border-login-500
|
|
69
91
|
text-login-text placeholder-login-200
|
|
70
92
|
focus:outline-none focus:border-login focus:ring-1 focus:ring-login
|
|
@@ -73,6 +95,6 @@ export default function Input({ label, name, type = 'text', placeholder, value,
|
|
|
73
95
|
transition-all duration-200
|
|
74
96
|
input-reset
|
|
75
97
|
${error ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : ''}
|
|
76
|
-
${
|
|
77
|
-
` }), isOpen && isDateType && !disabled && (_jsx(DateTimePickerPopup, { value: getDateValue(), onChange: handleDateChange, type: type, onClose: () => setIsOpen(false) }))] }) }));
|
|
98
|
+
${isClickableType && !inputProps.disabled ? 'cursor-pointer' : ''}
|
|
99
|
+
` }), isOpen && isDateType && !inputProps.disabled && (_jsx(DateTimePickerPopup, { value: getDateValue(), onChange: handleDateChange, type: type, onClose: () => setIsOpen(false) })), isOpen && isColorType && !inputProps.disabled && (_jsx(ColorPickerPopup, { value: value || '', onChange: handleColorChange, onClose: () => setIsOpen(false) }))] }) }));
|
|
78
100
|
}
|
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
export type RadioProps = {
|
|
3
|
-
label?: string;
|
|
1
|
+
export type RadioProps = Omit<React.ComponentProps<'input'>, 'name'> & {
|
|
4
2
|
name: string;
|
|
5
|
-
|
|
6
|
-
checked?: boolean;
|
|
7
|
-
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
8
|
-
className?: string;
|
|
9
|
-
disabled?: boolean;
|
|
3
|
+
label?: string;
|
|
10
4
|
error?: string;
|
|
11
5
|
info?: string;
|
|
12
|
-
|
|
6
|
+
className?: string;
|
|
13
7
|
};
|
|
14
|
-
export default function Radio(
|
|
8
|
+
export default function Radio(props: RadioProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { SelectionWrapper } from './shared';
|
|
3
|
-
export default function Radio(
|
|
3
|
+
export default function Radio(props) {
|
|
4
|
+
const { name, label, error, info, className, ...inputProps } = props;
|
|
5
|
+
const { value } = inputProps;
|
|
4
6
|
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',
|
|
7
|
+
return (_jsx(SelectionWrapper, { label: label, name: id, required: inputProps.required, info: info, error: error, className: className, disabled: inputProps.disabled, children: _jsxs("div", { className: 'relative flex items-center', children: [_jsx("input", { ...inputProps, id: id, name: name, type: 'radio', className: `
|
|
6
8
|
peer appearance-none h-5 w-5 rounded-full border border-login-500 bg-login-500/50
|
|
7
9
|
checked:bg-login checked:border-login
|
|
8
10
|
focus:outline-none focus:ring-2 focus:ring-login/50
|
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
export type RangeProps = {
|
|
3
|
-
label?: string;
|
|
1
|
+
export type RangeProps = Omit<React.ComponentProps<'input'>, 'name'> & {
|
|
4
2
|
name: string;
|
|
5
|
-
|
|
6
|
-
max?: number;
|
|
7
|
-
step?: number;
|
|
8
|
-
value?: number;
|
|
9
|
-
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
3
|
+
label?: string;
|
|
10
4
|
error?: string;
|
|
11
5
|
className?: string;
|
|
12
|
-
disabled?: boolean;
|
|
13
|
-
required?: boolean;
|
|
14
6
|
info?: string;
|
|
15
7
|
showValue?: boolean;
|
|
16
8
|
};
|
|
17
|
-
export default function Range(
|
|
9
|
+
export default function Range(props: RangeProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { FieldWrapper } from './shared';
|
|
3
|
-
export default function Range(
|
|
4
|
-
|
|
3
|
+
export default function Range(props) {
|
|
4
|
+
const { name, label, error, className, info, showValue = true, ...inputProps } = props;
|
|
5
|
+
const { min = 0, max = 100, step = 1, value = 0 } = inputProps;
|
|
6
|
+
return (_jsx(FieldWrapper, { label: label, name: name, required: inputProps.required, info: info, error: error, className: className, children: _jsxs("div", { className: 'flex items-center gap-4', children: [_jsx("input", { ...inputProps, id: name, name: name, type: 'range', min: min, max: max, step: step, value: value, title: label, "aria-invalid": !!error, "aria-describedby": error ? `${name}-error` : undefined, className: `
|
|
5
7
|
flex-1 h-2 bg-login-500 rounded-lg appearance-none cursor-pointer
|
|
6
8
|
accent-login
|
|
7
9
|
[&::-webkit-slider-thumb]:appearance-none
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useRef } from 'react';
|
|
3
|
+
function hexToHsv(hex) {
|
|
4
|
+
hex = hex.replace('#', '');
|
|
5
|
+
let r = 0, g = 0, b = 0;
|
|
6
|
+
if (hex.length === 3) {
|
|
7
|
+
r = parseInt(hex[0] + hex[0], 16);
|
|
8
|
+
g = parseInt(hex[1] + hex[1], 16);
|
|
9
|
+
b = parseInt(hex[2] + hex[2], 16);
|
|
10
|
+
}
|
|
11
|
+
else if (hex.length === 6) {
|
|
12
|
+
r = parseInt(hex.substring(0, 2), 16);
|
|
13
|
+
g = parseInt(hex.substring(2, 4), 16);
|
|
14
|
+
b = parseInt(hex.substring(4, 6), 16);
|
|
15
|
+
}
|
|
16
|
+
r /= 255;
|
|
17
|
+
g /= 255;
|
|
18
|
+
b /= 255;
|
|
19
|
+
const max = Math.max(r, g, b);
|
|
20
|
+
const min = Math.min(r, g, b);
|
|
21
|
+
const d = max - min;
|
|
22
|
+
let h = 0;
|
|
23
|
+
const s = max === 0 ? 0 : d / max;
|
|
24
|
+
const v = max;
|
|
25
|
+
if (max !== min) {
|
|
26
|
+
switch (max) {
|
|
27
|
+
case r:
|
|
28
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
29
|
+
break;
|
|
30
|
+
case g:
|
|
31
|
+
h = (b - r) / d + 2;
|
|
32
|
+
break;
|
|
33
|
+
case b:
|
|
34
|
+
h = (r - g) / d + 4;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
h /= 6;
|
|
38
|
+
}
|
|
39
|
+
return { h: h * 360, s: s * 100, v: v * 100 };
|
|
40
|
+
}
|
|
41
|
+
function hsvToRgb(h, s, v) {
|
|
42
|
+
let r = 0, g = 0, b = 0;
|
|
43
|
+
const i = Math.floor(h * 6);
|
|
44
|
+
const f = h * 6 - i;
|
|
45
|
+
const p = v * (1 - s);
|
|
46
|
+
const q = v * (1 - f * s);
|
|
47
|
+
const t = v * (1 - (1 - f) * s);
|
|
48
|
+
switch (i % 6) {
|
|
49
|
+
case 0:
|
|
50
|
+
r = v;
|
|
51
|
+
g = t;
|
|
52
|
+
b = p;
|
|
53
|
+
break;
|
|
54
|
+
case 1:
|
|
55
|
+
r = q;
|
|
56
|
+
g = v;
|
|
57
|
+
b = p;
|
|
58
|
+
break;
|
|
59
|
+
case 2:
|
|
60
|
+
r = p;
|
|
61
|
+
g = v;
|
|
62
|
+
b = t;
|
|
63
|
+
break;
|
|
64
|
+
case 3:
|
|
65
|
+
r = p;
|
|
66
|
+
g = q;
|
|
67
|
+
b = v;
|
|
68
|
+
break;
|
|
69
|
+
case 4:
|
|
70
|
+
r = t;
|
|
71
|
+
g = p;
|
|
72
|
+
b = v;
|
|
73
|
+
break;
|
|
74
|
+
case 5:
|
|
75
|
+
r = v;
|
|
76
|
+
g = p;
|
|
77
|
+
b = q;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255) };
|
|
81
|
+
}
|
|
82
|
+
function hsvToHex(h, s, v) {
|
|
83
|
+
const { r, g, b } = hsvToRgb(h / 360, s / 100, v / 100);
|
|
84
|
+
function toHex(x) {
|
|
85
|
+
const hex = x.toString(16);
|
|
86
|
+
return hex.length === 1 ? '0' + hex : hex;
|
|
87
|
+
}
|
|
88
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
89
|
+
}
|
|
90
|
+
const PRESET_COLORS = [
|
|
91
|
+
'#f87171', '#fb923c', '#fbbf24', '#facc15', '#a3e635', '#4ade80', '#34d399', '#2dd4bf',
|
|
92
|
+
'#38bdf8', '#60a5fa', '#818cf8', '#a78bfa', '#c084fc', '#e879f9', '#f472b6', '#fb7185'
|
|
93
|
+
];
|
|
94
|
+
function SaturationPicker({ hsv, onChange }) {
|
|
95
|
+
const containerRef = useRef(null);
|
|
96
|
+
function handleMove(e) {
|
|
97
|
+
if (!containerRef.current)
|
|
98
|
+
return;
|
|
99
|
+
const { left, top, width, height } = containerRef.current.getBoundingClientRect();
|
|
100
|
+
const x = Math.min(Math.max((e.clientX - left) / width, 0), 1);
|
|
101
|
+
const y = Math.min(Math.max((e.clientY - top) / height, 0), 1);
|
|
102
|
+
onChange(x * 100, (1 - y) * 100);
|
|
103
|
+
}
|
|
104
|
+
function handleMouseDown(e) {
|
|
105
|
+
handleMove(e);
|
|
106
|
+
function moveHandler(e) { handleMove(e); }
|
|
107
|
+
function upHandler() {
|
|
108
|
+
window.removeEventListener('mousemove', moveHandler);
|
|
109
|
+
window.removeEventListener('mouseup', upHandler);
|
|
110
|
+
}
|
|
111
|
+
window.addEventListener('mousemove', moveHandler);
|
|
112
|
+
window.addEventListener('mouseup', upHandler);
|
|
113
|
+
}
|
|
114
|
+
const bgColor = hsvToHex(hsv.h, 100, 100);
|
|
115
|
+
return (_jsxs("div", { ref: containerRef, className: 'w-full h-32 relative rounded-md overflow-hidden cursor-crosshair mb-3 select-none', style: { backgroundColor: bgColor }, onMouseDown: handleMouseDown, children: [_jsx("div", { className: 'absolute inset-0 bg-linear-to-r from-white to-transparent' }), _jsx("div", { className: 'absolute inset-0 bg-linear-to-t from-black to-transparent' }), _jsx("div", { className: `
|
|
116
|
+
absolute w-3 h-3 border-2 border-white rounded-full
|
|
117
|
+
shadow-md -translate-x-1/2 -translate-y-1/2 pointer-events-none
|
|
118
|
+
`, style: { left: `${hsv.s}%`, top: `${100 - hsv.v}%` } })] }));
|
|
119
|
+
}
|
|
120
|
+
function HuePicker({ hue, onChange }) {
|
|
121
|
+
const containerRef = useRef(null);
|
|
122
|
+
function handleMove(e) {
|
|
123
|
+
if (!containerRef.current)
|
|
124
|
+
return;
|
|
125
|
+
const { left, width } = containerRef.current.getBoundingClientRect();
|
|
126
|
+
const x = Math.min(Math.max((e.clientX - left) / width, 0), 1);
|
|
127
|
+
onChange(x * 360);
|
|
128
|
+
}
|
|
129
|
+
function handleMouseDown(e) {
|
|
130
|
+
handleMove(e);
|
|
131
|
+
function moveHandler(e) { handleMove(e); }
|
|
132
|
+
function upHandler() {
|
|
133
|
+
window.removeEventListener('mousemove', moveHandler);
|
|
134
|
+
window.removeEventListener('mouseup', upHandler);
|
|
135
|
+
}
|
|
136
|
+
window.addEventListener('mousemove', moveHandler);
|
|
137
|
+
window.addEventListener('mouseup', upHandler);
|
|
138
|
+
}
|
|
139
|
+
return (_jsx("div", { ref: containerRef, className: 'w-full h-3 relative rounded-full cursor-pointer mb-4 select-none', style: { background: 'linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%)' }, onMouseDown: handleMouseDown, children: _jsx("div", { className: `
|
|
140
|
+
absolute w-4 h-4 bg-white border border-gray-200
|
|
141
|
+
rounded-full shadow-sm -translate-x-1/2 -translate-y-1/2
|
|
142
|
+
top-1/2 pointer-events-none
|
|
143
|
+
`, style: { left: `${(hue / 360) * 100}%` } }) }));
|
|
144
|
+
}
|
|
145
|
+
export default function ColorPickerPopup({ value, onChange, onClose }) {
|
|
146
|
+
const [hsv, setHsv] = useState(() => hexToHsv(value || '#000000'));
|
|
147
|
+
const [hexInput, setHexInput] = useState(value || '#000000');
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
if (value && value !== hexInput) {
|
|
150
|
+
setHsv(hexToHsv(value));
|
|
151
|
+
setHexInput(value);
|
|
152
|
+
}
|
|
153
|
+
}, [value]);
|
|
154
|
+
function handleColorChange(newHsv) {
|
|
155
|
+
setHsv(newHsv);
|
|
156
|
+
const hex = hsvToHex(newHsv.h, newHsv.s, newHsv.v);
|
|
157
|
+
setHexInput(hex);
|
|
158
|
+
onChange(hex);
|
|
159
|
+
}
|
|
160
|
+
function handleSaturationChange(s, v) { handleColorChange({ ...hsv, s, v }); }
|
|
161
|
+
function handleHueChange(h) { handleColorChange({ ...hsv, h }); }
|
|
162
|
+
function manualHexChange(e) {
|
|
163
|
+
const val = e.target.value;
|
|
164
|
+
setHexInput(val);
|
|
165
|
+
if (/^#[0-9A-F]{6}$/i.test(val)) {
|
|
166
|
+
const newHsv = hexToHsv(val);
|
|
167
|
+
setHsv(newHsv);
|
|
168
|
+
onChange(val);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return (_jsxs("div", { className: 'absolute top-full left-0 mt-1 z-50 bg-login-600 border border-login-500 rounded-md shadow-lg p-3 w-64 select-none', children: [_jsx(SaturationPicker, { hsv: hsv, onChange: handleSaturationChange }), _jsx(HuePicker, { hue: hsv.h, onChange: handleHueChange }), _jsxs("div", { className: 'flex items-center gap-2 mb-3', children: [_jsx("div", { className: 'text-xs text-login-200 font-mono', children: "HEX" }), _jsx("input", { type: 'text', value: hexInput, onChange: manualHexChange, className: `
|
|
172
|
+
flex-1 min-w-0 bg-login-500 border border-login-500 rounded
|
|
173
|
+
px-2 py-1 text-sm text-login-text focus:outline-none
|
|
174
|
+
focus:border-login focus:ring-1 focus:ring-login
|
|
175
|
+
`, spellCheck: false }), _jsx("div", { className: 'w-8 h-8 rounded border border-login-500 shrink-0', style: { backgroundColor: hexInput } })] }), _jsx("div", { className: 'grid grid-cols-8 gap-1.5 pt-3 border-t border-login-500', children: PRESET_COLORS.map(color => (_jsx("button", { type: 'button', className: `
|
|
176
|
+
w-6 h-6 rounded-sm cursor-pointer hover:scale-110
|
|
177
|
+
hover:zIndex-10 transition-transform ring-1 ring-inset ring-black/10
|
|
178
|
+
`, style: { backgroundColor: color }, onClick: () => {
|
|
179
|
+
const newHsv = hexToHsv(color);
|
|
180
|
+
setHsv(newHsv);
|
|
181
|
+
setHexInput(color);
|
|
182
|
+
onChange(color);
|
|
183
|
+
onClose();
|
|
184
|
+
}, title: color }, color))) })] }));
|
|
185
|
+
}
|
|
@@ -3,3 +3,4 @@ export { default as SelectionWrapper } from './selectionWrapper';
|
|
|
3
3
|
export { default as InputLabel } from './inputLabel';
|
|
4
4
|
export { default as InputInfo } from './inputInfo';
|
|
5
5
|
export { default as InputError } from './inputError';
|
|
6
|
+
export { default as ColorPickerPopup } from './colorPickerPopup';
|
|
@@ -3,3 +3,4 @@ export { default as SelectionWrapper } from './selectionWrapper';
|
|
|
3
3
|
export { default as InputLabel } from './inputLabel';
|
|
4
4
|
export { default as InputInfo } from './inputInfo';
|
|
5
5
|
export { default as InputError } from './inputError';
|
|
6
|
+
export { default as ColorPickerPopup } from './colorPickerPopup';
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
export type SwitchProps = {
|
|
3
|
-
label?: string;
|
|
1
|
+
export type SwitchProps = Omit<React.ComponentProps<'input'>, 'name'> & {
|
|
4
2
|
name: string;
|
|
5
|
-
|
|
6
|
-
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
7
|
-
className?: string;
|
|
8
|
-
disabled?: boolean;
|
|
3
|
+
label?: string;
|
|
9
4
|
error?: string;
|
|
10
5
|
info?: string;
|
|
11
|
-
|
|
6
|
+
className?: string;
|
|
12
7
|
switchOnly?: boolean;
|
|
13
8
|
};
|
|
14
|
-
export default function Switch(
|
|
9
|
+
export default function Switch(props: SwitchProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { SelectionWrapper } from './shared';
|
|
3
|
-
export default function Switch(
|
|
4
|
-
|
|
3
|
+
export default function Switch(props) {
|
|
4
|
+
const { name, label, error, info, className, switchOnly, ...inputProps } = props;
|
|
5
|
+
return (_jsx(SelectionWrapper, { label: label, name: name, required: inputProps.required, info: info, error: error, hideError: switchOnly, className: className, disabled: inputProps.disabled, children: _jsxs("label", { className: `relative inline-flex items-center cursor-pointer ${switchOnly ? 'h-fit' : 'h-10.5'}`, children: [_jsx("input", { ...inputProps, type: 'checkbox', id: name, name: name, className: 'sr-only peer' }), _jsx("div", { className: `
|
|
5
6
|
w-11 h-6 bg-login-500/50 rounded-full peer
|
|
6
7
|
peer-checked:after:translate-x-full peer-checked:after:border-white
|
|
7
8
|
after:content-[''] after:absolute ${switchOnly ? 'after:top-0.5' : 'after:top-2.75'} after:left-0.5
|
|
8
9
|
after:bg-white after:border-gray-300 after:border after:rounded-full
|
|
9
10
|
after:h-5 after:w-5 after:transition-all peer-checked:bg-login
|
|
10
|
-
${disabled ? 'opacity-50 cursor-not-allowed' : ''}
|
|
11
|
+
${inputProps.disabled ? 'opacity-50 cursor-not-allowed' : ''}
|
|
11
12
|
${error ? 'ring-1 ring-red-500' : ''}
|
|
12
13
|
` })] }) }));
|
|
13
14
|
}
|
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
export type TextareaProps = {
|
|
3
|
-
label?: string;
|
|
1
|
+
export type TextareaProps = Omit<React.ComponentProps<'textarea'>, 'name'> & {
|
|
4
2
|
name: string;
|
|
5
|
-
|
|
6
|
-
type?: 'markdown' | 'json' | 'text';
|
|
7
|
-
value?: string;
|
|
8
|
-
onChange?: (e: ChangeEvent<HTMLTextAreaElement>) => void;
|
|
3
|
+
label?: string;
|
|
9
4
|
error?: string;
|
|
10
5
|
className?: string;
|
|
11
|
-
disabled?: boolean;
|
|
12
|
-
required?: boolean;
|
|
13
|
-
rows?: number;
|
|
14
6
|
info?: string;
|
|
7
|
+
type?: 'markdown' | 'json' | 'text';
|
|
15
8
|
};
|
|
16
|
-
export default function Textarea(
|
|
9
|
+
export default function Textarea(props: TextareaProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -12,17 +12,19 @@ function isValidJson(str) {
|
|
|
12
12
|
return error.message;
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
export default function Textarea(
|
|
15
|
+
export default function Textarea(props) {
|
|
16
|
+
const { name, label, error, className, info, type = 'text', rows = 4, ...textareaProps } = props;
|
|
17
|
+
const { value } = textareaProps;
|
|
16
18
|
const [preview, setPreview] = useState(false);
|
|
17
19
|
const jsonError = type === 'json' && value ? isValidJson(value) : undefined;
|
|
18
20
|
const displayError = jsonError || error;
|
|
19
|
-
return (_jsx(FieldWrapper, { label: label, name: name, required: required, info: info, error: displayError, className: className, children: _jsxs("div", { className: 'relative', children: [type === 'markdown' && (_jsx("div", { className: 'absolute right-2 top-2 z-10 flex gap-2', children: _jsx("button", { type: 'button', onClick: () => setPreview(!preview), className: 'p-1 rounded hover:bg-login-500/50 text-login-text transition-colors', title: preview ? 'Edit' : 'Preview', children: preview ? _jsx(Pencil, { size: 16 }) : _jsx(Eye, { size: 16 }) }) })), type === 'markdown' && preview ? (_jsx("div", { className: `
|
|
21
|
+
return (_jsx(FieldWrapper, { label: label, name: name, required: textareaProps.required, info: info, error: displayError, className: className, children: _jsxs("div", { className: 'relative', children: [type === 'markdown' && (_jsx("div", { className: 'absolute right-2 top-2 z-10 flex gap-2', children: _jsx("button", { type: 'button', onClick: () => setPreview(!preview), className: 'p-1 rounded hover:bg-login-500/50 text-login-text transition-colors', title: preview ? 'Edit' : 'Preview', children: preview ? _jsx(Pencil, { size: 16 }) : _jsx(Eye, { size: 16 }) }) })), type === 'markdown' && preview ? (_jsx("div", { className: `
|
|
20
22
|
w-full rounded-md bg-login-500/50 border border-login-500
|
|
21
23
|
text-login-text
|
|
22
24
|
p-3
|
|
23
25
|
prose prose-invert prose-sm max-w-none overflow-y-auto
|
|
24
26
|
${error ? 'border-red-500' : ''}
|
|
25
|
-
`, style: { minHeight: `${rows * 1.5}rem` }, children: _jsx(ReactMarkdown, { children: value || '' }) })) : (_jsx("textarea", { id: name, name: name,
|
|
27
|
+
`, style: { minHeight: `${rows * 1.5}rem` }, children: _jsx(ReactMarkdown, { children: String(value || '') }) })) : (_jsx("textarea", { ...textareaProps, id: name, name: name, rows: rows, title: label, "aria-invalid": !!error, "aria-describedby": error ? `${name}-error` : undefined, className: `
|
|
26
28
|
w-full rounded-md bg-login-500/50 border border-login-500
|
|
27
29
|
text-login-text placeholder-login-200
|
|
28
30
|
focus:outline-none focus:border-login focus:ring-1 focus:ring-login
|