Skip to content

Commit 864e234

Browse files
author
magomedov_gusein
committed
feat: React version of M3TextField
1 parent 44ab1da commit 864e234

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import React, { useState, useEffect, useRef, HTMLAttributes } from 'react';
2+
import makeId from '@/utils/id';
3+
import { toClassName } from '@/utils/styling';
4+
5+
interface M3TextFieldProps extends HTMLAttributes<HTMLElement> {
6+
id?: string;
7+
type?: string;
8+
value?: string | number;
9+
label?: string;
10+
placeholder?: string;
11+
lazy?: boolean;
12+
multiline?: boolean;
13+
invalid?: boolean;
14+
disabled?: boolean;
15+
readonly?: boolean;
16+
outlined?: boolean;
17+
onUpdateValue?: (value: string | number) => void;
18+
}
19+
20+
const M3TextField: React.FC<M3TextFieldProps> = ({
21+
id = makeId('m3-text-field'),
22+
type = 'text',
23+
value = '',
24+
label = '',
25+
placeholder = '',
26+
lazy = false,
27+
multiline = false,
28+
invalid = false,
29+
disabled = false,
30+
readonly = false,
31+
outlined = false,
32+
className = '',
33+
children,
34+
onUpdateValue,
35+
...props
36+
}) => {
37+
const [focused, setFocused] = useState(false);
38+
const inputElement = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);
39+
40+
const onInput = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
41+
const rawValue = event.target.value;
42+
if (!lazy) {
43+
onUpdateValue && onUpdateValue(rawValue);
44+
}
45+
};
46+
47+
const onChange = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
48+
const rawValue = event.target.value;
49+
if (lazy) {
50+
onUpdateValue && onUpdateValue(rawValue);
51+
}
52+
};
53+
54+
const onFocus = () => {
55+
setFocused(true);
56+
};
57+
58+
const onBlur = () => {
59+
setFocused(false);
60+
};
61+
62+
useEffect(() => {
63+
const input = inputElement.current;
64+
if (input) {
65+
const valueStr = String(value);
66+
if (valueStr.length) {
67+
input.value = valueStr;
68+
} else if (input.value.length) {
69+
onUpdateValue && onUpdateValue(input.value);
70+
}
71+
}
72+
}, [value, onUpdateValue]);
73+
74+
return (
75+
<div
76+
className={toClassName([className, {
77+
'm3-text-field': true,
78+
'm3-text-field_outlined': outlined,
79+
'm3-text-field_focused': focused,
80+
'm3-text-field_invalid': invalid,
81+
'm3-text-field_disabled': disabled,
82+
'm3-text-field_readonly': readonly,
83+
}])}
84+
onClick={() => inputElement.current?.focus()}
85+
{...props}
86+
>
87+
{outlined && <div className="m3-text-field__outline">
88+
<div className="m3-text-field__outline-leading" />
89+
<div className="m3-text-field__outline-notch">
90+
<label
91+
id={`${id}-label`}
92+
htmlFor={id}
93+
className="m3-text-field__label"
94+
>
95+
{label}
96+
</label>
97+
</div>
98+
<div className="m3-text-field__outline-trailing" />
99+
</div>}
100+
101+
{!outlined && <label
102+
id={`${id}-label`}
103+
htmlFor={id}
104+
className="m3-text-field__label"
105+
>
106+
{label}
107+
</label>}
108+
109+
<div className="m3-text-field__state">
110+
{children}
111+
112+
{multiline ? (
113+
<textarea
114+
id={id}
115+
ref={inputElement as React.RefObject<HTMLInputElement>}
116+
defaultValue={String(value)}
117+
placeholder={placeholder}
118+
disabled={disabled}
119+
readOnly={readonly}
120+
aria-invalid={invalid ? 'true' : 'false'}
121+
onInput={onInput}
122+
onChange={onChange}
123+
onFocus={onFocus}
124+
onBlur={onBlur}
125+
/>
126+
) : (
127+
<input
128+
id={id}
129+
ref={inputElement as React.RefObject<HTMLInputElement>}
130+
type={type}
131+
defaultValue={String(value)}
132+
placeholder={placeholder}
133+
disabled={disabled}
134+
readOnly={readonly}
135+
aria-invalid={invalid ? 'true' : 'false'}
136+
onInput={onInput}
137+
onChange={onChange}
138+
onFocus={onFocus}
139+
onBlur={onBlur}
140+
/>
141+
)}
142+
</div>
143+
144+
{!outlined && <div className="m3-text-field__underline" />}
145+
</div>
146+
);
147+
};
148+
149+
export default M3TextField;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React, { FC } from 'react';
2+
import { toClassName } from '@/utils/styling';
3+
4+
interface M3TextFieldSupportTextProps {
5+
text?: string;
6+
danger?: boolean;
7+
muted?: boolean;
8+
}
9+
10+
const M3TextFieldSupportText: FC<M3TextFieldSupportTextProps> = ({ text = '', danger = false, muted = false, children }) => {
11+
return (
12+
<div
13+
className={toClassName({
14+
'm3-text-field-support-text': true,
15+
'm3-text-field-support-text_danger': danger,
16+
'm3-text-field-support-text_muted': muted,
17+
})}
18+
>
19+
{children || text}
20+
</div>
21+
);
22+
};
23+
24+
export default M3TextFieldSupportText;

m3-react/src/components/text-field/index.ts

Whitespace-only changes.

0 commit comments

Comments
 (0)