@basic-ui/material 1.0.0-alpha.49 → 1.0.0-alpha.50
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/build/cjs/index.js +70 -0
- package/build/cjs/index.js.map +1 -1
- package/build/esm/Chip/ButtonChip.d.ts +1 -1
- package/build/esm/SearchBar/SearchBar.d.ts +15 -0
- package/build/esm/SearchBar/SearchBar.js +90 -0
- package/build/esm/SearchBar/SearchBar.js.map +1 -0
- package/build/esm/SearchBar/index.d.ts +1 -0
- package/build/esm/SearchBar/index.js +2 -0
- package/build/esm/SearchBar/index.js.map +1 -0
- package/build/esm/Select/CustomContainerExample.d.ts +1 -1
- package/build/esm/Table/TableHead.d.ts +1 -1
- package/build/esm/TextField/IconContainer.js.map +1 -1
- package/build/esm/index.d.ts +1 -0
- package/build/esm/index.js +1 -0
- package/build/esm/index.js.map +1 -1
- package/build/tsconfig-build.tsbuildinfo +1 -1
- package/package.json +2 -3
- package/src/Combobox/Combobox.story.tsx +157 -157
- package/src/SearchBar/SearchBar.story.tsx +98 -0
- package/src/SearchBar/SearchBar.tsx +105 -0
- package/src/SearchBar/index.ts +1 -0
- package/src/TextField/IconContainer.tsx +33 -33
- package/src/TextField/TextField.story.tsx +240 -241
- package/src/index.ts +42 -41
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basic-ui/material",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.50",
|
|
4
4
|
"description": "Accessible React Components used as building blocks for UI patterns",
|
|
5
5
|
"author": "Lucas Terra <lucasterra7@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -61,6 +61,5 @@
|
|
|
61
61
|
"react": "^16.14.0 || ^17.0.0 || ^18.0.0",
|
|
62
62
|
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0",
|
|
63
63
|
"tailwindcss": "^3.0.0"
|
|
64
|
-
}
|
|
65
|
-
"gitHead": "82de3b448289214052d402b2fae13ad5ca635808"
|
|
64
|
+
}
|
|
66
65
|
}
|
|
@@ -1,157 +1,157 @@
|
|
|
1
|
-
import type { ChangeEvent } from 'react';
|
|
2
|
-
import { useMemo, useState } from 'react';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
Combobox,
|
|
6
|
-
ComboboxOption,
|
|
7
|
-
ComboboxList,
|
|
8
|
-
ComboboxPopover,
|
|
9
|
-
ComboboxInput,
|
|
10
|
-
ComboboxButton,
|
|
11
|
-
} from './Combobox';
|
|
12
|
-
import { Box } from '../Box';
|
|
13
|
-
import cities from '../../../core/src/ComboBox/cities';
|
|
14
|
-
|
|
15
|
-
export default {
|
|
16
|
-
title: 'components/Combobox',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
function useCityMatch(searchTerm: string) {
|
|
20
|
-
return useMemo(() => {
|
|
21
|
-
const term = searchTerm.trim().toLowerCase();
|
|
22
|
-
return term === ''
|
|
23
|
-
? cities
|
|
24
|
-
: cities.filter(
|
|
25
|
-
(city) =>
|
|
26
|
-
city.city.toLowerCase().indexOf(term) !== -1 ||
|
|
27
|
-
city.state.toLowerCase().indexOf(term) !== -1
|
|
28
|
-
);
|
|
29
|
-
}, [searchTerm]);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function UncontrolledClientSideExample({ initialValue = '' }) {
|
|
33
|
-
const [term, setTerm] = useState(initialValue);
|
|
34
|
-
const [selected, setSelected] = useState(initialValue);
|
|
35
|
-
const results = useCityMatch(term);
|
|
36
|
-
|
|
37
|
-
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
38
|
-
setTerm(event.target.value);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const handleSelect = (value: string) => {
|
|
42
|
-
setSelected(value);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<div>
|
|
47
|
-
<h2>Clientside Search</h2>
|
|
48
|
-
<p>Selection: {selected}</p>
|
|
49
|
-
<p>Term: {term}</p>
|
|
50
|
-
<Combobox onSelect={handleSelect} selectOnBlur>
|
|
51
|
-
<Box position="relative">
|
|
52
|
-
<ComboboxInput
|
|
53
|
-
onChange={handleChange}
|
|
54
|
-
defaultValue={initialValue}
|
|
55
|
-
label="Enter a city name"
|
|
56
|
-
/>
|
|
57
|
-
<Box
|
|
58
|
-
height={56}
|
|
59
|
-
width={56}
|
|
60
|
-
position="absolute"
|
|
61
|
-
justifyContent="center"
|
|
62
|
-
alignItems="center"
|
|
63
|
-
display="flex"
|
|
64
|
-
top={0}
|
|
65
|
-
right={0}
|
|
66
|
-
bottom={0}
|
|
67
|
-
>
|
|
68
|
-
<ComboboxButton />
|
|
69
|
-
</Box>
|
|
70
|
-
</Box>
|
|
71
|
-
{results.length > 0 && (
|
|
72
|
-
<ComboboxPopover>
|
|
73
|
-
<ComboboxList persistSelection={true}>
|
|
74
|
-
{results.slice(0, 10).map((result, index) => (
|
|
75
|
-
<ComboboxOption
|
|
76
|
-
key={`${result.city}, ${result.state}, ${index}`}
|
|
77
|
-
id={`${result.city}, ${result.state}, ${index}`}
|
|
78
|
-
text={`${result.city}, ${result.state}`}
|
|
79
|
-
value={result}
|
|
80
|
-
/>
|
|
81
|
-
))}
|
|
82
|
-
</ComboboxList>
|
|
83
|
-
</ComboboxPopover>
|
|
84
|
-
)}
|
|
85
|
-
</Combobox>
|
|
86
|
-
</div>
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function ControlledClientSideExample({ initialValue = '' }) {
|
|
91
|
-
const [term, setTerm] = useState(initialValue);
|
|
92
|
-
const [selected, setSelected] = useState(initialValue);
|
|
93
|
-
const results = useCityMatch(term);
|
|
94
|
-
|
|
95
|
-
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
96
|
-
setTerm(event.target.value);
|
|
97
|
-
setSelected('');
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const handleSelect = (value: string) => {
|
|
101
|
-
setSelected(value);
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<div>
|
|
106
|
-
<h2>Clientside Search</h2>
|
|
107
|
-
<p>Selection: {selected}</p>
|
|
108
|
-
<p>Term: {term}</p>
|
|
109
|
-
<Combobox onSelect={handleSelect} selectOnBlur>
|
|
110
|
-
<Box position="relative">
|
|
111
|
-
<ComboboxInput
|
|
112
|
-
onChange={handleChange}
|
|
113
|
-
value={selected || term}
|
|
114
|
-
label="Enter a city name"
|
|
115
|
-
/>
|
|
116
|
-
<Box
|
|
117
|
-
height={56}
|
|
118
|
-
width={56}
|
|
119
|
-
position="absolute"
|
|
120
|
-
justifyContent="center"
|
|
121
|
-
alignItems="center"
|
|
122
|
-
display="flex"
|
|
123
|
-
top={0}
|
|
124
|
-
right={0}
|
|
125
|
-
bottom={0}
|
|
126
|
-
>
|
|
127
|
-
<ComboboxButton />
|
|
128
|
-
</Box>
|
|
129
|
-
</Box>
|
|
130
|
-
{results.length > 0 && (
|
|
131
|
-
<ComboboxPopover>
|
|
132
|
-
<ComboboxList persistSelection={true}>
|
|
133
|
-
{results.slice(0, 10).map((result, index) => (
|
|
134
|
-
<ComboboxOption
|
|
135
|
-
key={`${result.city}, ${result.state}, ${index}`}
|
|
136
|
-
id={`${result.city}, ${result.state}, ${index}`}
|
|
137
|
-
text={`${result.city}, ${result.state}`}
|
|
138
|
-
value={result}
|
|
139
|
-
/>
|
|
140
|
-
))}
|
|
141
|
-
</ComboboxList>
|
|
142
|
-
</ComboboxPopover>
|
|
143
|
-
)}
|
|
144
|
-
</Combobox>
|
|
145
|
-
</div>
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export const UncontrolledClientSide = () => <UncontrolledClientSideExample />;
|
|
150
|
-
export const UncontrolledClientSideInitial = () => (
|
|
151
|
-
<UncontrolledClientSideExample initialValue="Aberdeen" />
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
export const ControlledClientSide = () => <ControlledClientSideExample />;
|
|
155
|
-
export const ControlledClientSideInitial = () => (
|
|
156
|
-
<ControlledClientSideExample initialValue="Aberdeen" />
|
|
157
|
-
);
|
|
1
|
+
import type { ChangeEvent } from 'react';
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Combobox,
|
|
6
|
+
ComboboxOption,
|
|
7
|
+
ComboboxList,
|
|
8
|
+
ComboboxPopover,
|
|
9
|
+
ComboboxInput,
|
|
10
|
+
ComboboxButton,
|
|
11
|
+
} from './Combobox';
|
|
12
|
+
import { Box } from '../Box';
|
|
13
|
+
import cities from '../../../core/src/ComboBox/cities';
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
title: 'components/Combobox',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function useCityMatch(searchTerm: string) {
|
|
20
|
+
return useMemo(() => {
|
|
21
|
+
const term = searchTerm.trim().toLowerCase();
|
|
22
|
+
return term === ''
|
|
23
|
+
? cities
|
|
24
|
+
: cities.filter(
|
|
25
|
+
(city) =>
|
|
26
|
+
city.city.toLowerCase().indexOf(term) !== -1 ||
|
|
27
|
+
city.state.toLowerCase().indexOf(term) !== -1
|
|
28
|
+
);
|
|
29
|
+
}, [searchTerm]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function UncontrolledClientSideExample({ initialValue = '' }) {
|
|
33
|
+
const [term, setTerm] = useState(initialValue);
|
|
34
|
+
const [selected, setSelected] = useState(initialValue);
|
|
35
|
+
const results = useCityMatch(term);
|
|
36
|
+
|
|
37
|
+
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
38
|
+
setTerm(event.target.value);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const handleSelect = (value: string) => {
|
|
42
|
+
setSelected(value);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div>
|
|
47
|
+
<h2>Clientside Search</h2>
|
|
48
|
+
<p>Selection: {selected}</p>
|
|
49
|
+
<p>Term: {term}</p>
|
|
50
|
+
<Combobox onSelect={handleSelect} selectOnBlur>
|
|
51
|
+
<Box position="relative">
|
|
52
|
+
<ComboboxInput
|
|
53
|
+
onChange={handleChange}
|
|
54
|
+
defaultValue={initialValue}
|
|
55
|
+
label="Enter a city name"
|
|
56
|
+
/>
|
|
57
|
+
<Box
|
|
58
|
+
height={56}
|
|
59
|
+
width={56}
|
|
60
|
+
position="absolute"
|
|
61
|
+
justifyContent="center"
|
|
62
|
+
alignItems="center"
|
|
63
|
+
display="flex"
|
|
64
|
+
top={0}
|
|
65
|
+
right={0}
|
|
66
|
+
bottom={0}
|
|
67
|
+
>
|
|
68
|
+
<ComboboxButton />
|
|
69
|
+
</Box>
|
|
70
|
+
</Box>
|
|
71
|
+
{results.length > 0 && (
|
|
72
|
+
<ComboboxPopover>
|
|
73
|
+
<ComboboxList persistSelection={true}>
|
|
74
|
+
{results.slice(0, 10).map((result, index) => (
|
|
75
|
+
<ComboboxOption
|
|
76
|
+
key={`${result.city}, ${result.state}, ${index}`}
|
|
77
|
+
id={`${result.city}, ${result.state}, ${index}`}
|
|
78
|
+
text={`${result.city}, ${result.state}`}
|
|
79
|
+
value={result}
|
|
80
|
+
/>
|
|
81
|
+
))}
|
|
82
|
+
</ComboboxList>
|
|
83
|
+
</ComboboxPopover>
|
|
84
|
+
)}
|
|
85
|
+
</Combobox>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function ControlledClientSideExample({ initialValue = '' }) {
|
|
91
|
+
const [term, setTerm] = useState(initialValue);
|
|
92
|
+
const [selected, setSelected] = useState(initialValue);
|
|
93
|
+
const results = useCityMatch(term);
|
|
94
|
+
|
|
95
|
+
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
96
|
+
setTerm(event.target.value);
|
|
97
|
+
setSelected('');
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const handleSelect = (value: string) => {
|
|
101
|
+
setSelected(value);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<div>
|
|
106
|
+
<h2>Clientside Search</h2>
|
|
107
|
+
<p>Selection: {selected}</p>
|
|
108
|
+
<p>Term: {term}</p>
|
|
109
|
+
<Combobox onSelect={handleSelect} selectOnBlur>
|
|
110
|
+
<Box position="relative">
|
|
111
|
+
<ComboboxInput
|
|
112
|
+
onChange={handleChange}
|
|
113
|
+
value={selected || term}
|
|
114
|
+
label="Enter a city name"
|
|
115
|
+
/>
|
|
116
|
+
<Box
|
|
117
|
+
height={56}
|
|
118
|
+
width={56}
|
|
119
|
+
position="absolute"
|
|
120
|
+
justifyContent="center"
|
|
121
|
+
alignItems="center"
|
|
122
|
+
display="flex"
|
|
123
|
+
top={0}
|
|
124
|
+
right={0}
|
|
125
|
+
bottom={0}
|
|
126
|
+
>
|
|
127
|
+
<ComboboxButton />
|
|
128
|
+
</Box>
|
|
129
|
+
</Box>
|
|
130
|
+
{results.length > 0 && (
|
|
131
|
+
<ComboboxPopover>
|
|
132
|
+
<ComboboxList persistSelection={true}>
|
|
133
|
+
{results.slice(0, 10).map((result, index) => (
|
|
134
|
+
<ComboboxOption
|
|
135
|
+
key={`${result.city}, ${result.state}, ${index}`}
|
|
136
|
+
id={`${result.city}, ${result.state}, ${index}`}
|
|
137
|
+
text={`${result.city}, ${result.state}`}
|
|
138
|
+
value={result}
|
|
139
|
+
/>
|
|
140
|
+
))}
|
|
141
|
+
</ComboboxList>
|
|
142
|
+
</ComboboxPopover>
|
|
143
|
+
)}
|
|
144
|
+
</Combobox>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const UncontrolledClientSide = () => <UncontrolledClientSideExample />;
|
|
150
|
+
export const UncontrolledClientSideInitial = () => (
|
|
151
|
+
<UncontrolledClientSideExample initialValue="Aberdeen" />
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
export const ControlledClientSide = () => <ControlledClientSideExample />;
|
|
155
|
+
export const ControlledClientSideInitial = () => (
|
|
156
|
+
<ControlledClientSideExample initialValue="Aberdeen" />
|
|
157
|
+
);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Box } from '../Box';
|
|
4
|
+
import { CheckBox } from '../CheckBox';
|
|
5
|
+
import { Select, SelectItem } from '..';
|
|
6
|
+
import { SearchBar } from './SearchBar';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: 'components/SearchBar',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const SearchIcon = () => (
|
|
13
|
+
<svg
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
height={20}
|
|
16
|
+
width={20}
|
|
17
|
+
viewBox="0 0 48 48"
|
|
18
|
+
fill="currentColor"
|
|
19
|
+
>
|
|
20
|
+
<path d="M39.8 41.95 26.65 28.8q-1.5 1.3-3.5 2.025-2 .725-4.25.725-5.4 0-9.15-3.75T6 18.75q0-5.3 3.75-9.05 3.75-3.75 9.1-3.75 5.3 0 9.025 3.75 3.725 3.75 3.725 9.05 0 2.15-.7 4.15-.7 2-2.1 3.75L42 39.75Zm-20.95-13.4q4.05 0 6.9-2.875Q28.6 22.8 28.6 18.75t-2.85-6.925Q22.9 8.95 18.85 8.95q-4.1 0-6.975 2.875T9 18.75q0 4.05 2.875 6.925t6.975 2.875Z" />
|
|
21
|
+
</svg>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const Example = () => {
|
|
25
|
+
const [error, setError] = useState<boolean | string>(false);
|
|
26
|
+
const [color, setColor] = useState<'primary' | 'secondary'>('primary');
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Box p={3} display="flex" alignItems="center" flexDirection="column">
|
|
30
|
+
<Box>
|
|
31
|
+
<CheckBox
|
|
32
|
+
checked={Boolean(error)}
|
|
33
|
+
onChange={(e) =>
|
|
34
|
+
setError(e.target.checked ? 'This field is required' : false)
|
|
35
|
+
}
|
|
36
|
+
>
|
|
37
|
+
Has Error
|
|
38
|
+
</CheckBox>
|
|
39
|
+
<Box width={230} display="inline-block">
|
|
40
|
+
<Select
|
|
41
|
+
value={color}
|
|
42
|
+
onChange={(e, value: 'primary' | 'secondary') => setColor(value)}
|
|
43
|
+
label="Color"
|
|
44
|
+
>
|
|
45
|
+
<SelectItem value="primary">Primary</SelectItem>
|
|
46
|
+
<SelectItem value="secondary">Secondary</SelectItem>
|
|
47
|
+
</Select>
|
|
48
|
+
</Box>
|
|
49
|
+
</Box>
|
|
50
|
+
<Box
|
|
51
|
+
py={3}
|
|
52
|
+
backgroundColor="surface"
|
|
53
|
+
display="inline-flex"
|
|
54
|
+
flexWrap="wrap"
|
|
55
|
+
flexDirection="column"
|
|
56
|
+
>
|
|
57
|
+
<Box m={2} width={230} display="inline-block">
|
|
58
|
+
<SearchBar error={error} color={color} />
|
|
59
|
+
</Box>
|
|
60
|
+
<Box m={2} width={230} display="inline-block">
|
|
61
|
+
<SearchBar error={error} color={color} leadingIcon={<SearchIcon />} />
|
|
62
|
+
</Box>
|
|
63
|
+
<Box m={2} width={230} display="inline-block">
|
|
64
|
+
<SearchBar
|
|
65
|
+
error={error}
|
|
66
|
+
color={color}
|
|
67
|
+
trailingIcon={<SearchIcon />}
|
|
68
|
+
/>
|
|
69
|
+
</Box>
|
|
70
|
+
<Box m={2} width={230} display="inline-block">
|
|
71
|
+
<SearchBar
|
|
72
|
+
error={error}
|
|
73
|
+
color={color}
|
|
74
|
+
placeholder="Search your messages..."
|
|
75
|
+
/>
|
|
76
|
+
</Box>
|
|
77
|
+
<Box m={2} width={230} display="inline-block">
|
|
78
|
+
<SearchBar
|
|
79
|
+
error={error}
|
|
80
|
+
color={color}
|
|
81
|
+
placeholder="Search your messages..."
|
|
82
|
+
leadingIcon={<SearchIcon />}
|
|
83
|
+
/>
|
|
84
|
+
</Box>
|
|
85
|
+
<Box m={2} width={230} display="inline-block">
|
|
86
|
+
<SearchBar
|
|
87
|
+
error={error}
|
|
88
|
+
color={color}
|
|
89
|
+
placeholder="Search your messages..."
|
|
90
|
+
trailingIcon={<SearchIcon />}
|
|
91
|
+
/>
|
|
92
|
+
</Box>
|
|
93
|
+
</Box>
|
|
94
|
+
</Box>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const Default = () => <Example />;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { forwardRef, useId } from 'react';
|
|
3
|
+
import { useControlledState } from '@basic-ui/core';
|
|
4
|
+
import { rem } from 'polished';
|
|
5
|
+
|
|
6
|
+
import type { BoxProps } from '../Box';
|
|
7
|
+
import { Box } from '../Box';
|
|
8
|
+
import { Input } from '../TextField/Input';
|
|
9
|
+
import { IconContainer } from '../TextField/IconContainer';
|
|
10
|
+
import { EASING_STANDARD } from '../motion';
|
|
11
|
+
|
|
12
|
+
interface InputProps
|
|
13
|
+
extends BoxProps<HTMLInputElement, InputHTMLAttributes<HTMLInputElement>> {
|
|
14
|
+
as?: 'input';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type SearchBarProps = InputProps & {
|
|
18
|
+
containerProps?: Omit<BoxProps, 'color'>;
|
|
19
|
+
defaultValue?: string;
|
|
20
|
+
value?: string;
|
|
21
|
+
error?: boolean | string;
|
|
22
|
+
leadingIcon?: ReactNode;
|
|
23
|
+
trailingIcon?: ReactNode;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const SearchBar = forwardRef<HTMLInputElement, SearchBarProps>(
|
|
27
|
+
function SearchBar(props, forwardedRef) {
|
|
28
|
+
const {
|
|
29
|
+
type = 'text',
|
|
30
|
+
id: idProp,
|
|
31
|
+
color = 'primary',
|
|
32
|
+
value: valueProp,
|
|
33
|
+
defaultValue = '',
|
|
34
|
+
error,
|
|
35
|
+
onChange: onChangeProp,
|
|
36
|
+
leadingIcon = null,
|
|
37
|
+
trailingIcon = null,
|
|
38
|
+
containerProps,
|
|
39
|
+
variant,
|
|
40
|
+
__css,
|
|
41
|
+
...otherProps
|
|
42
|
+
} = props;
|
|
43
|
+
const { __css: __containerCss, ...otherContainerProps } =
|
|
44
|
+
containerProps || {};
|
|
45
|
+
const [value, onChange] = useControlledState(
|
|
46
|
+
valueProp,
|
|
47
|
+
onChangeProp,
|
|
48
|
+
defaultValue,
|
|
49
|
+
(setState) => (e) => {
|
|
50
|
+
setState(e.target.value);
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const fallbackId = useId();
|
|
55
|
+
|
|
56
|
+
const hasError = Boolean(error);
|
|
57
|
+
|
|
58
|
+
const id = idProp || fallbackId;
|
|
59
|
+
const inputId = `${id}-search-bar`;
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Box
|
|
63
|
+
__css={{
|
|
64
|
+
display: 'inline-flex',
|
|
65
|
+
position: 'relative',
|
|
66
|
+
...__containerCss,
|
|
67
|
+
}}
|
|
68
|
+
{...otherContainerProps}
|
|
69
|
+
>
|
|
70
|
+
<Input
|
|
71
|
+
__css={{
|
|
72
|
+
borderRadius: 'full',
|
|
73
|
+
backgroundColor: 'surface-container-high',
|
|
74
|
+
height: rem(56),
|
|
75
|
+
pl: leadingIcon ? rem(48) : rem(24),
|
|
76
|
+
pr: trailingIcon ? rem(48) : rem(24),
|
|
77
|
+
py: 0,
|
|
78
|
+
transition: `outline-color .2s ${EASING_STANDARD}`,
|
|
79
|
+
outlineStyle: 'solid',
|
|
80
|
+
outlineWidth: rem(2),
|
|
81
|
+
outlineColor: 'transparent',
|
|
82
|
+
outlineOffset: rem(-1),
|
|
83
|
+
'&:focus': {
|
|
84
|
+
outlineColor: hasError ? ('error' as const) : (color as any),
|
|
85
|
+
},
|
|
86
|
+
...__css,
|
|
87
|
+
}}
|
|
88
|
+
type={type}
|
|
89
|
+
ref={forwardedRef}
|
|
90
|
+
hasLabel={false}
|
|
91
|
+
id={inputId}
|
|
92
|
+
value={value}
|
|
93
|
+
onChange={onChange}
|
|
94
|
+
{...otherProps}
|
|
95
|
+
/>
|
|
96
|
+
{leadingIcon && (
|
|
97
|
+
<IconContainer position="start">{leadingIcon}</IconContainer>
|
|
98
|
+
)}
|
|
99
|
+
{trailingIcon && (
|
|
100
|
+
<IconContainer position="end">{trailingIcon}</IconContainer>
|
|
101
|
+
)}
|
|
102
|
+
</Box>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SearchBar';
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { rem } from 'polished';
|
|
2
|
-
import type { FC, ReactNode } from 'react';
|
|
3
|
-
|
|
4
|
-
import { Box } from '../Box';
|
|
5
|
-
import { alpha } from '../color';
|
|
6
|
-
import { ICON_WIDTH, PADDING_LEFT_WITH_ICON } from './consts';
|
|
7
|
-
|
|
8
|
-
export const IconContainer: FC<{
|
|
9
|
-
children?: ReactNode;
|
|
10
|
-
position: 'start' | 'end';
|
|
11
|
-
}> = ({ position, children }) => (
|
|
12
|
-
<Box
|
|
13
|
-
position="absolute"
|
|
14
|
-
__css={{
|
|
15
|
-
top: 0,
|
|
16
|
-
[position === 'start' ? 'left' : 'right']: rem(PADDING_LEFT_WITH_ICON),
|
|
17
|
-
minWidth: rem(ICON_WIDTH),
|
|
18
|
-
display: 'inline-flex',
|
|
19
|
-
alignItems: 'center',
|
|
20
|
-
justifyContent: 'center',
|
|
21
|
-
height: '100%',
|
|
22
|
-
pointerEvents: 'none',
|
|
23
|
-
color: alpha('on.surface', 0.54),
|
|
24
|
-
zIndex: 1,
|
|
25
|
-
fontFamily: 'body',
|
|
26
|
-
lineHeight: 'body',
|
|
27
|
-
fontWeight: 'regular',
|
|
28
|
-
fontSize: 3,
|
|
29
|
-
}}
|
|
30
|
-
>
|
|
31
|
-
{children}
|
|
32
|
-
</Box>
|
|
33
|
-
);
|
|
1
|
+
import { rem } from 'polished';
|
|
2
|
+
import type { FC, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
import { Box } from '../Box';
|
|
5
|
+
import { alpha } from '../color';
|
|
6
|
+
import { ICON_WIDTH, PADDING_LEFT_WITH_ICON } from './consts';
|
|
7
|
+
|
|
8
|
+
export const IconContainer: FC<{
|
|
9
|
+
children?: ReactNode;
|
|
10
|
+
position: 'start' | 'end';
|
|
11
|
+
}> = ({ position, children }) => (
|
|
12
|
+
<Box
|
|
13
|
+
position="absolute"
|
|
14
|
+
__css={{
|
|
15
|
+
top: 0,
|
|
16
|
+
[position === 'start' ? 'left' : 'right']: rem(PADDING_LEFT_WITH_ICON),
|
|
17
|
+
minWidth: rem(ICON_WIDTH),
|
|
18
|
+
display: 'inline-flex',
|
|
19
|
+
alignItems: 'center',
|
|
20
|
+
justifyContent: 'center',
|
|
21
|
+
height: '100%',
|
|
22
|
+
pointerEvents: 'none',
|
|
23
|
+
color: alpha('on.surface', 0.54),
|
|
24
|
+
zIndex: 1,
|
|
25
|
+
fontFamily: 'body',
|
|
26
|
+
lineHeight: 'body',
|
|
27
|
+
fontWeight: 'regular',
|
|
28
|
+
fontSize: 3,
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
</Box>
|
|
33
|
+
);
|