@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basic-ui/material",
3
- "version": "1.0.0-alpha.49",
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
+ );