@capyx/components-library 0.0.1 → 0.0.3

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.
Files changed (72) hide show
  1. package/README.md +250 -210
  2. package/dist/addons/AutocompleteInput.d.ts +40 -0
  3. package/dist/addons/AutocompleteInput.d.ts.map +1 -0
  4. package/dist/addons/AutocompleteInput.js +101 -0
  5. package/dist/addons/CharacterCountInput.d.ts +73 -0
  6. package/dist/addons/CharacterCountInput.d.ts.map +1 -0
  7. package/dist/addons/CharacterCountInput.js +130 -0
  8. package/dist/addons/index.d.ts +5 -0
  9. package/dist/addons/index.d.ts.map +1 -0
  10. package/dist/addons/index.js +2 -0
  11. package/dist/components/CheckInput.d.ts +49 -0
  12. package/dist/components/CheckInput.d.ts.map +1 -0
  13. package/dist/components/CheckInput.js +58 -0
  14. package/dist/components/DateInput.d.ts +63 -0
  15. package/dist/components/DateInput.d.ts.map +1 -0
  16. package/dist/components/DateInput.js +86 -0
  17. package/dist/components/FileInput.d.ts +102 -0
  18. package/dist/components/FileInput.d.ts.map +1 -0
  19. package/dist/components/FileInput.js +164 -0
  20. package/dist/components/RichTextInput.d.ts +34 -0
  21. package/dist/components/RichTextInput.d.ts.map +1 -0
  22. package/dist/components/RichTextInput.js +57 -0
  23. package/dist/components/SelectInput.d.ts +54 -0
  24. package/dist/components/SelectInput.d.ts.map +1 -0
  25. package/dist/components/SelectInput.js +64 -0
  26. package/dist/components/SwitchInput.d.ts +46 -0
  27. package/dist/components/SwitchInput.d.ts.map +1 -0
  28. package/dist/components/SwitchInput.js +53 -0
  29. package/dist/components/TagsInput.d.ts +35 -0
  30. package/dist/components/TagsInput.d.ts.map +1 -0
  31. package/dist/components/TagsInput.js +67 -0
  32. package/dist/components/TextAreaInput.d.ts +71 -0
  33. package/dist/components/TextAreaInput.d.ts.map +1 -0
  34. package/dist/components/TextAreaInput.js +113 -0
  35. package/dist/components/TextInput.d.ts +68 -0
  36. package/dist/components/TextInput.d.ts.map +1 -0
  37. package/dist/components/TextInput.js +77 -0
  38. package/dist/components/index.d.ts +10 -0
  39. package/dist/components/index.d.ts.map +1 -0
  40. package/dist/index.cjs +18 -0
  41. package/dist/index.d.ts +3 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/package.json +87 -72
  44. package/.storybook/main.ts +0 -33
  45. package/.storybook/preview.ts +0 -36
  46. package/.storybook/vitest.setup.ts +0 -7
  47. package/biome.json +0 -37
  48. package/lib/addons/CharacterCountInput.tsx +0 -204
  49. package/lib/addons/index.ts +0 -2
  50. package/lib/components/CheckInput.tsx +0 -126
  51. package/lib/components/DateInput.tsx +0 -179
  52. package/lib/components/FileInput.tsx +0 -353
  53. package/lib/components/RichTextInput.tsx +0 -112
  54. package/lib/components/SelectInput.tsx +0 -144
  55. package/lib/components/SwitchInput.tsx +0 -116
  56. package/lib/components/TagsInput.tsx +0 -118
  57. package/lib/components/TextAreaInput.tsx +0 -211
  58. package/lib/components/TextInput.tsx +0 -381
  59. package/stories/CharacterCountInput.stories.tsx +0 -104
  60. package/stories/CheckInput.stories.tsx +0 -80
  61. package/stories/DateInput.stories.tsx +0 -137
  62. package/stories/FileInput.stories.tsx +0 -125
  63. package/stories/RichTextInput.stories.tsx +0 -77
  64. package/stories/SelectInput.stories.tsx +0 -131
  65. package/stories/SwitchInput.stories.tsx +0 -80
  66. package/stories/TagsInput.stories.tsx +0 -69
  67. package/stories/TextAreaInput.stories.tsx +0 -117
  68. package/stories/TextInput.stories.tsx +0 -167
  69. package/vitest.config.ts +0 -37
  70. package/vitest.shims.d.ts +0 -1
  71. /package/{lib/components/index.ts → dist/components/index.js} +0 -0
  72. /package/{lib/index.ts → dist/index.js} +0 -0
