Skip to content

Commit 372a7ff

Browse files
Add TextFields
Add as simple as possible TextField that dangerously affect global scope by styling <input> and <label> elements using type selector. It should be moved to proper place in Foundation at some point.
1 parent a3df413 commit 372a7ff

File tree

8 files changed

+201
-0
lines changed

8 files changed

+201
-0
lines changed

src/elements/forms/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import url('text-field.css');

src/elements/forms/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export TextField from './text-field'

src/elements/forms/text-field.css

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
:root {
2+
--component-padding: 1rem;
3+
--label-padding-x: .25em;
4+
--label-padding-y: .125em;
5+
--form-control-corner-radius: var(--component-corner-radius, 2px);
6+
--form-control-padding-x: var(--component-padding, 1rem);
7+
--form-control-padding-y: var(--component-padding, 1rem);
8+
}
9+
10+
/* Global styles */
11+
12+
/* TODO: Move to Foundation partially */
13+
14+
input {
15+
display: block;
16+
width: 100%;
17+
padding: var(--form-control-padding-y) var(--form-control-padding-x);
18+
border: 1px solid var(--primary);
19+
border-radius: var(--form-control-corner-radius, 2px);
20+
height: calc(2 * var(--form-control-padding-y) + 1em);
21+
22+
&:focus {
23+
box-shadow: none;
24+
outline: none;
25+
}
26+
}
27+
28+
::placeholder {
29+
color: var(--gray-600);
30+
opacity: 0;
31+
transition: 150ms;
32+
transform: translateY(calc(1em + var(--form-control-padding-y)));
33+
}
34+
35+
:focus::placeholder {
36+
opacity: 1;
37+
transform: none;
38+
}
39+
40+
label {
41+
position: absolute;
42+
display: block;
43+
margin:
44+
calc(var(--form-control-padding-y) - var(--label-padding-y))
45+
calc(var(--form-control-padding-x) - var(--label-padding-x));
46+
padding: var(--label-padding-y) var(--label-padding-x);
47+
color: var(--primary);
48+
transition: 150ms;
49+
top: 0;
50+
line-height: 1;
51+
transform-origin: left;
52+
border-radius: var(--form-control-corner-radius, 2px);
53+
}
54+
55+
:matches(input, select, textarea):focus + label,
56+
:matches(input, select, textarea):not(:placeholder-shown) + label {
57+
transform: translateY(calc(-1 * var(--form-control-padding-y) - 0.4375em));
58+
font-size: var(--font-size-small, 0.875em);
59+
background: var(--white, #fff);
60+
}
61+
62+
/* Text field */
63+
64+
.container {
65+
position: relative;
66+
}
67+
68+
/* Size variations */
69+
70+
.small {
71+
--form-control-padding-y: calc(0.75 * var(--component-padding));
72+
}
73+
74+
.large {
75+
--form-control-padding-y: calc(1.25 * var(--component-padding));
76+
}

src/elements/forms/text-field.jsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React from 'react'
2+
import PropTypes from 'prop-types'
3+
4+
import textFieldClassNames from './text-field.css'
5+
6+
import { classNames } from 'utils'
7+
8+
/**
9+
* Unique ID generator
10+
*
11+
* Based on @gordonbrander's gist.
12+
* See: https://gist.github.com/gordonbrander/2230317
13+
*
14+
* > Math.random should be unique because of its seeding algorithm.
15+
* > Convert it to base 36 (numbers + letters), and grab the first 9 characters
16+
* > after the decimal.
17+
*
18+
* @return {string}
19+
*/
20+
const generateId = () =>
21+
Math.random()
22+
.toString(36)
23+
.substr(2, 9)
24+
25+
const TextField = ({
26+
children,
27+
id,
28+
className,
29+
label,
30+
size,
31+
tag: Tag = 'div',
32+
...inputProps
33+
}) => {
34+
const controlId = [id, 'control'].join('-')
35+
36+
return (
37+
<Tag
38+
id={id}
39+
className={classNames
40+
// prevents passing 'default' to the class list
41+
.use('container', { [size]: size !== 'default' }, className)
42+
.withModule(textFieldClassNames)}
43+
>
44+
<input id={controlId} {...inputProps} />
45+
<label htmlFor={controlId}>{label}</label>
46+
</Tag>
47+
)
48+
}
49+
50+
TextField.propTypes = {
51+
/**
52+
* Label for the text field
53+
*/
54+
label: PropTypes.node.isRequired,
55+
/**
56+
* Size variation. Affects height only.
57+
*/
58+
size: PropTypes.oneOf(['default', 'small', 'large']),
59+
/**
60+
* An ID that will be used to link input and label. It's necessary for
61+
* accessibility.
62+
*
63+
* If not passed, it will be generated automatically using a random string.
64+
*/
65+
id: PropTypes.string,
66+
}
67+
68+
TextField.defaultProps = {
69+
size: 'default',
70+
id: generateId(),
71+
}
72+
73+
export default TextField

src/elements/forms/text-field.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
### Type variations
2+
3+
Example with a search input:
4+
5+
```jsx
6+
<TextField
7+
id="search"
8+
type="search"
9+
name="search"
10+
label="Search"
11+
placeholder="Type to search..."
12+
/>
13+
```
14+
15+
Example with a password:
16+
17+
```jsx
18+
<TextField
19+
id="login-password"
20+
type="password"
21+
name="password"
22+
label="Password"
23+
placeholder="🔒 Something secure"
24+
/>
25+
```
26+
27+
### Size variations
28+
29+
#### Small
30+
31+
```jsx
32+
<TextField
33+
label="Name"
34+
placeholder="e.g. John Doe"
35+
size="small"
36+
/>
37+
```
38+
39+
#### Large
40+
41+
```jsx
42+
<TextField
43+
label="Name"
44+
placeholder="e.g. John Doe"
45+
size="large"
46+
/>
47+
```

src/elements/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import url('forms/index.css');

src/elements/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export Heading from './heading'
2+
export * from './forms'

src/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
@import url('foundation/index.css');
2+
@import url('elements/index.css');
23

34
@import url('components/icon/index.css');

0 commit comments

Comments
 (0)