@capyx/components-library 0.0.1 → 0.0.2
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/README.md +20 -5
- package/dist/addons/CharacterCountInput.d.ts +73 -0
- package/dist/addons/CharacterCountInput.d.ts.map +1 -0
- package/dist/addons/CharacterCountInput.js +130 -0
- package/{lib/addons/index.ts → dist/addons/index.d.ts} +1 -0
- package/dist/addons/index.d.ts.map +1 -0
- package/dist/addons/index.js +1 -0
- package/dist/components/CheckInput.d.ts +49 -0
- package/dist/components/CheckInput.d.ts.map +1 -0
- package/dist/components/CheckInput.js +58 -0
- package/dist/components/DateInput.d.ts +63 -0
- package/dist/components/DateInput.d.ts.map +1 -0
- package/dist/components/DateInput.js +86 -0
- package/dist/components/FileInput.d.ts +102 -0
- package/dist/components/FileInput.d.ts.map +1 -0
- package/dist/components/FileInput.js +164 -0
- package/dist/components/RichTextInput.d.ts +34 -0
- package/dist/components/RichTextInput.d.ts.map +1 -0
- package/dist/components/RichTextInput.js +57 -0
- package/dist/components/SelectInput.d.ts +54 -0
- package/dist/components/SelectInput.d.ts.map +1 -0
- package/dist/components/SelectInput.js +64 -0
- package/dist/components/SwitchInput.d.ts +46 -0
- package/dist/components/SwitchInput.d.ts.map +1 -0
- package/dist/components/SwitchInput.js +53 -0
- package/dist/components/TagsInput.d.ts +35 -0
- package/dist/components/TagsInput.d.ts.map +1 -0
- package/dist/components/TagsInput.js +67 -0
- package/dist/components/TextAreaInput.d.ts +71 -0
- package/dist/components/TextAreaInput.d.ts.map +1 -0
- package/dist/components/TextAreaInput.js +113 -0
- package/dist/components/TextInput.d.ts +89 -0
- package/dist/components/TextInput.d.ts.map +1 -0
- package/dist/components/TextInput.js +177 -0
- package/dist/components/index.d.ts +10 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/index.cjs +18 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/package.json +85 -70
- package/.storybook/main.ts +0 -33
- package/.storybook/preview.ts +0 -36
- package/.storybook/vitest.setup.ts +0 -7
- package/biome.json +0 -37
- package/lib/addons/CharacterCountInput.tsx +0 -204
- package/lib/components/CheckInput.tsx +0 -126
- package/lib/components/DateInput.tsx +0 -179
- package/lib/components/FileInput.tsx +0 -353
- package/lib/components/RichTextInput.tsx +0 -112
- package/lib/components/SelectInput.tsx +0 -144
- package/lib/components/SwitchInput.tsx +0 -116
- package/lib/components/TagsInput.tsx +0 -118
- package/lib/components/TextAreaInput.tsx +0 -211
- package/lib/components/TextInput.tsx +0 -381
- package/stories/CharacterCountInput.stories.tsx +0 -104
- package/stories/CheckInput.stories.tsx +0 -80
- package/stories/DateInput.stories.tsx +0 -137
- package/stories/FileInput.stories.tsx +0 -125
- package/stories/RichTextInput.stories.tsx +0 -77
- package/stories/SelectInput.stories.tsx +0 -131
- package/stories/SwitchInput.stories.tsx +0 -80
- package/stories/TagsInput.stories.tsx +0 -69
- package/stories/TextAreaInput.stories.tsx +0 -117
- package/stories/TextInput.stories.tsx +0 -167
- package/vitest.config.ts +0 -37
- package/vitest.shims.d.ts +0 -1
- /package/{lib/components/index.ts → dist/components/index.js} +0 -0
- /package/{lib/index.ts → dist/index.js} +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import debounce from 'lodash.debounce';
|
|
3
|
+
import { useEffect, useLayoutEffect, useRef, useState, } from 'react';
|
|
4
|
+
import { Form } from 'react-bootstrap';
|
|
5
|
+
import { Controller, useFormContext } from 'react-hook-form';
|
|
6
|
+
const MIN_TEXTAREA_HEIGHT = 32;
|
|
7
|
+
/**
|
|
8
|
+
* A flexible textarea input component with automatic height adjustment,
|
|
9
|
+
* react-hook-form integration, and optional debouncing.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - Auto-expands height based on content
|
|
13
|
+
* - Seamless integration with react-hook-form for validation
|
|
14
|
+
* - Debounced onChange callback to reduce update frequency
|
|
15
|
+
* - Built-in validation rules (required, maxLength)
|
|
16
|
+
* - Works in both controlled and standalone modes
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Basic usage with react-hook-form
|
|
20
|
+
* <TextAreaInput
|
|
21
|
+
* name="description"
|
|
22
|
+
* label="Description"
|
|
23
|
+
* required
|
|
24
|
+
* maxLength={500}
|
|
25
|
+
* placeholder="Enter description..."
|
|
26
|
+
* />
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // With custom onChange and debouncing
|
|
30
|
+
* <TextAreaInput
|
|
31
|
+
* name="notes"
|
|
32
|
+
* label="Notes"
|
|
33
|
+
* debounceMs={300}
|
|
34
|
+
* onChange={(value) => console.log(value)}
|
|
35
|
+
* />
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // Standalone mode without form context
|
|
39
|
+
* <TextAreaInput
|
|
40
|
+
* name="comment"
|
|
41
|
+
* value={commentText}
|
|
42
|
+
* onChange={setCommentText}
|
|
43
|
+
* placeholder="Add your comment..."
|
|
44
|
+
* />
|
|
45
|
+
*/
|
|
46
|
+
export const TextAreaInput = ({ name, label, required = false, maxLength, controlSize, placeholder, value, onChange, disabled = false, isReadOnly = false, isPlainText = false, debounceMs, }) => {
|
|
47
|
+
const formContext = useFormContext();
|
|
48
|
+
// Create ref for debounced onChange to clean up on unmount
|
|
49
|
+
const debouncedOnChangeRef = useRef(null);
|
|
50
|
+
// Create debounced version of onChange if debounceMs is provided
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (debounceMs && onChange) {
|
|
53
|
+
debouncedOnChangeRef.current = debounce(onChange, debounceMs);
|
|
54
|
+
}
|
|
55
|
+
// Cleanup on unmount
|
|
56
|
+
return () => {
|
|
57
|
+
debouncedOnChangeRef.current?.cancel();
|
|
58
|
+
};
|
|
59
|
+
}, [debounceMs, onChange]);
|
|
60
|
+
// Helper to handle onChange with optional debouncing
|
|
61
|
+
const handleChange = (value) => {
|
|
62
|
+
if (debounceMs && debouncedOnChangeRef.current) {
|
|
63
|
+
debouncedOnChangeRef.current(value);
|
|
64
|
+
}
|
|
65
|
+
else if (onChange) {
|
|
66
|
+
onChange(value);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const getFieldError = (fieldName) => {
|
|
70
|
+
try {
|
|
71
|
+
const error = formContext?.formState?.errors?.[fieldName];
|
|
72
|
+
return error?.message;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const errorMessage = getFieldError(name);
|
|
79
|
+
const isInvalid = !!errorMessage;
|
|
80
|
+
const textareaRef = useRef(null);
|
|
81
|
+
const [_textAreaValue, setTextAreaValue] = useState('');
|
|
82
|
+
const _handleTextAreaChange = (event) => {
|
|
83
|
+
setTextAreaValue(event.target.value);
|
|
84
|
+
handleChange(event.target.value);
|
|
85
|
+
};
|
|
86
|
+
useLayoutEffect(() => {
|
|
87
|
+
if (textareaRef.current) {
|
|
88
|
+
textareaRef.current.style.height = 'inherit';
|
|
89
|
+
textareaRef.current.style.height = `${Math.max(textareaRef.current.scrollHeight, MIN_TEXTAREA_HEIGHT)}px`;
|
|
90
|
+
}
|
|
91
|
+
}, []);
|
|
92
|
+
// Integrated with react-hook-form
|
|
93
|
+
if (formContext) {
|
|
94
|
+
return (_jsx(Controller, { name: name, control: formContext.control, rules: {
|
|
95
|
+
required: required ? `${label || 'This field'} is required` : false,
|
|
96
|
+
maxLength: maxLength
|
|
97
|
+
? {
|
|
98
|
+
value: maxLength,
|
|
99
|
+
message: `Maximum ${maxLength} characters allowed`,
|
|
100
|
+
}
|
|
101
|
+
: undefined,
|
|
102
|
+
}, render: ({ field }) => (_jsx(Form.Control, { ...field, onChange: (e) => {
|
|
103
|
+
field.onChange(e);
|
|
104
|
+
setTextAreaValue(e.target.value);
|
|
105
|
+
handleChange(e.target.value);
|
|
106
|
+
}, ref: textareaRef, style: {
|
|
107
|
+
minHeight: MIN_TEXTAREA_HEIGHT,
|
|
108
|
+
resize: 'none',
|
|
109
|
+
}, as: "textarea", required: required, maxLength: maxLength, size: controlSize, placeholder: placeholder, disabled: disabled, readOnly: isReadOnly, plaintext: isPlainText, isInvalid: isInvalid })) }));
|
|
110
|
+
}
|
|
111
|
+
// Standalone mode
|
|
112
|
+
return (_jsx(Form.Control, { as: "textarea", required: required, maxLength: maxLength, size: controlSize, placeholder: placeholder, value: value || '', disabled: disabled, readOnly: isReadOnly, plaintext: isPlainText }));
|
|
113
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type FC, type ReactElement } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Props for the TextInput component
|
|
4
|
+
*/
|
|
5
|
+
export type TextInputProps = {
|
|
6
|
+
/** The name of the text input field */
|
|
7
|
+
name?: string;
|
|
8
|
+
/** Label text displayed for the input */
|
|
9
|
+
label?: string;
|
|
10
|
+
/** HTML input type (default: "text") */
|
|
11
|
+
type?: string;
|
|
12
|
+
/** Minimum value for number/date inputs */
|
|
13
|
+
min?: string | number;
|
|
14
|
+
/** Maximum value for number/date inputs */
|
|
15
|
+
max?: string | number;
|
|
16
|
+
/** Whether the field is required */
|
|
17
|
+
required?: boolean;
|
|
18
|
+
/** Maximum number of characters allowed */
|
|
19
|
+
maxLength?: number;
|
|
20
|
+
/** Size variant of the input control */
|
|
21
|
+
controlSize?: 'sm' | 'lg' | undefined;
|
|
22
|
+
/** Placeholder text shown when input is empty */
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
/** Controlled field value */
|
|
25
|
+
fieldValue?: string | number | Date | boolean | undefined;
|
|
26
|
+
/** Callback to update field value */
|
|
27
|
+
setFieldValue?: (value: string | number | Date | boolean | undefined) => void;
|
|
28
|
+
/** Callback to save/persist field value */
|
|
29
|
+
saveFieldvalue?: (value: string | number | Date | boolean | undefined) => void;
|
|
30
|
+
/** Whether the input is disabled */
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
/** Whether the input is read-only */
|
|
33
|
+
isReadOnly?: boolean;
|
|
34
|
+
/** Whether to render as plain text */
|
|
35
|
+
isPlainText?: boolean;
|
|
36
|
+
/** Regex pattern for validation */
|
|
37
|
+
pattern?: string;
|
|
38
|
+
/** Icon element to display in the input group */
|
|
39
|
+
icon?: ReactElement | null;
|
|
40
|
+
/** Enable autocomplete/suggestions functionality */
|
|
41
|
+
shouldAutoComplete?: boolean;
|
|
42
|
+
/** Array of suggestion values for autocomplete */
|
|
43
|
+
values?: string[];
|
|
44
|
+
/** Debounce delay in milliseconds for value changes */
|
|
45
|
+
debounceMs?: number;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* A versatile text input component with support for react-hook-form integration,
|
|
49
|
+
* autocomplete suggestions, debouncing, and various input types.
|
|
50
|
+
*
|
|
51
|
+
* Features:
|
|
52
|
+
* - Seamless integration with react-hook-form for validation and state management
|
|
53
|
+
* - Autocomplete with suggestion filtering when enabled
|
|
54
|
+
* - Debounced value updates to reduce callback frequency
|
|
55
|
+
* - Support for various input types (text, number, email, tel, etc.)
|
|
56
|
+
* - Built-in validation rules (required, min, max, maxLength, pattern)
|
|
57
|
+
* - Icon support for enhanced UI
|
|
58
|
+
* - Automatic phone number formatting for tel type
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* // Basic usage with react-hook-form
|
|
62
|
+
* <TextInput
|
|
63
|
+
* name="email"
|
|
64
|
+
* label="Email Address"
|
|
65
|
+
* type="email"
|
|
66
|
+
* required
|
|
67
|
+
* placeholder="Enter your email"
|
|
68
|
+
* />
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* // With autocomplete suggestions
|
|
72
|
+
* <TextInput
|
|
73
|
+
* name="country"
|
|
74
|
+
* label="Country"
|
|
75
|
+
* shouldAutoComplete
|
|
76
|
+
* values={["USA", "Canada", "Mexico"]}
|
|
77
|
+
* />
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* // With debouncing and custom callbacks
|
|
81
|
+
* <TextInput
|
|
82
|
+
* name="search"
|
|
83
|
+
* placeholder="Search..."
|
|
84
|
+
* debounceMs={300}
|
|
85
|
+
* setFieldValue={handleSearch}
|
|
86
|
+
* />
|
|
87
|
+
*/
|
|
88
|
+
export declare const TextInput: FC<TextInputProps>;
|
|
89
|
+
//# sourceMappingURL=TextInput.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextInput.d.ts","sourceRoot":"","sources":["../../lib/components/TextInput.tsx"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,EAAE,EACP,KAAK,YAAY,EAKjB,MAAM,OAAO,CAAC;AAWf;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC;IAC1D,qCAAqC;IACrC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,KAAK,IAAI,CAAC;IAC9E,2CAA2C;IAC3C,cAAc,CAAC,EAAE,CAChB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,KAC/C,IAAI,CAAC;IACV,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sCAAsC;IACtC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B,oDAAoD;IACpD,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,CA+QxC,CAAC"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import debounce from 'lodash.debounce';
|
|
3
|
+
import { useEffect, useRef, useState, } from 'react';
|
|
4
|
+
import Autosuggest from 'react-autosuggest';
|
|
5
|
+
import { Form, InputGroup } from 'react-bootstrap';
|
|
6
|
+
import { Controller, useFormContext } from 'react-hook-form';
|
|
7
|
+
const formatSpaces = (value) => value.replace(/(?<=[A-Z])(?![0-9]+)(?=[A-Z][a-z])|(?<=[^A-Z])(?![0-9]+)(?=[A-Z])|(?<=[A-Za-z])(?![0-9]+)(?=[^A-Za-z])/g, ' ');
|
|
8
|
+
/**
|
|
9
|
+
* A versatile text input component with support for react-hook-form integration,
|
|
10
|
+
* autocomplete suggestions, debouncing, and various input types.
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - Seamless integration with react-hook-form for validation and state management
|
|
14
|
+
* - Autocomplete with suggestion filtering when enabled
|
|
15
|
+
* - Debounced value updates to reduce callback frequency
|
|
16
|
+
* - Support for various input types (text, number, email, tel, etc.)
|
|
17
|
+
* - Built-in validation rules (required, min, max, maxLength, pattern)
|
|
18
|
+
* - Icon support for enhanced UI
|
|
19
|
+
* - Automatic phone number formatting for tel type
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // Basic usage with react-hook-form
|
|
23
|
+
* <TextInput
|
|
24
|
+
* name="email"
|
|
25
|
+
* label="Email Address"
|
|
26
|
+
* type="email"
|
|
27
|
+
* required
|
|
28
|
+
* placeholder="Enter your email"
|
|
29
|
+
* />
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // With autocomplete suggestions
|
|
33
|
+
* <TextInput
|
|
34
|
+
* name="country"
|
|
35
|
+
* label="Country"
|
|
36
|
+
* shouldAutoComplete
|
|
37
|
+
* values={["USA", "Canada", "Mexico"]}
|
|
38
|
+
* />
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // With debouncing and custom callbacks
|
|
42
|
+
* <TextInput
|
|
43
|
+
* name="search"
|
|
44
|
+
* placeholder="Search..."
|
|
45
|
+
* debounceMs={300}
|
|
46
|
+
* setFieldValue={handleSearch}
|
|
47
|
+
* />
|
|
48
|
+
*/
|
|
49
|
+
export const TextInput = ({ name, label, type = 'text', min, max, required, maxLength, controlSize, placeholder, fieldValue, setFieldValue, saveFieldvalue, disabled, isReadOnly, isPlainText, pattern, icon, shouldAutoComplete, values = [], debounceMs, }) => {
|
|
50
|
+
const formContext = useFormContext();
|
|
51
|
+
// Create refs for debounced functions to clean up on unmount
|
|
52
|
+
const debouncedSetFieldValueRef = useRef(null);
|
|
53
|
+
const debouncedSaveFieldValueRef = useRef(null);
|
|
54
|
+
// Create debounced versions of callbacks if debounceMs is provided
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (debounceMs && setFieldValue) {
|
|
57
|
+
debouncedSetFieldValueRef.current = debounce(setFieldValue, debounceMs);
|
|
58
|
+
}
|
|
59
|
+
if (debounceMs && saveFieldvalue) {
|
|
60
|
+
debouncedSaveFieldValueRef.current = debounce(saveFieldvalue, debounceMs);
|
|
61
|
+
}
|
|
62
|
+
// Cleanup on unmount
|
|
63
|
+
return () => {
|
|
64
|
+
debouncedSetFieldValueRef.current?.cancel();
|
|
65
|
+
debouncedSaveFieldValueRef.current?.cancel();
|
|
66
|
+
};
|
|
67
|
+
}, [debounceMs, setFieldValue, saveFieldvalue]);
|
|
68
|
+
const fieldError = formContext && name ? formContext.formState.errors[name] : null;
|
|
69
|
+
const isInvalid = !!fieldError;
|
|
70
|
+
const [suggestions, setSuggestions] = useState([]);
|
|
71
|
+
const getSuggestions = (value) => {
|
|
72
|
+
const inputValue = value.trim().toLowerCase();
|
|
73
|
+
const inputLength = inputValue.length;
|
|
74
|
+
return inputLength === 0
|
|
75
|
+
? []
|
|
76
|
+
: values.filter((v) => v.trim().toLowerCase().indexOf(inputValue) > -1);
|
|
77
|
+
};
|
|
78
|
+
const onChange = (_event, { newValue }) => {
|
|
79
|
+
if (debounceMs) {
|
|
80
|
+
if (debouncedSetFieldValueRef.current)
|
|
81
|
+
debouncedSetFieldValueRef.current(newValue);
|
|
82
|
+
if (debouncedSaveFieldValueRef.current)
|
|
83
|
+
debouncedSaveFieldValueRef.current(newValue);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
if (setFieldValue)
|
|
87
|
+
setFieldValue(newValue);
|
|
88
|
+
if (saveFieldvalue)
|
|
89
|
+
saveFieldvalue(newValue);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const defaultOnChange = async (event) => {
|
|
93
|
+
if (type === 'tel') {
|
|
94
|
+
const nativeEvent = event.nativeEvent;
|
|
95
|
+
const isPasted = nativeEvent.inputType?.startsWith('insertFromPaste');
|
|
96
|
+
if (isPasted) {
|
|
97
|
+
const pasted = event.currentTarget.value;
|
|
98
|
+
const formatted = pasted.replace(/[./]/g, ' ');
|
|
99
|
+
event.currentTarget.value = formatted;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (setFieldValue)
|
|
103
|
+
setFieldValue(event.currentTarget.value);
|
|
104
|
+
if (saveFieldvalue)
|
|
105
|
+
saveFieldvalue(event.currentTarget.value);
|
|
106
|
+
};
|
|
107
|
+
let processedFieldValue = fieldValue;
|
|
108
|
+
if (label && label === 'Profile') {
|
|
109
|
+
processedFieldValue = formatSpaces(fieldValue);
|
|
110
|
+
}
|
|
111
|
+
if (formContext && name) {
|
|
112
|
+
// With react-hook-form Controller
|
|
113
|
+
if (shouldAutoComplete && values?.length > 0) {
|
|
114
|
+
return (_jsx(Autosuggest, { suggestions: suggestions, onSuggestionsFetchRequested: ({ value }) => {
|
|
115
|
+
const newSuggestions = getSuggestions(value);
|
|
116
|
+
setSuggestions(newSuggestions);
|
|
117
|
+
}, onSuggestionsClearRequested: () => setSuggestions([]), getSuggestionValue: (item) => item, renderSuggestion: (suggestion) => (_jsx("span", { children: suggestion }, suggestion)), highlightFirstSuggestion: true, inputProps: {
|
|
118
|
+
value: String(processedFieldValue || ''),
|
|
119
|
+
onChange: (_event, { newValue }) => {
|
|
120
|
+
if (formContext && name) {
|
|
121
|
+
formContext.setValue(name, newValue);
|
|
122
|
+
}
|
|
123
|
+
if (setFieldValue)
|
|
124
|
+
setFieldValue(newValue);
|
|
125
|
+
if (saveFieldvalue)
|
|
126
|
+
saveFieldvalue(newValue);
|
|
127
|
+
},
|
|
128
|
+
}, renderInputComponent: ({ value, onChange, ...props }) => (_jsxs(InputGroup, { children: [icon && _jsx(InputGroup.Text, { children: icon }), _jsx(Form.Control, { type: type,
|
|
129
|
+
// biome-ignore lint/suspicious/noExplicitAny: Type mismatch between Props and Form.Control - controlSize needs type assertion
|
|
130
|
+
size: controlSize, min: min ?? undefined, max: max ?? undefined, required: required ?? false, maxLength: maxLength ?? undefined, placeholder: placeholder ?? undefined, value: value, onChange: onChange, disabled: disabled, readOnly: isReadOnly, plaintext: isPlainText, pattern: pattern, isInvalid: isInvalid, ...props })] })) }));
|
|
131
|
+
}
|
|
132
|
+
return (_jsx(Controller, { name: name, control: formContext.control, rules: {
|
|
133
|
+
required: required ? `${label || 'This field'} is required` : false,
|
|
134
|
+
maxLength: maxLength
|
|
135
|
+
? {
|
|
136
|
+
value: maxLength,
|
|
137
|
+
message: `Maximum ${maxLength} characters allowed`,
|
|
138
|
+
}
|
|
139
|
+
: undefined,
|
|
140
|
+
min: min
|
|
141
|
+
? { value: Number(min), message: `Minimum value is ${min}` }
|
|
142
|
+
: undefined,
|
|
143
|
+
max: max
|
|
144
|
+
? { value: Number(max), message: `Maximum value is ${max}` }
|
|
145
|
+
: undefined,
|
|
146
|
+
pattern: pattern
|
|
147
|
+
? { value: new RegExp(pattern), message: 'Invalid format' }
|
|
148
|
+
: undefined,
|
|
149
|
+
}, render: ({ field: { value, ...field } }) => (_jsx(Form.Control, { ...field, value: value ?? '', type: type,
|
|
150
|
+
// biome-ignore lint/suspicious/noExplicitAny: Type mismatch between Props and Form.Control - controlSize needs type assertion
|
|
151
|
+
size: controlSize, min: min ?? undefined, max: max ?? undefined, required: required ?? false, maxLength: maxLength ?? undefined, placeholder: placeholder ?? undefined, onChange: (e) => {
|
|
152
|
+
field.onChange(e);
|
|
153
|
+
if (setFieldValue)
|
|
154
|
+
setFieldValue(e.target.value);
|
|
155
|
+
if (saveFieldvalue)
|
|
156
|
+
saveFieldvalue(e.target.value);
|
|
157
|
+
}, disabled: disabled, readOnly: isReadOnly, plaintext: isPlainText, pattern: pattern, isInvalid: isInvalid })) }));
|
|
158
|
+
}
|
|
159
|
+
// Without react-hook-form (fallback)
|
|
160
|
+
if (shouldAutoComplete && values?.length > 0) {
|
|
161
|
+
return (_jsx(Autosuggest, { suggestions: suggestions, onSuggestionsFetchRequested: ({ value }) => {
|
|
162
|
+
const newSuggestions = getSuggestions(value);
|
|
163
|
+
setSuggestions(newSuggestions);
|
|
164
|
+
}, onSuggestionsClearRequested: () => setSuggestions([]), getSuggestionValue: (item) => item, renderSuggestion: (suggestion) => (_jsx("span", { children: suggestion }, suggestion)), highlightFirstSuggestion: true, inputProps: {
|
|
165
|
+
value: String(processedFieldValue || ''),
|
|
166
|
+
onChange,
|
|
167
|
+
}, renderInputComponent: (inputProps) => {
|
|
168
|
+
const { value, onChange, ...props } = inputProps;
|
|
169
|
+
return (_jsxs(InputGroup, { children: [icon && _jsx(InputGroup.Text, { children: icon }), _jsx(Form.Control, { type: type,
|
|
170
|
+
// biome-ignore lint/suspicious/noExplicitAny: Type mismatch between Props and Form.Control - controlSize needs type assertion
|
|
171
|
+
size: controlSize, min: min ?? undefined, max: max ?? undefined, required: required ?? false, maxLength: maxLength ?? undefined, placeholder: placeholder ?? undefined, value: String(value || ''), onChange: onChange, disabled: disabled, readOnly: isReadOnly, plaintext: isPlainText, pattern: pattern, ...props })] }));
|
|
172
|
+
} }));
|
|
173
|
+
}
|
|
174
|
+
return (_jsx(Form.Control, { type: type,
|
|
175
|
+
// biome-ignore lint/suspicious/noExplicitAny: Type mismatch between Props and Form.Control - controlSize needs type assertion
|
|
176
|
+
size: controlSize, min: min ?? undefined, max: max ?? undefined, required: required ?? false, maxLength: maxLength ?? undefined, placeholder: placeholder ?? undefined, onChange: defaultOnChange, value: processedFieldValue, disabled: disabled, readOnly: isReadOnly, plaintext: isPlainText, pattern: pattern }));
|
|
177
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { CheckInput } from './CheckInput';
|
|
2
|
+
export { DateInput } from './DateInput';
|
|
3
|
+
export { FileInput } from './FileInput';
|
|
4
|
+
export { RichTextInput } from './RichTextInput';
|
|
5
|
+
export { SelectInput } from './SelectInput';
|
|
6
|
+
export { SwitchInput } from './SwitchInput';
|
|
7
|
+
export { TagsInput } from './TagsInput';
|
|
8
|
+
export { TextAreaInput } from './TextAreaInput';
|
|
9
|
+
export { TextInput } from './TextInput';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./addons"), exports);
|
|
18
|
+
__exportStar(require("./components"), exports);
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,72 +1,87 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
2
|
+
"name": "@capyx/components-library",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Capyx Components Library for forms across applications",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"components",
|
|
10
|
+
"library",
|
|
11
|
+
"typescript"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://gitlab.com/capyx/rmt/components-library#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://gitlab.com/capyx/rmt/components-library/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+ssh://git@gitlab.com/capyx/rmt/components-library.git"
|
|
20
|
+
},
|
|
21
|
+
"author": "tinael.devresse@capyx.be",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js",
|
|
29
|
+
"require": "./dist/index.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"directories": {
|
|
37
|
+
"lib": "lib"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc && npm run build:cjs",
|
|
41
|
+
"build:cjs": "tsc --module commonjs --outDir dist-cjs && node scripts/rename-cjs.js",
|
|
42
|
+
"prepublishOnly": "npm run build",
|
|
43
|
+
"lint": "biome lint .",
|
|
44
|
+
"lint:fix": "biome lint --write .",
|
|
45
|
+
"format": "biome format .",
|
|
46
|
+
"format:fix": "biome format --write .",
|
|
47
|
+
"check": "biome check .",
|
|
48
|
+
"check:fix": "biome check --write .",
|
|
49
|
+
"storybook": "storybook dev -p 6006",
|
|
50
|
+
"build-storybook": "storybook build"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@biomejs/biome": "^2.3.11",
|
|
54
|
+
"@chromatic-com/storybook": "^5.0.0",
|
|
55
|
+
"@storybook/addon-a11y": "^10.1.11",
|
|
56
|
+
"@storybook/addon-docs": "^10.1.11",
|
|
57
|
+
"@storybook/addon-onboarding": "^10.1.11",
|
|
58
|
+
"@storybook/addon-vitest": "^10.1.11",
|
|
59
|
+
"@storybook/react-vite": "^10.1.11",
|
|
60
|
+
"@types/dateformat": "^5.0.3",
|
|
61
|
+
"@types/lodash.debounce": "^4.0.9",
|
|
62
|
+
"@types/node": "^25.0.9",
|
|
63
|
+
"@types/react": "^19.2.8",
|
|
64
|
+
"@types/react-autosuggest": "^10.1.11",
|
|
65
|
+
"@types/react-datepicker": "^7.0.0",
|
|
66
|
+
"@vitest/browser-playwright": "^4.0.17",
|
|
67
|
+
"@vitest/coverage-v8": "^4.0.17",
|
|
68
|
+
"playwright": "^1.57.0",
|
|
69
|
+
"storybook": "^10.1.11",
|
|
70
|
+
"typescript": "^5.9.3",
|
|
71
|
+
"vitest": "^4.0.17"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@emotion/styled": "^11.14.1",
|
|
75
|
+
"@mui/material": "^7.3.7",
|
|
76
|
+
"@mui/x-date-pickers": "^8.25.0",
|
|
77
|
+
"bootstrap": "^5.3.8",
|
|
78
|
+
"dateformat": "^5.0.3",
|
|
79
|
+
"dayjs": "^1.11.19",
|
|
80
|
+
"lodash.debounce": "^4.0.8",
|
|
81
|
+
"react": "^19.2.3",
|
|
82
|
+
"react-autosuggest": "^10.1.0",
|
|
83
|
+
"react-bootstrap": "^2.10.10",
|
|
84
|
+
"react-hook-form": "^7.71.1",
|
|
85
|
+
"react-quill-new": "^3.7.0"
|
|
86
|
+
}
|
|
72
87
|
}
|
package/.storybook/main.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { StorybookConfig } from '@storybook/react-vite';
|
|
2
|
-
|
|
3
|
-
const config: StorybookConfig = {
|
|
4
|
-
"stories": [
|
|
5
|
-
"../stories/**/*.mdx",
|
|
6
|
-
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)"
|
|
7
|
-
],
|
|
8
|
-
"addons": [
|
|
9
|
-
"@chromatic-com/storybook",
|
|
10
|
-
"@storybook/addon-vitest",
|
|
11
|
-
"@storybook/addon-a11y",
|
|
12
|
-
"@storybook/addon-docs",
|
|
13
|
-
"@storybook/addon-onboarding"
|
|
14
|
-
],
|
|
15
|
-
"framework": "@storybook/react-vite",
|
|
16
|
-
async viteFinal(config) {
|
|
17
|
-
// Suppress "use client" directive warnings from MUI components
|
|
18
|
-
if (config.build) {
|
|
19
|
-
config.build.rollupOptions = {
|
|
20
|
-
...config.build.rollupOptions,
|
|
21
|
-
onwarn(warning, warn) {
|
|
22
|
-
// Ignore "use client" directive warnings
|
|
23
|
-
if (warning.code === 'MODULE_LEVEL_DIRECTIVE' && warning.message.includes('"use client"')) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
warn(warning);
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
return config;
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
export default config;
|
package/.storybook/preview.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { Preview } from '@storybook/react-vite'
|
|
2
|
-
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
3
|
-
import 'react-quill-new/dist/quill.snow.css';
|
|
4
|
-
|
|
5
|
-
const preview: Preview = {
|
|
6
|
-
parameters: {
|
|
7
|
-
backgrounds: {
|
|
8
|
-
default: 'dark',
|
|
9
|
-
values: [
|
|
10
|
-
{
|
|
11
|
-
name: 'dark',
|
|
12
|
-
value: '#1a1a1a',
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
name: 'light',
|
|
16
|
-
value: '#ffffff',
|
|
17
|
-
},
|
|
18
|
-
],
|
|
19
|
-
},
|
|
20
|
-
controls: {
|
|
21
|
-
matchers: {
|
|
22
|
-
color: /(background|color)$/i,
|
|
23
|
-
date: /Date$/i,
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
a11y: {
|
|
28
|
-
// 'todo' - show a11y violations in the test UI only
|
|
29
|
-
// 'error' - fail CI on a11y violations
|
|
30
|
-
// 'off' - skip a11y checks entirely
|
|
31
|
-
test: 'todo'
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export default preview;
|