@react-fabric/form 1.0.0-beta.8 → 1.0.1
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/{src/input/ArrayInput.module.css → css/_arrayInput.css} +8 -8
- package/css/_colorPicker.css +95 -0
- package/{src/internal/Field.module.css → css/_field.css} +94 -33
- package/{src/input/File.module.css → css/_file.css} +21 -6
- package/css/styles.css +27 -0
- package/dist/cjs/builder/FileField.js +107 -78
- package/dist/cjs/builder/FormField.js +60 -39
- package/dist/cjs/builder/SchemaEditor.js +72 -0
- package/dist/cjs/builder/components/AddSchemaFields.js +64 -0
- package/dist/cjs/builder/components/SchemaArrayForm.js +146 -0
- package/dist/cjs/builder/components/SchemaField.js +423 -0
- package/dist/cjs/form/Controller.js +66 -19
- package/dist/cjs/form/Form.js +97 -39
- package/dist/cjs/form/useForm.js +32 -0
- package/dist/cjs/hooks/useFileUploader.js +74 -45
- package/dist/cjs/hooks/useFormBuilder.js +85 -31
- package/dist/cjs/i18n/{ar.json.js → ar/form.json.js} +5 -4
- package/dist/cjs/i18n/{en.json.js → en/form.json.js} +5 -5
- package/dist/cjs/i18n/index.js +4 -4
- package/dist/cjs/index.js +41 -2
- package/dist/cjs/input/ArrayInput.js +259 -135
- package/dist/cjs/input/AvatarInput.js +99 -63
- package/dist/cjs/input/Checkbox.js +24 -25
- package/dist/cjs/input/ColorInput.js +138 -81
- package/dist/cjs/input/ColorPicker.js +78 -95
- package/dist/cjs/input/DateInput.js +192 -0
- package/dist/cjs/input/Field.js +21 -20
- package/dist/cjs/input/File.js +85 -80
- package/dist/cjs/input/Hidden.js +27 -13
- package/dist/cjs/input/Input.js +49 -48
- package/dist/cjs/input/Number.js +46 -44
- package/dist/cjs/input/Password.js +51 -49
- package/dist/cjs/input/Radio.js +26 -16
- package/dist/cjs/input/RadioGroup.js +26 -11
- package/dist/cjs/input/{Range.js → RangeSlider.js} +61 -66
- package/dist/cjs/input/Search.js +58 -64
- package/dist/cjs/input/Slider.js +57 -66
- package/dist/cjs/input/Switch.js +29 -27
- package/dist/cjs/input/Textarea.js +65 -53
- package/dist/cjs/internal/ErrorIcon.js +9 -9
- package/dist/cjs/internal/FieldWrapper.js +23 -13
- package/dist/cjs/internal/InputWrapper.js +85 -87
- package/dist/cjs/ranger/index.js +7 -7
- package/dist/cjs/ranger/utils.js +9 -4
- package/dist/cjs/select/AutoComplete.js +93 -76
- package/dist/cjs/select/DualList.js +235 -154
- package/dist/cjs/select/List.js +122 -89
- package/dist/cjs/select/Options.js +58 -54
- package/dist/cjs/select/Select.js +119 -89
- package/dist/cjs/select/helpers.js +5 -5
- package/dist/cjs/select/useSelect.js +83 -58
- package/dist/cjs/types/schema.js +43 -0
- package/dist/cjs/utils/eventHandlers.js +16 -6
- package/dist/cjs/utils/getColor.js +6 -6
- package/dist/cjs/utils/getProps.js +49 -0
- package/dist/esm/builder/FileField.js +109 -80
- package/dist/esm/builder/FormField.js +60 -39
- package/dist/esm/builder/SchemaEditor.js +72 -0
- package/dist/esm/builder/components/AddSchemaFields.js +64 -0
- package/dist/esm/builder/components/SchemaArrayForm.js +146 -0
- package/dist/esm/builder/components/SchemaField.js +423 -0
- package/dist/esm/form/Controller.js +66 -19
- package/dist/esm/form/Form.js +97 -39
- package/dist/esm/form/useForm.js +32 -0
- package/dist/esm/hooks/useFileUploader.js +75 -46
- package/dist/esm/hooks/useFormBuilder.js +84 -30
- package/dist/esm/i18n/{ar.json.js → ar/form.json.js} +5 -4
- package/dist/esm/i18n/{en.json.js → en/form.json.js} +5 -5
- package/dist/esm/i18n/index.js +2 -2
- package/dist/esm/index.js +36 -1
- package/dist/esm/input/ArrayInput.js +260 -136
- package/dist/esm/input/AvatarInput.js +99 -63
- package/dist/esm/input/Checkbox.js +26 -27
- package/dist/esm/input/ColorInput.js +141 -84
- package/dist/esm/input/ColorPicker.js +80 -97
- package/dist/esm/input/DateInput.js +192 -0
- package/dist/esm/input/Field.js +21 -20
- package/dist/esm/input/File.js +89 -84
- package/dist/esm/input/Hidden.js +28 -14
- package/dist/esm/input/Input.js +51 -50
- package/dist/esm/input/Number.js +48 -46
- package/dist/esm/input/Password.js +54 -52
- package/dist/esm/input/Radio.js +27 -17
- package/dist/esm/input/RadioGroup.js +27 -12
- package/dist/esm/input/{Range.js → RangeSlider.js} +62 -67
- package/dist/esm/input/Search.js +61 -67
- package/dist/esm/input/Slider.js +58 -67
- package/dist/esm/input/Switch.js +31 -29
- package/dist/esm/input/Textarea.js +67 -55
- package/dist/esm/internal/ErrorIcon.js +11 -11
- package/dist/esm/internal/FieldWrapper.js +23 -13
- package/dist/esm/internal/InputWrapper.js +88 -90
- package/dist/esm/ranger/index.js +7 -7
- package/dist/esm/ranger/utils.js +9 -4
- package/dist/esm/select/AutoComplete.js +95 -78
- package/dist/esm/select/DualList.js +233 -152
- package/dist/esm/select/List.js +125 -92
- package/dist/esm/select/Options.js +58 -54
- package/dist/esm/select/Select.js +122 -92
- package/dist/esm/select/helpers.js +5 -5
- package/dist/esm/select/useSelect.js +84 -59
- package/{src/input/ColorPicker.module.css → dist/esm/types/schema.js} +18 -27
- package/dist/esm/utils/eventHandlers.js +16 -6
- package/dist/esm/utils/getColor.js +6 -6
- package/dist/esm/utils/getProps.js +49 -0
- package/dist/types/builder/FileField.d.ts +7 -4
- package/dist/types/builder/FormField.d.ts +4 -2
- package/dist/types/builder/SchemaEditor.d.ts +6 -0
- package/dist/types/builder/components/AddSchemaFields.d.ts +2 -0
- package/dist/types/builder/components/SchemaArrayForm.d.ts +2 -0
- package/dist/types/builder/components/SchemaField.d.ts +12 -0
- package/dist/types/form/Controller.d.ts +45 -3
- package/dist/types/form/Form.d.ts +38 -7
- package/dist/types/form/useForm.d.ts +6 -0
- package/dist/types/hooks/useFileUploader.d.ts +24 -3
- package/dist/types/hooks/useFormBuilder.d.ts +41 -3
- package/dist/types/index.d.ts +32 -2
- package/dist/types/input/ArrayInput.d.ts +68 -13
- package/dist/types/input/AvatarInput.d.ts +23 -6
- package/dist/types/input/Checkbox.d.ts +12 -10
- package/dist/types/input/ColorInput.d.ts +15 -5
- package/dist/types/input/ColorPicker.d.ts +10 -2
- package/dist/types/input/DateInput.d.ts +21 -0
- package/dist/types/input/Field.d.ts +9 -36
- package/dist/types/input/File.d.ts +16 -4
- package/dist/types/input/Hidden.d.ts +7 -6
- package/dist/types/input/Input.d.ts +11 -4
- package/dist/types/input/Number.d.ts +9 -4
- package/dist/types/input/Password.d.ts +9 -4
- package/dist/types/input/Radio.d.ts +9 -4
- package/dist/types/input/RadioGroup.d.ts +14 -6
- package/dist/types/input/{Range.d.ts → RangeSlider.d.ts} +11 -6
- package/dist/types/input/Search.d.ts +14 -4
- package/dist/types/input/Slider.d.ts +9 -6
- package/dist/types/input/Switch.d.ts +13 -8
- package/dist/types/input/Textarea.d.ts +15 -4
- package/dist/types/internal/ErrorIcon.d.ts +4 -1
- package/dist/types/internal/FieldWrapper.d.ts +10 -1
- package/dist/types/internal/InputWrapper.d.ts +19 -1
- package/dist/types/ranger/index.d.ts +7 -3
- package/dist/types/ranger/utils.d.ts +2 -2
- package/dist/types/select/AutoComplete.d.ts +7 -2
- package/dist/types/select/DualList.d.ts +11 -37
- package/dist/types/select/List.d.ts +7 -2
- package/dist/types/select/Options.d.ts +8 -7
- package/dist/types/select/Select.d.ts +7 -2
- package/dist/types/select/helpers.d.ts +2 -2
- package/dist/types/select/useSelect.d.ts +10 -28
- package/dist/types/types/index.d.ts +73 -39
- package/dist/types/types/schema.d.ts +78 -32
- package/dist/types/utils/eventHandlers.d.ts +4 -4
- package/dist/types/utils/getColor.d.ts +4 -4
- package/dist/types/utils/getProps.d.ts +26 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/package.json +25 -25
- package/src/builder/FileField.tsx +73 -56
- package/src/builder/FormField.tsx +66 -58
- package/src/builder/SchemaEditor.tsx +78 -0
- package/src/builder/components/AddSchemaFields.tsx +64 -0
- package/src/builder/components/SchemaArrayForm.tsx +146 -0
- package/src/builder/components/SchemaField.tsx +405 -0
- package/src/form/Controller.tsx +61 -24
- package/src/form/Form.tsx +74 -50
- package/src/form/useForm.ts +53 -0
- package/src/hooks/useFileUploader.ts +115 -84
- package/src/hooks/useFormBuilder.tsx +80 -41
- package/src/i18n/{ar.json → ar/form.json} +5 -4
- package/src/i18n/{en.json → en/form.json} +5 -5
- package/src/i18n/index.ts +2 -2
- package/src/index.ts +34 -20
- package/src/input/ArrayInput.tsx +255 -173
- package/src/input/AvatarInput.tsx +82 -39
- package/src/input/Checkbox.tsx +30 -54
- package/src/input/ColorInput.tsx +146 -105
- package/src/input/ColorPicker.tsx +76 -102
- package/src/input/DateInput.tsx +225 -0
- package/src/input/Field.tsx +23 -65
- package/src/input/File.tsx +76 -98
- package/src/input/Hidden.tsx +36 -24
- package/src/input/Input.tsx +55 -53
- package/src/input/Number.tsx +49 -54
- package/src/input/Password.tsx +61 -66
- package/src/input/Radio.tsx +28 -37
- package/src/input/RadioGroup.tsx +30 -25
- package/src/input/{Range.tsx → RangeSlider.tsx} +81 -109
- package/src/input/Search.tsx +64 -77
- package/src/input/Slider.tsx +80 -109
- package/src/input/Switch.tsx +39 -53
- package/src/input/Textarea.tsx +72 -59
- package/src/internal/ErrorIcon.tsx +22 -21
- package/src/internal/FieldWrapper.tsx +33 -23
- package/src/internal/InputWrapper.tsx +132 -143
- package/src/ranger/index.tsx +37 -117
- package/src/ranger/utils.ts +12 -11
- package/src/select/AutoComplete.tsx +113 -123
- package/src/select/DualList.tsx +218 -204
- package/src/select/List.tsx +130 -97
- package/src/select/Options.tsx +59 -57
- package/src/select/Select.tsx +148 -122
- package/src/select/helpers.ts +13 -8
- package/src/select/useSelect.tsx +110 -127
- package/src/types/index.ts +76 -45
- package/src/types/schema.ts +97 -40
- package/src/utils/eventHandlers.ts +24 -26
- package/src/utils/getColor.ts +9 -8
- package/src/utils/getProps.ts +47 -0
- package/src/utils/index.ts +1 -0
- package/dist/cjs/input/ArrayInput.module.css +0 -1
- package/dist/cjs/input/ColorPicker.module.css +0 -1
- package/dist/cjs/input/File.module.css +0 -1
- package/dist/cjs/internal/Field.module.css +0 -1
- package/dist/esm/input/ArrayInput.module.css +0 -1
- package/dist/esm/input/ColorPicker.module.css +0 -1
- package/dist/esm/input/File.module.css +0 -1
- package/dist/esm/internal/Field.module.css +0 -1
package/src/input/Password.tsx
CHANGED
|
@@ -21,23 +21,17 @@
|
|
|
21
21
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { Button,
|
|
25
|
-
import {
|
|
26
|
-
import
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
useDeferredValue,
|
|
30
|
-
useEffect,
|
|
31
|
-
useMemo,
|
|
32
|
-
useState,
|
|
33
|
-
type ChangeEvent,
|
|
34
|
-
} from "react";
|
|
24
|
+
import { Button, Tooltip, useControlledValue } from "@react-fabric/core";
|
|
25
|
+
import type { RefProp } from "@react-fabric/core/dist/types/types";
|
|
26
|
+
import { cn } from "@react-fabric/utilities";
|
|
27
|
+
import type { InputHTMLAttributes } from "react";
|
|
28
|
+
import { useCallback, useId, useState } from "react";
|
|
35
29
|
import { useTranslation } from "react-i18next";
|
|
36
30
|
import { InputWrapper } from "../internal/InputWrapper";
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
31
|
+
import type { InputProps, WrapperProps } from "../types";
|
|
32
|
+
import { getWrapperProps, useHandleEnter } from "../utils";
|
|
39
33
|
|
|
40
|
-
export interface PasswordProps extends InputProps, RefProp<HTMLInputElement> {
|
|
34
|
+
export interface PasswordProps extends InputProps, WrapperProps, RefProp<HTMLInputElement> {
|
|
41
35
|
/**
|
|
42
36
|
* show password toggle
|
|
43
37
|
*/
|
|
@@ -46,69 +40,68 @@ export interface PasswordProps extends InputProps, RefProp<HTMLInputElement> {
|
|
|
46
40
|
* password strength meter (0-1)
|
|
47
41
|
*/
|
|
48
42
|
strength?: number;
|
|
43
|
+
|
|
44
|
+
autoComplete?: InputHTMLAttributes<HTMLInputElement>["autoComplete"];
|
|
49
45
|
}
|
|
50
46
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Password input component with optional strength meter and toggle visibility.
|
|
49
|
+
*/
|
|
50
|
+
export function Password(props: PasswordProps) {
|
|
51
|
+
const {
|
|
52
|
+
value,
|
|
53
|
+
defaultValue,
|
|
54
|
+
onEnterPressed,
|
|
55
|
+
onChange,
|
|
56
|
+
onBlur,
|
|
57
|
+
onFocus,
|
|
58
|
+
disabled,
|
|
59
|
+
readOnly,
|
|
60
|
+
invalid,
|
|
61
|
+
showToggle,
|
|
62
|
+
strength,
|
|
63
|
+
placeholder,
|
|
64
|
+
name,
|
|
65
|
+
autoFocus,
|
|
66
|
+
allowClear,
|
|
67
|
+
autoComplete = "off",
|
|
68
|
+
ref,
|
|
69
|
+
} = props;
|
|
69
70
|
|
|
70
|
-
const
|
|
71
|
-
const
|
|
71
|
+
const id = useId();
|
|
72
|
+
const { currentValue, updateValue } = useControlledValue(value, defaultValue);
|
|
72
73
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
[onEnterPressed],
|
|
76
|
-
);
|
|
74
|
+
const { t } = useTranslation("form");
|
|
75
|
+
const [show, setShow] = useState(false);
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
setActualValue(deferred ?? "");
|
|
80
|
-
}, [deferred]);
|
|
77
|
+
const handleEnterPressed = useHandleEnter(onEnterPressed);
|
|
81
78
|
|
|
82
79
|
const handleChange = useCallback(
|
|
83
|
-
(e?: ChangeEvent<HTMLInputElement>) => {
|
|
84
|
-
const value = e?.target.value;
|
|
85
|
-
|
|
86
|
-
onChange?.(value
|
|
80
|
+
(e?: React.ChangeEvent<HTMLInputElement>) => {
|
|
81
|
+
const value = e?.target.value ?? defaultValue;
|
|
82
|
+
updateValue(value);
|
|
83
|
+
onChange?.(value);
|
|
87
84
|
},
|
|
88
|
-
[onChange],
|
|
85
|
+
[defaultValue, updateValue, onChange],
|
|
89
86
|
);
|
|
90
87
|
|
|
91
88
|
return (
|
|
92
89
|
<InputWrapper
|
|
93
|
-
|
|
90
|
+
id={id}
|
|
91
|
+
showClear={allowClear && !!currentValue && !disabled && !readOnly}
|
|
94
92
|
onClear={handleChange}
|
|
95
93
|
invalid={invalid}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
showToggle && (
|
|
102
|
-
<Tooltip
|
|
103
|
-
color="warning"
|
|
104
|
-
content={t(show ? "password.hide" : "password.show")}
|
|
105
|
-
>
|
|
94
|
+
{...getWrapperProps(props)}
|
|
95
|
+
actionButton={
|
|
96
|
+
showToggle &&
|
|
97
|
+
currentValue && (
|
|
98
|
+
<Tooltip color="warning" content={t(show ? "password.hide" : "password.show")}>
|
|
106
99
|
<Button
|
|
107
100
|
tabIndex={-1}
|
|
108
101
|
color="warning"
|
|
109
102
|
variant="link"
|
|
110
103
|
aria-label={t(show ? "password.hide" : "password.show")}
|
|
111
|
-
icon={show ?
|
|
104
|
+
icon={show ? "icon-[mdi--eye-off-outline]" : "icon-[mdi--eye-outline]"}
|
|
112
105
|
onClick={() => setShow(!show)}
|
|
113
106
|
/>
|
|
114
107
|
</Tooltip>
|
|
@@ -116,7 +109,8 @@ export const Password = ({
|
|
|
116
109
|
}
|
|
117
110
|
>
|
|
118
111
|
<input
|
|
119
|
-
|
|
112
|
+
id={id}
|
|
113
|
+
className="appearance-none bg-transparent py-1 px-4 not-first:ps-2 not-last:pe-2 flex-1 border-none outline-none ring-0 peer"
|
|
120
114
|
aria-invalid={invalid}
|
|
121
115
|
aria-disabled={disabled}
|
|
122
116
|
aria-readonly={readOnly}
|
|
@@ -128,23 +122,24 @@ export const Password = ({
|
|
|
128
122
|
size={1}
|
|
129
123
|
type={show ? "text" : "password"}
|
|
130
124
|
ref={ref}
|
|
131
|
-
autoComplete=
|
|
125
|
+
autoComplete={autoComplete}
|
|
132
126
|
{...{ autoFocus }}
|
|
133
|
-
value={
|
|
127
|
+
value={currentValue ?? ""}
|
|
128
|
+
onBlur={onBlur}
|
|
129
|
+
onFocus={onFocus}
|
|
134
130
|
onChange={handleChange}
|
|
135
131
|
onKeyDown={handleEnterPressed}
|
|
136
132
|
/>
|
|
137
133
|
{!!strength && (
|
|
138
134
|
<div
|
|
139
|
-
className={
|
|
140
|
-
"absolute bottom-px h-[3px] inset-x-0 after:block after:h-full after:bg-dimmed",
|
|
141
|
-
"ltr:bg-gradient-to-r rtl:bg-gradient-to-l from-danger-500 via-marigold-500 to-jade-500",
|
|
142
|
-
)}
|
|
135
|
+
className={cn("absolute bottom-px h-0.5 inset-x-0 after:block after:h-full after:bg-dimmed")}
|
|
143
136
|
style={{
|
|
144
137
|
paddingInlineStart: `${strength > 1 ? 100 : strength * 100}%`,
|
|
138
|
+
background:
|
|
139
|
+
"linear-gradient(to right,var(--color-scarlet-500) 25%,var(--color-amber-500) 50%,var(--color-jade-500) 75%,var(--color-avocado-500) 100%)",
|
|
145
140
|
}}
|
|
146
141
|
/>
|
|
147
142
|
)}
|
|
148
143
|
</InputWrapper>
|
|
149
144
|
);
|
|
150
|
-
}
|
|
145
|
+
}
|
package/src/input/Radio.tsx
CHANGED
|
@@ -21,17 +21,18 @@
|
|
|
21
21
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import
|
|
27
|
-
import { useCallback, useMemo
|
|
24
|
+
import { Icon } from "@react-fabric/core";
|
|
25
|
+
import type { RefProp } from "@react-fabric/core/dist/types/types";
|
|
26
|
+
import { cn } from "@react-fabric/utilities";
|
|
27
|
+
import { useCallback, useMemo } from "react";
|
|
28
|
+
import { ErrorIcon } from "../internal/ErrorIcon";
|
|
28
29
|
|
|
29
30
|
export interface RadioProps extends RefProp<HTMLInputElement> {
|
|
30
31
|
name?: string;
|
|
31
32
|
/**
|
|
32
33
|
* radio label
|
|
33
34
|
*/
|
|
34
|
-
label?:
|
|
35
|
+
label?: React.ReactElement | string;
|
|
35
36
|
/**
|
|
36
37
|
* value
|
|
37
38
|
*/
|
|
@@ -69,49 +70,48 @@ export interface RadioProps extends RefProp<HTMLInputElement> {
|
|
|
69
70
|
iconChecked?: string;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Radio input component with custom icons and label.
|
|
75
|
+
*
|
|
76
|
+
* This component renders a radio button with an optional label and custom icons for checked and unchecked states.
|
|
77
|
+
* It supports disabled and invalid states, and allows for custom width.
|
|
78
|
+
*/
|
|
79
|
+
export function Radio({
|
|
73
80
|
ref,
|
|
74
81
|
name,
|
|
75
82
|
label,
|
|
76
83
|
value,
|
|
77
84
|
invalid,
|
|
78
85
|
disabled,
|
|
79
|
-
error,
|
|
80
86
|
width,
|
|
81
87
|
icon,
|
|
88
|
+
error,
|
|
82
89
|
iconChecked,
|
|
83
90
|
onChange,
|
|
84
91
|
...rest
|
|
85
|
-
}: RadioProps)
|
|
86
|
-
const iconOn = useMemo(
|
|
87
|
-
|
|
88
|
-
[icon, iconChecked],
|
|
89
|
-
);
|
|
90
|
-
const iconOff = useMemo(() => icon ?? CoreIcons.radioOff, [icon]);
|
|
92
|
+
}: RadioProps) {
|
|
93
|
+
const iconOn = useMemo(() => iconChecked ?? icon ?? "icon-[mdi--radiobox-marked]", [icon, iconChecked]);
|
|
94
|
+
const iconOff = useMemo(() => icon ?? "icon-[mdi--radiobox-blank]", [icon]);
|
|
91
95
|
|
|
92
96
|
const handleChange = useCallback(
|
|
93
|
-
(e?: ChangeEvent<HTMLInputElement>) => {
|
|
97
|
+
(e?: React.ChangeEvent<HTMLInputElement>) => {
|
|
94
98
|
// @ts-expect-error ignore
|
|
95
|
-
e?.target.checked && value
|
|
99
|
+
if (e?.target.checked && value) onChange?.(value);
|
|
96
100
|
},
|
|
97
101
|
[onChange, value],
|
|
98
102
|
);
|
|
99
103
|
|
|
100
104
|
return (
|
|
101
105
|
<label
|
|
102
|
-
className={
|
|
106
|
+
className={cn(
|
|
103
107
|
invalid ? "ring-danger-500" : "ring-primary-500",
|
|
104
|
-
disabled
|
|
105
|
-
|
|
106
|
-
: "hover:opacity-100 cursor-pointer",
|
|
107
|
-
"inline-flex gap-1 py-1 items-center relative cursor-pointer opacity-85 rounded ring-offset-2 has-[:focus-visible]:ring-1",
|
|
108
|
+
disabled ? "opacity-50 cursor-not-allowed" : "hover:opacity-100 cursor-pointer",
|
|
109
|
+
"inline-flex gap-1 py-1 items-center relative cursor-pointer opacity-85 rounded ring-offset-2 has-focus-visible:ring-1",
|
|
108
110
|
)}
|
|
109
111
|
style={{ width }}
|
|
110
112
|
>
|
|
111
113
|
<input
|
|
112
|
-
className={
|
|
113
|
-
"appearance-none absolute peer opacity-0 inset-0 bg-transparent border-none outline-none ring-0",
|
|
114
|
-
)}
|
|
114
|
+
className={cn("appearance-none absolute peer opacity-0 inset-0 bg-transparent border-none outline-none ring-0")}
|
|
115
115
|
aria-disabled={disabled}
|
|
116
116
|
disabled={disabled}
|
|
117
117
|
type="radio"
|
|
@@ -124,23 +124,14 @@ export const Radio = ({
|
|
|
124
124
|
{...rest}
|
|
125
125
|
/>
|
|
126
126
|
<Icon
|
|
127
|
-
className={
|
|
128
|
-
"p-0.5 hidden peer-checked:inline-block",
|
|
129
|
-
icon && !iconChecked && "bg-primary-500 text-white",
|
|
130
|
-
)}
|
|
127
|
+
className={cn("p-0.5 not-peer-checked:hidden", icon && !iconChecked && "bg-primary-500 text-white!")}
|
|
131
128
|
color={invalid ? "danger" : "primary"}
|
|
132
129
|
size="1.25em"
|
|
133
130
|
icon={iconOn}
|
|
134
131
|
/>
|
|
135
|
-
<Icon
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
size="1.25em"
|
|
139
|
-
icon={iconOff}
|
|
140
|
-
/>
|
|
141
|
-
{label && (
|
|
142
|
-
<span className="flex-initial leading-none truncate">{label}</span>
|
|
143
|
-
)}
|
|
132
|
+
<Icon className={cn("p-0.5 peer-checked:hidden")} color={invalid ? "danger" : "primary"} size="1.25em" icon={iconOff} />
|
|
133
|
+
<ErrorIcon invalid={invalid} error={error} />
|
|
134
|
+
{label && <span className="flex-initial leading-tight truncate">{label}</span>}
|
|
144
135
|
</label>
|
|
145
136
|
);
|
|
146
|
-
}
|
|
137
|
+
}
|
package/src/input/RadioGroup.tsx
CHANGED
|
@@ -21,15 +21,14 @@
|
|
|
21
21
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} from "
|
|
28
|
-
import { Children, cloneElement } from "react";
|
|
24
|
+
import { cloneChildren } from "@react-fabric/core";
|
|
25
|
+
import type { ChildrenProp, CssProp } from "@react-fabric/core/dist/types/types";
|
|
26
|
+
import { cn } from "@react-fabric/utilities";
|
|
27
|
+
import { cloneElement, isValidElement } from "react";
|
|
29
28
|
import { FieldWrapper } from "../internal/FieldWrapper";
|
|
30
|
-
import {
|
|
29
|
+
import type { RadioProps } from "./Radio";
|
|
31
30
|
|
|
32
|
-
export interface RadioGroupProps extends ChildrenProp<
|
|
31
|
+
export interface RadioGroupProps extends ChildrenProp<RadioProps>, CssProp {
|
|
33
32
|
/**
|
|
34
33
|
* input label
|
|
35
34
|
*/
|
|
@@ -37,7 +36,7 @@ export interface RadioGroupProps extends ChildrenProp<typeof Radio>, CssProp {
|
|
|
37
36
|
/**
|
|
38
37
|
* append to label end
|
|
39
38
|
*/
|
|
40
|
-
appendLabel?:
|
|
39
|
+
appendLabel?: React.ReactElement | string | number | boolean;
|
|
41
40
|
/**
|
|
42
41
|
* field info
|
|
43
42
|
*/
|
|
@@ -67,31 +66,37 @@ export interface RadioGroupProps extends ChildrenProp<typeof Radio>, CssProp {
|
|
|
67
66
|
* vertical orientation
|
|
68
67
|
*/
|
|
69
68
|
vertical?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* value
|
|
71
|
+
*/
|
|
72
|
+
value?: string | number;
|
|
70
73
|
/**
|
|
71
74
|
* change handler
|
|
72
75
|
*/
|
|
73
76
|
onChange?: (value: AnyObject) => void;
|
|
74
77
|
}
|
|
75
78
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
...rest
|
|
83
|
-
}: RadioGroupProps) => {
|
|
79
|
+
/**
|
|
80
|
+
* RadioGroup component for rendering a group of radio buttons.
|
|
81
|
+
* It allows for customization of the label, options, and layout.
|
|
82
|
+
* The component supports inline and vertical orientations, and can handle change events.
|
|
83
|
+
*/
|
|
84
|
+
export function RadioGroup({ children, name, className, optionWidth, vertical, value, onChange, ...rest }: RadioGroupProps) {
|
|
84
85
|
return (
|
|
85
86
|
<FieldWrapper {...rest}>
|
|
86
|
-
<div className={className}>
|
|
87
|
-
{
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
<div className={cn(className, vertical && "flex flex-col gap-1")}>
|
|
88
|
+
{cloneChildren(children)?.map((child) => {
|
|
89
|
+
if (isValidElement<RadioProps & { checked: boolean }>(child)) {
|
|
90
|
+
return cloneElement(child, {
|
|
91
|
+
name,
|
|
92
|
+
checked: child.props?.value === value,
|
|
93
|
+
onChange,
|
|
94
|
+
width: optionWidth,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return child;
|
|
98
|
+
})}
|
|
94
99
|
</div>
|
|
95
100
|
</FieldWrapper>
|
|
96
101
|
);
|
|
97
|
-
}
|
|
102
|
+
}
|
|
@@ -21,25 +21,15 @@
|
|
|
21
21
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { Format } from "@react-fabric/utilities";
|
|
25
|
-
import
|
|
26
|
-
import {
|
|
27
|
-
Fragment,
|
|
28
|
-
useCallback,
|
|
29
|
-
useDeferredValue,
|
|
30
|
-
useEffect,
|
|
31
|
-
useMemo,
|
|
32
|
-
useState,
|
|
33
|
-
} from "react";
|
|
24
|
+
import { Format, cn } from "@react-fabric/utilities";
|
|
25
|
+
import { Fragment, useCallback, useDeferredValue, useEffect, useMemo, useState } from "react";
|
|
34
26
|
import { InputWrapper } from "../internal/InputWrapper";
|
|
35
27
|
import { useRanger } from "../ranger";
|
|
36
|
-
import {
|
|
28
|
+
import type { InputProps, WrapperProps } from "../types";
|
|
29
|
+
import { getWrapperProps } from "../utils";
|
|
37
30
|
|
|
38
31
|
export interface RangeProps
|
|
39
|
-
extends Omit<
|
|
40
|
-
InputProps<[number, number]>,
|
|
41
|
-
"placeholder" | "allowClear" | "children"
|
|
42
|
-
> {
|
|
32
|
+
extends Omit<InputProps<[number, number]>, "placeholder" | "allowClear" | "children" | "defaultValue">, WrapperProps {
|
|
43
33
|
/**
|
|
44
34
|
* min value
|
|
45
35
|
*/
|
|
@@ -75,11 +65,11 @@ export interface RangeProps
|
|
|
75
65
|
/**
|
|
76
66
|
* custom min label
|
|
77
67
|
*/
|
|
78
|
-
minLabel?: string;
|
|
68
|
+
minLabel?: string | React.ReactElement;
|
|
79
69
|
/**
|
|
80
70
|
* custom max label
|
|
81
71
|
*/
|
|
82
|
-
maxLabel?: string;
|
|
72
|
+
maxLabel?: string | React.ReactElement;
|
|
83
73
|
/**
|
|
84
74
|
* step ranges
|
|
85
75
|
*/
|
|
@@ -95,36 +85,34 @@ export interface RangeProps
|
|
|
95
85
|
}
|
|
96
86
|
|
|
97
87
|
/**
|
|
98
|
-
*
|
|
88
|
+
* RangeSlider component for rendering a range slider input.
|
|
89
|
+
* It supports vertical orientation, custom colors, step increments,
|
|
90
|
+
* min/max values, and display of labels and value tooltips.
|
|
91
|
+
* The component uses the Ranger library for handling the slider functionality.
|
|
92
|
+
* It also provides error handling and supports disabled and read-only states.
|
|
93
|
+
* The component is designed to be flexible and customizable, allowing for various configurations.
|
|
99
94
|
*/
|
|
100
|
-
export
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
onEnterPressed,
|
|
122
|
-
onFocus,
|
|
123
|
-
onSlide,
|
|
124
|
-
invalid,
|
|
125
|
-
...rest
|
|
126
|
-
}: RangeProps) => {
|
|
127
|
-
const [actualValue, setActualValue] = useState<[number, number]>([0, 0]);
|
|
95
|
+
export function RangeSlider(props: RangeProps) {
|
|
96
|
+
const {
|
|
97
|
+
value,
|
|
98
|
+
showLabels,
|
|
99
|
+
showValue,
|
|
100
|
+
onChange,
|
|
101
|
+
onSlide,
|
|
102
|
+
ranges,
|
|
103
|
+
invalid,
|
|
104
|
+
step = 1,
|
|
105
|
+
min = 0,
|
|
106
|
+
max = 100,
|
|
107
|
+
height = 192,
|
|
108
|
+
vertical,
|
|
109
|
+
minLabel,
|
|
110
|
+
maxLabel,
|
|
111
|
+
format,
|
|
112
|
+
color,
|
|
113
|
+
} = props;
|
|
114
|
+
|
|
115
|
+
const [actualValue, setActualValue] = useState<[number, number]>(() => [0, 0]);
|
|
128
116
|
const [displayValue, setDisplayValue] = useState(false);
|
|
129
117
|
const deferred = useDeferredValue(value);
|
|
130
118
|
|
|
@@ -132,16 +120,18 @@ export const Range = ({
|
|
|
132
120
|
const maxValue = useMemo(() => ranges?.slice(-1)[0] ?? max, [max, ranges]);
|
|
133
121
|
|
|
134
122
|
useEffect(() => {
|
|
123
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
135
124
|
setActualValue(deferred ?? [min, max]);
|
|
136
125
|
}, [deferred, min, max]);
|
|
137
126
|
useEffect(() => {
|
|
127
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
138
128
|
setDisplayValue(!!showValue);
|
|
139
129
|
}, [showValue]);
|
|
140
130
|
|
|
141
131
|
const handleChange = useCallback(
|
|
142
132
|
(val: [number, number]) => {
|
|
143
133
|
setActualValue(val ?? [min, max]);
|
|
144
|
-
onChange?.(val ??
|
|
134
|
+
onChange?.(val ?? null);
|
|
145
135
|
},
|
|
146
136
|
[onChange, min, max],
|
|
147
137
|
);
|
|
@@ -174,23 +164,14 @@ export const Range = ({
|
|
|
174
164
|
onDrag: (val) => onSlide?.(val[0]),
|
|
175
165
|
onDragStart: () => setDisplayValue(true),
|
|
176
166
|
onDragEnd: () => setDisplayValue(!!showValue),
|
|
177
|
-
onChange: (values: readonly number[]) =>
|
|
178
|
-
handleChange([values[0], values[1]]),
|
|
167
|
+
onChange: (values: readonly number[]) => handleChange([values[0], values[1]]),
|
|
179
168
|
});
|
|
180
169
|
|
|
181
170
|
return (
|
|
182
|
-
<InputWrapper
|
|
183
|
-
{...rest}
|
|
184
|
-
noOutline
|
|
185
|
-
error={error}
|
|
186
|
-
invalid={invalid}
|
|
187
|
-
isDisabled={disabled}
|
|
188
|
-
isReadOnly={readOnly}
|
|
189
|
-
canClear={!!actualValue}
|
|
190
|
-
>
|
|
171
|
+
<InputWrapper {...getWrapperProps(props)} borderless focusRing>
|
|
191
172
|
<div
|
|
192
|
-
className={
|
|
193
|
-
"flex-
|
|
173
|
+
className={cn(
|
|
174
|
+
"flex-1 order-3 flex items-center select-none z-0",
|
|
194
175
|
vertical && "flex-col w-8",
|
|
195
176
|
invalid ? "text-danger-500" : "text-primary-600",
|
|
196
177
|
)}
|
|
@@ -200,17 +181,14 @@ export const Range = ({
|
|
|
200
181
|
{showLabels && (vertical ? maxDisplay : minDisplay)}
|
|
201
182
|
<div
|
|
202
183
|
ref={rangerInstance.trackElRef}
|
|
203
|
-
className={
|
|
204
|
-
"flex-auto relative flex items-center",
|
|
205
|
-
vertical ? "my-2 w-8" : "mx-2 h-8",
|
|
206
|
-
)}
|
|
184
|
+
className={cn("flex-1 relative flex items-center", vertical ? "my-2 w-8" : "mx-2 h-8")}
|
|
207
185
|
style={{ height: vertical ? height : undefined }}
|
|
208
186
|
>
|
|
209
|
-
{rangerInstance.ticks.map(({
|
|
187
|
+
{rangerInstance.ticks.map(({ key, styles }) => (
|
|
210
188
|
<div
|
|
211
189
|
key={key}
|
|
212
|
-
className={
|
|
213
|
-
"absolute inline-block h-1 w-1 rounded-sm bg-invert opacity-30 leading-
|
|
190
|
+
className={cn(
|
|
191
|
+
"absolute inline-block h-1 w-1 rounded-sm bg-invert opacity-30 leading-0 z-1",
|
|
214
192
|
vertical && "left-1/2 -ml-0.5",
|
|
215
193
|
)}
|
|
216
194
|
style={styles}
|
|
@@ -219,58 +197,52 @@ export const Range = ({
|
|
|
219
197
|
{rangerInstance.segments.map(({ key, styles, active }) => (
|
|
220
198
|
<div
|
|
221
199
|
key={key}
|
|
222
|
-
className={
|
|
200
|
+
className={cn(
|
|
223
201
|
"absolute",
|
|
224
|
-
active
|
|
225
|
-
|
|
226
|
-
: "bg-invert opacity-5 z-0",
|
|
227
|
-
vertical
|
|
228
|
-
? "-bottom-1 left-1/2 -translate-x-1/2"
|
|
229
|
-
: "top-1/2 -translate-y-1/2",
|
|
202
|
+
active ? "bg-current z-1 opacity-90" : "bg-invert opacity-5 z-0",
|
|
203
|
+
vertical ? "-bottom-1 left-1/2 -translate-x-1/2" : "top-1/2 -translate-y-1/2",
|
|
230
204
|
!vertical && (active ? "h-1" : "h-0.5"),
|
|
231
205
|
vertical && (active ? "w-1" : "w-0.5"),
|
|
232
206
|
)}
|
|
233
207
|
style={styles}
|
|
234
208
|
/>
|
|
235
209
|
))}
|
|
236
|
-
{rangerInstance.handles.map(
|
|
237
|
-
|
|
238
|
-
<
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
vertical && "left-1/2 -ml-1.5",
|
|
245
|
-
)}
|
|
246
|
-
{...props}
|
|
247
|
-
style={styles}
|
|
248
|
-
/>
|
|
249
|
-
{displayValue && (
|
|
250
|
-
<div
|
|
251
|
-
className={classNames(
|
|
252
|
-
"bg-invert text-invert py-px px-2 text-xs absolute rounded z-1 whitespace-nowrap",
|
|
253
|
-
"before:absolute before:h-1.5 before:w-1.5 before:z-[-1] before:bg-invert before:top-1/2 before:-translate-y-1/2 before:rotate-45",
|
|
254
|
-
vertical
|
|
255
|
-
? "end-0 ltr:translate-x-full rtl:-translate-x-full translate-y-2"
|
|
256
|
-
: "top-1/2 -translate-y-1/2",
|
|
257
|
-
!vertical &&
|
|
258
|
-
(key === 0
|
|
259
|
-
? "-ms-3 ltr:-translate-x-full rtl:translate-x-full ltr:before:translate-x-[0.125rem] rtl:before:-translate-x-[0.125rem] before:end-0"
|
|
260
|
-
: "ms-3 ltr:before:-translate-x-[0.125rem] rtl:before:translate-x-[0.125rem] before:start-0"),
|
|
261
|
-
)}
|
|
262
|
-
data-align={key === 0 ? "start" : "end"}
|
|
263
|
-
style={valueStyles}
|
|
264
|
-
>
|
|
265
|
-
{Format.number(value, format)}
|
|
266
|
-
</div>
|
|
210
|
+
{rangerInstance.handles.map(({ key, value, props, styles, valueStyles }) => (
|
|
211
|
+
<Fragment key={key}>
|
|
212
|
+
<button
|
|
213
|
+
type="button"
|
|
214
|
+
className={cn(
|
|
215
|
+
"bg-current cursor-pointer appearance-none h-3 w-3 rounded-full z-1 absolute",
|
|
216
|
+
"focus:ring-2 focus:ring-offset-2",
|
|
217
|
+
vertical && "left-1/2 -ml-1.5",
|
|
267
218
|
)}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
219
|
+
{...props}
|
|
220
|
+
style={styles}
|
|
221
|
+
/>
|
|
222
|
+
{displayValue && (
|
|
223
|
+
<div
|
|
224
|
+
className={cn(
|
|
225
|
+
"bg-invert text-invert py-px px-2 text-xs absolute rounded z-1 whitespace-nowrap",
|
|
226
|
+
"before:absolute before:h-1.5 before:w-1.5 before:z-[-1] before:bg-invert before:top-1/2 before:-translate-y-1/2 before:rotate-45",
|
|
227
|
+
vertical
|
|
228
|
+
? "inset-e-0 ltr:translate-x-full rtl:-translate-x-full translate-y-2"
|
|
229
|
+
: "top-1/2 -translate-y-1/2",
|
|
230
|
+
!vertical &&
|
|
231
|
+
(key === 0
|
|
232
|
+
? "-ms-3 ltr:-translate-x-full rtl:translate-x-full ltr:before:translate-x-0.5 rtl:before:-translate-x-0.5 before:inset-e-0"
|
|
233
|
+
: "ms-3 ltr:before:-translate-x-0.5 rtl:before:translate-x-0.5 before:inset-s-0"),
|
|
234
|
+
)}
|
|
235
|
+
data-align={key === 0 ? "start" : "end"}
|
|
236
|
+
style={valueStyles}
|
|
237
|
+
>
|
|
238
|
+
{Format.number(value, format)}
|
|
239
|
+
</div>
|
|
240
|
+
)}
|
|
241
|
+
</Fragment>
|
|
242
|
+
))}
|
|
271
243
|
</div>
|
|
272
244
|
{showLabels && (vertical ? minDisplay : maxDisplay)}
|
|
273
245
|
</div>
|
|
274
246
|
</InputWrapper>
|
|
275
247
|
);
|
|
276
|
-
}
|
|
248
|
+
}
|