@@ -1,116 +0,0 @@
1
- import React, { type FC } from 'react';
2
- import { Form } from 'react-bootstrap';
3
- import { Controller, useFormContext } from 'react-hook-form';
4
-
5
- /**
6
- * Props for the SwitchInput component
7
- */
8
- export type SwitchInputProps = {
9
- /** The name of the switch input field */
10
- name: string;
11
- /** Label text displayed next to the switch */
12
- label?: string;
13
- /** Whether the switch is required */
14
- required?: boolean;
15
- /** Current on/off state (standalone mode) */
16
- value?: boolean;
17
- /** Callback function called when the switch state changes */
18
- onChange?: (checked: boolean) => void;
19
- /** Whether the switch is disabled */
20
- disabled?: boolean;
21
- /** Custom HTML id for the switch element */
22
- id?: string;
23
- };
24
-
25
- /**
26
- * SwitchInput - A toggle switch component with react-hook-form integration
27
- *
28
- * Provides a toggle switch input that works both standalone and with react-hook-form.
29
- * Automatically integrates with FormProvider when available, providing validation
30
- * and error handling.
31
- *
32
- * @example
33
- * ```tsx
34
- * // With react-hook-form
35
- * <SwitchInput
36
- * name="notifications"
37
- * label="Enable notifications"
38
- * />
39
- *
40
- * // Standalone mode
41
- * <SwitchInput
42
- * name="darkMode"
43
- * label="Dark mode"
44
- * value={isDarkMode}
45
- * onChange={setIsDarkMode}
46
- * />
47
- * ```
48
- */
49
- export const SwitchInput: FC<SwitchInputProps> = ({
50
- name,
51
- label,
52
- required = false,
53
- value,
54
- onChange,
55
- disabled = false,
56
- id,
57
- }) => {
58
- const formContext = useFormContext();
59
-
60
- const getFieldError = (fieldName: string) => {
61
- try {
62
- const error = formContext?.formState?.errors?.[fieldName];
63
- return error?.message as string | undefined;
64
- } catch {
65
- return undefined;
66
- }
67
- };
68
-
69
- const errorMessage = getFieldError(name);
70
- const inputId = id || `switch-input-${name}`;
71
-
72
- // Integrated with react-hook-form
73
- if (formContext) {
74
- return (
75
- <Controller
76
- name={name}
77
- control={formContext.control}
78
- rules={{
79
- required: required ? `${label || 'This field'} is required` : false,
80
- }}
81
- render={({ field }) => (
82
- <Form.Check
83
- {...field}
84
- id={inputId}
85
- type="switch"
86
- label={label}
87
- checked={field.value ?? false}
88
- onChange={(e) => {
89
- const checked = e.target.checked;
90
- field.onChange(checked);
91
- onChange?.(checked);
92
- }}
93
- disabled={disabled}
94
- required={required}
95
- isInvalid={!!errorMessage}
96
- feedback={errorMessage}
97
- feedbackType="invalid"
98
- />
99
- )}
100
- />
101
- );
102
- }
103
-
104
- // Standalone mode
105
- return (
106
- <Form.Check
107
- id={inputId}
108
- type="switch"
109
- label={label}
110
- checked={value ?? false}
111
- onChange={(e) => onChange?.(e.target.checked)}
112
- disabled={disabled}
113
- required={required}
114
- />
115
- );
116
- };
@@ -1,118 +0,0 @@
1
- import { Autocomplete, Chip, TextField } from '@mui/material';
2
- import React, { type FC } from 'react';
3
-
4
- /**
5
- * Props for the TagsInput component
6
- */
7
- export type TagsInputProps = {
8
- /** Array of current tag values */
9
- value: string[];
10
- /** Callback function called when tags change */
11
- onChange: (value: string[]) => void;
12
- /** Placeholder text shown when no tags are entered (default: "Add tags...") */
13
- placeholder?: string;
14
- /** Whether the input is disabled */
15
- disabled?: boolean;
16
- /** Name attribute for the input field */
17
- name?: string;
18
- };
19
-
20
- /**
21
- * TagsInput - A multi-tag input component using Material-UI Autocomplete
22
- *
23
- * Provides a tag input interface where users can add/remove multiple tags.
24
- * Automatically trims whitespace and filters empty values. Tags are displayed
25
- * as chips with delete functionality.
26
- *
27
- * @example
28
- * ```tsx
29
- * <TagsInput
30
- * name="skills"
31
- * value={tags}
32
- * onChange={setTags}
33
- * placeholder="Add skills..."
34
- * />
35
- * ```
36
- */
37
- export const TagsInput: FC<TagsInputProps> = ({
38
- value = [],
39
- onChange,
40
- placeholder = 'Add tags...',
41
- disabled = false,
42
- name,
43
- }) => {
44
- return (
45
- <Autocomplete
46
- multiple
47
- freeSolo
48
- options={[]}
49
- value={value}
50
- onChange={(_event, newValue) => {
51
- // Filter out empty strings and trim whitespace
52
- const cleanedValues = newValue
53
- .map((v) => (typeof v === 'string' ? v.trim() : v))
54
- .filter((v) => v.length > 0);
55
- onChange(cleanedValues);
56
- }}
57
- disabled={disabled}
58
- renderValue={(tagValue, getTagProps) =>
59
- tagValue.map((option, index) => (
60
- <Chip
61
- {...getTagProps({ index })}
62
- key={option}
63
- label={option}
64
- sx={{
65
- backgroundColor: '#212529',
66
- color: '#ffffff',
67
- '& .MuiChip-deleteIcon': {
68
- color: 'rgba(255, 255, 255, 0.7)',
69
- userSelect: 'none',
70
- WebkitUserSelect: 'none',
71
- MozUserSelect: 'none',
72
- msUserSelect: 'none',
73
- pointerEvents: 'auto',
74
- '&::selection': {
75
- backgroundColor: 'transparent',
76
- color: 'transparent',
77
- },
78
- '& svg': {
79
- userSelect: 'none',
80
- WebkitUserSelect: 'none',
81
- pointerEvents: 'none',
82
- },
83
- '&:hover': {
84
- color: '#dc3545',
85
- },
86
- },
87
- }}
88
- />
89
- ))
90
- }
91
- renderInput={(params) => (
92
- <TextField
93
- {...params}
94
- name={name}
95
- placeholder={value.length === 0 ? placeholder : undefined}
96
- variant="outlined"
97
- size="small"
98
- sx={{
99
- '& .MuiOutlinedInput-root': {
100
- padding: '4px',
101
- minHeight: '38px',
102
- '& fieldset': {
103
- borderColor: '#ced4da',
104
- },
105
- '&:hover fieldset': {
106
- borderColor: '#86b7fe',
107
- },
108
- '&.Mui-focused fieldset': {
109
- borderColor: '#86b7fe',
110
- borderWidth: '1px',
111
- },
112
- },
113
- }}
114
- />
115
- )}
116
- />
117
- );
118
- };
@@ -1,211 +0,0 @@
1
- import debounce from 'lodash.debounce';
2
- import React, {
3
- type ChangeEvent,
4
- type FC,
5
- useEffect,
6
- useLayoutEffect,
7
- useRef,
8
- useState,
9
- } from 'react';
10
- import { Form } from 'react-bootstrap';
11
- import { Controller, useFormContext } from 'react-hook-form';
12
-
13
- /**
14
- * Props for the TextAreaInput component
15
- */
16
- export type TextAreaInputProps = {
17
- /** The name of the textarea field */
18
- name: string;
19
- /** Label text displayed for the textarea */
20
- label?: string;
21
- /** Whether the field is required */
22
- required?: boolean;
23
- /** Maximum number of characters allowed */
24
- maxLength?: number;
25
- /** Size variant of the textarea control */
26
- controlSize?: 'sm' | 'lg';
27
- /** Placeholder text shown when textarea is empty */
28
- placeholder?: string;
29
- /** Controlled value of the textarea */
30
- value?: string;
31
- /** Callback fired when the value changes */
32
- onChange?: (value: string) => void;
33
- /** Whether the textarea is disabled */
34
- disabled?: boolean;
35
- /** Whether the textarea is read-only */
36
- isReadOnly?: boolean;
37
- /** Whether to render as plain text */
38
- isPlainText?: boolean;
39
- /** Debounce delay in milliseconds for value changes */
40
- debounceMs?: number;
41
- };
42
-
43
- const MIN_TEXTAREA_HEIGHT = 32;
44
-
45
- /**
46
- * A flexible textarea input component with automatic height adjustment,
47
- * react-hook-form integration, and optional debouncing.
48
- *
49
- * Features:
50
- * - Auto-expands height based on content
51
- * - Seamless integration with react-hook-form for validation
52
- * - Debounced onChange callback to reduce update frequency
53
- * - Built-in validation rules (required, maxLength)
54
- * - Works in both controlled and standalone modes
55
- *
56
- * @example
57
- * // Basic usage with react-hook-form
58
- * <TextAreaInput
59
- * name="description"
60
- * label="Description"
61
- * required
62
- * maxLength={500}
63
- * placeholder="Enter description..."
64
- * />
65
- *
66
- * @example
67
- * // With custom onChange and debouncing
68
- * <TextAreaInput
69
- * name="notes"
70
- * label="Notes"
71
- * debounceMs={300}
72
- * onChange={(value) => console.log(value)}
73
- * />
74
- *
75
- * @example
76
- * // Standalone mode without form context
77
- * <TextAreaInput
78
- * name="comment"
79
- * value={commentText}
80
- * onChange={setCommentText}
81
- * placeholder="Add your comment..."
82
- * />
83
- */
84
- export const TextAreaInput: FC<TextAreaInputProps> = ({
85
- name,
86
- label,
87
- required = false,
88
- maxLength,
89
- controlSize,
90
- placeholder,
91
- value,
92
- onChange,
93
- disabled = false,
94
- isReadOnly = false,
95
- isPlainText = false,
96
- debounceMs,
97
- }) => {
98
- const formContext = useFormContext();
99
-
100
- // Create ref for debounced onChange to clean up on unmount
101
- const debouncedOnChangeRef = useRef<ReturnType<typeof debounce> | null>(null);
102
-
103
- // Create debounced version of onChange if debounceMs is provided
104
- useEffect(() => {
105
- if (debounceMs && onChange) {
106
- debouncedOnChangeRef.current = debounce(onChange, debounceMs);
107
- }
108
-
109
- // Cleanup on unmount
110
- return () => {
111
- debouncedOnChangeRef.current?.cancel();
112
- };
113
- }, [debounceMs, onChange]);
114
-
115
- // Helper to handle onChange with optional debouncing
116
- const handleChange = (value: string) => {
117
- if (debounceMs && debouncedOnChangeRef.current) {
118
- debouncedOnChangeRef.current(value);
119
- } else if (onChange) {
120
- onChange(value);
121
- }
122
- };
123
-
124
- const getFieldError = (fieldName: string) => {
125
- try {
126
- const error = formContext?.formState?.errors?.[fieldName];
127
- return error?.message as string | undefined;
128
- } catch {
129
- return undefined;
130
- }
131
- };
132
-
133
- const errorMessage = getFieldError(name);
134
- const isInvalid = !!errorMessage;
135
-
136
- const textareaRef = useRef<HTMLTextAreaElement>(null);
137
- const [_textAreaValue, setTextAreaValue] = useState('');
138
-
139
- const _handleTextAreaChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
140
- setTextAreaValue(event.target.value);
141
- handleChange(event.target.value);
142
- };
143
-
144
- useLayoutEffect(() => {
145
- if (textareaRef.current) {
146
- textareaRef.current.style.height = 'inherit';
147
- textareaRef.current.style.height = `${Math.max(
148
- textareaRef.current.scrollHeight,
149
- MIN_TEXTAREA_HEIGHT,
150
- )}px`;
151
- }
152
- }, []);
153
-
154
- // Integrated with react-hook-form
155
- if (formContext) {
156
- return (
157
- <Controller
158
- name={name}
159
- control={formContext.control}
160
- rules={{
161
- required: required ? `${label || 'This field'} is required` : false,
162
- maxLength: maxLength
163
- ? {
164
- value: maxLength,
165
- message: `Maximum ${maxLength} characters allowed`,
166
- }
167
- : undefined,
168
- }}
169
- render={({ field }) => (
170
- <Form.Control
171
- {...field}
172
- onChange={(e: ChangeEvent<HTMLTextAreaElement>) => {
173
- field.onChange(e);
174
- setTextAreaValue(e.target.value);
175
- handleChange(e.target.value);
176
- }}
177
- ref={textareaRef}
178
- style={{
179
- minHeight: MIN_TEXTAREA_HEIGHT,
180
- resize: 'none',
181
- }}
182
- as="textarea"
183
- required={required}
184
- maxLength={maxLength}
185
- size={controlSize}
186
- placeholder={placeholder}
187
- disabled={disabled}
188
- readOnly={isReadOnly}
189
- plaintext={isPlainText}
190
- isInvalid={isInvalid}
191
- />
192
- )}
193
- />
194
- );
195
- }
196
-
197
- // Standalone mode
198
- return (
199
- <Form.Control
200
- as="textarea"
201
- required={required}
202
- maxLength={maxLength}
203
- size={controlSize}
204
- placeholder={placeholder}
205
- value={value || ''}
206
- disabled={disabled}
207
- readOnly={isReadOnly}
208
- plaintext={isPlainText}
209
- />
210
- );
211
- };