@soul-refiner/design-system 0.1.11 → 0.1.13

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": "@soul-refiner/design-system",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "main": "src/index.ts",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -173,8 +173,6 @@ const base = css<{
173
173
  color: var(--color-grey-7) !important;
174
174
  cursor: unset !important;
175
175
  }
176
-
177
- ${({ $sx }) => ($sx ? $sx : '')}
178
176
  `;
179
177
 
180
178
  const Primary = styled(Box)<{
@@ -199,6 +197,8 @@ const Primary = styled(Box)<{
199
197
  ${base}
200
198
  ${Styles.flex}
201
199
  ${Styles.padding}
200
+
201
+ ${({ $sx }) => ($sx ? $sx : '')}
202
202
  `;
203
203
 
204
204
  const Secondary = styled(Box)<{
@@ -222,6 +222,8 @@ const Secondary = styled(Box)<{
222
222
  ${base}
223
223
  ${Styles.flex}
224
224
  ${Styles.padding}
225
+
226
+ ${({ $sx }) => ($sx ? $sx : '')}
225
227
  `;
226
228
 
227
229
  const Outlined = styled(Box)<{
@@ -240,6 +242,8 @@ const Outlined = styled(Box)<{
240
242
  ${base}
241
243
  ${Styles.flex}
242
244
  ${Styles.padding}
245
+
246
+ ${({ $sx }) => ($sx ? $sx : '')}
243
247
  `;
244
248
 
245
249
  const Danger = styled(Box)<{
@@ -264,6 +268,8 @@ const Danger = styled(Box)<{
264
268
  ${base}
265
269
  ${Styles.flex}
266
270
  ${Styles.padding}
271
+
272
+ ${({ $sx }) => ($sx ? $sx : '')}
267
273
  `;
268
274
 
269
275
  const Text = styled(Box)<{
@@ -291,6 +297,8 @@ const Text = styled(Box)<{
291
297
  ${base}
292
298
  ${Styles.flex}
293
299
  ${Styles.padding}
300
+
301
+ ${({ $sx }) => ($sx ? $sx : '')}
294
302
  `;
295
303
 
296
304
  const Raised = styled(Box)<{
@@ -320,6 +328,8 @@ const Raised = styled(Box)<{
320
328
  ${base}
321
329
  ${Styles.flex}
322
330
  ${Styles.padding}
331
+
332
+ ${({ $sx }) => ($sx ? $sx : '')}
323
333
  `;
324
334
 
325
335
  const Special = styled(Box)<{
@@ -347,4 +357,6 @@ const Special = styled(Box)<{
347
357
  ${base}
348
358
  ${Styles.flex}
349
359
  ${Styles.padding}
360
+
361
+ ${({ $sx }) => ($sx ? $sx : '')}
350
362
  `;
@@ -119,7 +119,7 @@ const Dropdown = ({
119
119
  const handleChange = multiple ? handleChangeMultiple : handleChangeSingle;
120
120
 
121
121
  const isSelected = (value: string) =>
122
- maintainState && multiple ? selected.includes(value) : value === selected;
122
+ maintainState && (multiple ? selected.includes(value) : value === selected);
123
123
 
124
124
  /**
125
125
  * allow for the API to accept children as options
@@ -134,7 +134,7 @@ const Dropdown = ({
134
134
  };
135
135
 
136
136
  if (React.isValidElement(child)) {
137
- // eslint is okay bc deconstruction removes field
137
+ // eslint ignore is okay bc deconstruction removes field
138
138
  // eslint-disable-next-line
139
139
  const { onClick, ...possibleProps } = child.props;
140
140
  props = possibleProps;
@@ -30,6 +30,7 @@ export interface IconProps extends NativeProps {
30
30
  as?: React.ElementType;
31
31
  color?: (typeof COLORS)[number];
32
32
  size?: 16 | 20 | 24;
33
+ sx?: string;
33
34
  }
34
35
 
35
36
  export const Icon = ({
@@ -37,6 +38,7 @@ export const Icon = ({
37
38
  children,
38
39
  color = 'grey-3',
39
40
  size = 16,
41
+ sx,
40
42
  ...rest
41
43
  }: IconProps) => {
42
44
  return (
@@ -47,6 +49,7 @@ export const Icon = ({
47
49
  viewBox="0 0 16 16"
48
50
  fill={colorReference[color]}
49
51
  xmlns="http://www.w3.org/2000/svg"
52
+ $sx={sx}
50
53
  {...rest}
51
54
  >
52
55
  {children}
@@ -54,4 +57,6 @@ export const Icon = ({
54
57
  );
55
58
  };
56
59
 
57
- const SVG = styled.svg``;
60
+ const SVG = styled.svg<{ $sx?: string }>`
61
+ ${({ $sx }) => ($sx ? $sx : '')}
62
+ `;
@@ -0,0 +1,305 @@
1
+ import React from 'react';
2
+ import styled, { css } from 'styled-components';
3
+ import { BoxProps, P } from 'components';
4
+ import { ChevronDown, ChevronUp } from 'icons';
5
+ import { Styles } from 'common';
6
+
7
+ /********************************************************
8
+ * Filter
9
+ */
10
+
11
+ type FilterTriggerTypes = 'outlined' | 'pill' | 'labeled' | 'avatar' | 'text';
12
+
13
+ export interface FilterProps extends React.PropsWithChildren {
14
+ closeOnBlur?: boolean;
15
+ defaultValue?: string;
16
+ disabled?: boolean;
17
+ graphic?: React.ReactElement;
18
+ label?: string;
19
+ sx?: string;
20
+ triggerType: FilterTriggerTypes;
21
+ value: string;
22
+ }
23
+
24
+ const Filter = ({
25
+ closeOnBlur = true,
26
+ children,
27
+ disabled,
28
+ graphic,
29
+ label,
30
+ sx,
31
+ triggerType,
32
+ value,
33
+ }: FilterProps) => {
34
+ const wrapperRef = React.useRef<HTMLDivElement>(null);
35
+ const optionsRef = React.useRef<HTMLDivElement>(null);
36
+ const [open, setOpen] = React.useState(false);
37
+
38
+ React.useEffect(() => {
39
+ if (disabled) {
40
+ setOpen(false);
41
+ }
42
+ }, [disabled]);
43
+
44
+ const handleOpen = () => {
45
+ if (disabled) {
46
+ return;
47
+ }
48
+ setOpen((prev) => !prev);
49
+ };
50
+
51
+ const handleBlur = (event: React.FocusEvent) => {
52
+ if (
53
+ closeOnBlur &&
54
+ wrapperRef?.current &&
55
+ !wrapperRef?.current?.contains(event.relatedTarget)
56
+ ) {
57
+ setOpen(false);
58
+ }
59
+ };
60
+
61
+ return (
62
+ <Wrapper $sx={sx} tabIndex={0} ref={wrapperRef} onBlurCapture={handleBlur}>
63
+ <FilterTrigger
64
+ disabled={disabled}
65
+ graphic={graphic}
66
+ label={label}
67
+ onClick={handleOpen}
68
+ open={open}
69
+ type={triggerType}
70
+ value={value}
71
+ />
72
+ {open && <Container ref={optionsRef}>{children}</Container>}
73
+ </Wrapper>
74
+ );
75
+ };
76
+
77
+ const Wrapper = styled.div<{ $sx?: string }>`
78
+ position: relative;
79
+ display: block;
80
+ outline: none;
81
+ ${({ $sx }) => ($sx ? $sx : '')}
82
+ `;
83
+
84
+ const Container = styled.div`
85
+ position: absolute;
86
+ width: max-content;
87
+ z-index: 100;
88
+ outline: none;
89
+ `;
90
+
91
+ /********************************************************
92
+ * Triggers
93
+ */
94
+
95
+ const textProps: any = {
96
+ as: 'span',
97
+ $fontSize: 14,
98
+ $lineHeight: 20,
99
+ };
100
+
101
+ const wrapperProps: any = {
102
+ $align: 'center',
103
+ $dir: 'row',
104
+ $gap: 16,
105
+ };
106
+
107
+ export interface FilterTriggerProps {
108
+ disabled?: boolean;
109
+ graphic?: React.ReactElement;
110
+ label?: string;
111
+ onClick?: (event: React.MouseEvent) => void;
112
+ open: boolean;
113
+ type: FilterTriggerTypes;
114
+ value: string;
115
+ }
116
+
117
+ const FilterTrigger = ({
118
+ disabled,
119
+ graphic,
120
+ label,
121
+ onClick,
122
+ open,
123
+ type,
124
+ value,
125
+ }: FilterTriggerProps) => {
126
+ if (type === 'pill') {
127
+ return (
128
+ <Pill disabled={disabled} onClick={onClick} {...wrapperProps}>
129
+ {graphic}
130
+ <P {...textProps}>{value}</P>
131
+ {!graphic && (open ? <ChevronUp /> : <ChevronDown />)}
132
+ </Pill>
133
+ );
134
+ }
135
+
136
+ if (type === 'labeled') {
137
+ return (
138
+ <Labeled disabled={disabled} onClick={onClick} {...wrapperProps}>
139
+ <P {...textProps}>{label}</P>
140
+ <LabelSeparator />
141
+ <P $color="primary-blue-2" $mediumBold {...textProps}>
142
+ {value}
143
+ </P>
144
+ {open ? <ChevronUp /> : <ChevronDown />}
145
+ </Labeled>
146
+ );
147
+ }
148
+
149
+ if (type === 'avatar') {
150
+ return (
151
+ <Avatar
152
+ $align="center"
153
+ $dir="row"
154
+ $gap={8}
155
+ disabled={disabled}
156
+ onClick={onClick}
157
+ >
158
+ {graphic}
159
+ {open ? <ChevronUp /> : <ChevronDown />}
160
+ </Avatar>
161
+ );
162
+ }
163
+
164
+ if (type === 'text') {
165
+ return (
166
+ <Text disabled={disabled} onClick={onClick} {...wrapperProps}>
167
+ <P $color="grey-4" $semiBold {...textProps}>
168
+ {label}:
169
+ </P>
170
+ <P
171
+ $color="primary-blue-2"
172
+ $semiBold
173
+ className="filter-trigger-value"
174
+ {...textProps}
175
+ >
176
+ {value}
177
+ </P>
178
+ {open ? <ChevronUp /> : <ChevronDown />}
179
+ </Text>
180
+ );
181
+ }
182
+
183
+ return (
184
+ <Outlined disabled={disabled} onClick={onClick} {...wrapperProps}>
185
+ {graphic}
186
+ <P {...textProps}>{value}</P>
187
+ {!graphic && (open ? <ChevronUp /> : <ChevronDown />)}
188
+ </Outlined>
189
+ );
190
+ };
191
+
192
+ Filter.Trigger = FilterTrigger;
193
+ FilterTrigger.displayName = 'Filter.Trigger';
194
+
195
+ const sharedStyles = css`
196
+ display: flex;
197
+ padding: 10px 16px;
198
+ border: none;
199
+ outline: none;
200
+ cursor: pointer;
201
+ user-select: none;
202
+
203
+ &:disabled {
204
+ background-color: var(--color-grey-9);
205
+ border: 1px solid var(--color-grey-8);
206
+ pointer-events: none;
207
+
208
+ & > * {
209
+ color: var(--color-grey-7);
210
+ fill: var(--color-grey-7);
211
+ }
212
+ }
213
+ `;
214
+
215
+ const Outlined = styled.button<BoxProps>`
216
+ ${sharedStyles}
217
+ ${Styles.flex}
218
+ border: 1px solid var(--color-grey-8);
219
+ border-radius: 8px;
220
+ background-color: var(--color-white);
221
+
222
+ &:hover {
223
+ background-color: var(--color-grey-9);
224
+ }
225
+ `;
226
+
227
+ const Pill = styled.button<BoxProps>`
228
+ ${sharedStyles}
229
+ ${Styles.flex}
230
+ border-radius: 24px;
231
+ background-color: var(--color-primary-blue-4);
232
+
233
+ & > * {
234
+ color: var(--color-primary-blue-2);
235
+ fill: currentColor;
236
+
237
+ &:hover {
238
+ color: var(--color-primary-blue-1);
239
+ }
240
+ }
241
+ `;
242
+
243
+ const Labeled = styled.button<BoxProps>`
244
+ ${sharedStyles}
245
+ ${Styles.flex}
246
+ border-radius: 24px;
247
+ border: 1px solid var(--color-grey-8);
248
+ background-color: var(--color-white);
249
+
250
+ &:hover {
251
+ background-color: var(--color-grey-9);
252
+ }
253
+ `;
254
+
255
+ const LabelSeparator = styled.div`
256
+ width: 1px;
257
+ height: 20px;
258
+ background-color: var(--color-grey-8);
259
+ `;
260
+
261
+ const Avatar = styled.button<BoxProps>`
262
+ ${sharedStyles}
263
+ ${Styles.flex}
264
+ padding: 4px 16px;
265
+ border-radius: 32px;
266
+
267
+ &:hover {
268
+ background-color: var(--color-grey-10);
269
+ }
270
+
271
+ &:active {
272
+ background-color: var(--color-grey-9);
273
+ }
274
+
275
+ &:disabled {
276
+ img {
277
+ opacity: 0.5;
278
+ }
279
+ }
280
+ `;
281
+
282
+ const Text = styled.button<BoxProps>`
283
+ ${sharedStyles}
284
+ ${Styles.flex}
285
+ gap: 8px;
286
+ background-color: transparent;
287
+ border-radius: 32px;
288
+
289
+ &:hover {
290
+ background-color: var(--color-grey-10);
291
+ }
292
+
293
+ &:active {
294
+ & > .filter-trigger-value {
295
+ color: var(--color-primary-blue-0);
296
+ }
297
+ }
298
+
299
+ &:disabled {
300
+ background-color: transparent;
301
+ border: 1px solid transparent;
302
+ }
303
+ `;
304
+
305
+ export { Filter };
@@ -0,0 +1,2 @@
1
+ export { Filter } from './filter';
2
+ export type { FilterProps, FilterTriggerProps } from './filter';
@@ -1,4 +1,8 @@
1
1
  export { Checkbox } from './checkbox';
2
2
  export type { CheckboxProps } from './checkbox';
3
+ export { Filter } from './filter';
4
+ export type { FilterProps, FilterTriggerProps } from './filter';
3
5
  export { Radio } from './radio';
4
6
  export type { RadioProps } from './radio';
7
+ export { Search } from './search';
8
+ export type { SearchProps } from './search';
@@ -0,0 +1,2 @@
1
+ export { Search } from './search';
2
+ export type { SearchProps } from './search';
@@ -0,0 +1,223 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { Styles } from 'common';
4
+ import { BoxProps } from 'components';
5
+ import { Close, Search as SearchIcon } from 'icons';
6
+
7
+ type NativeProps = Pick<
8
+ React.HTMLAttributes<HTMLInputElement>,
9
+ 'placeholder' | 'onChange' | 'onSubmit'
10
+ >;
11
+
12
+ type SearchSize = 'collapsed' | 'small' | 'medium' | 'large';
13
+
14
+ export interface SearchProps extends NativeProps {
15
+ dropdownFilter?: React.ReactElement;
16
+ onClearText?: () => void;
17
+ size: SearchSize;
18
+ sx?: string;
19
+ value: string;
20
+ }
21
+
22
+ export const Search = ({
23
+ dropdownFilter,
24
+ onChange,
25
+ onClearText,
26
+ onSubmit,
27
+ placeholder,
28
+ size,
29
+ sx,
30
+ value,
31
+ }: SearchProps) => {
32
+ const [expanded, setExpanded] = React.useState(false);
33
+ const [focused, setFocused] = React.useState(false);
34
+ const inputRef = React.useRef<HTMLInputElement>(null);
35
+
36
+ const isCollapsed = !expanded && size === 'collapsed';
37
+ const iconSize = isCollapsed || size === 'large' ? 24 : 16;
38
+
39
+ const handleExpand = () => {
40
+ setExpanded(true);
41
+ inputRef?.current?.focus();
42
+ };
43
+
44
+ const handleCollapse = () => {
45
+ setExpanded(false);
46
+ setFocused(false);
47
+ inputRef?.current?.blur();
48
+ };
49
+
50
+ const handleFocus = () => {
51
+ inputRef?.current?.focus();
52
+ };
53
+
54
+ return (
55
+ <Wrapper
56
+ $align="center"
57
+ $dir="row"
58
+ $isCollapsed={isCollapsed}
59
+ $isFocused={focused}
60
+ $borderRadius="large"
61
+ $size={size}
62
+ $sx={sx}
63
+ onClick={
64
+ dropdownFilter && size === 'large'
65
+ ? undefined
66
+ : isCollapsed
67
+ ? handleExpand
68
+ : handleFocus
69
+ }
70
+ >
71
+ <SearchIcon
72
+ color={size === 'large' ? 'blue-7' : 'grey-3'}
73
+ size={iconSize}
74
+ onClick={isCollapsed ? undefined : handleCollapse}
75
+ sx={
76
+ isCollapsed || size !== 'collapsed'
77
+ ? undefined
78
+ : `
79
+ &:hover {
80
+ cursor: pointer;
81
+ background-color: var(--color-grey-10);
82
+ border-radius: 50%;
83
+ }`
84
+ }
85
+ />
86
+ <Input
87
+ $isCollapsed={isCollapsed}
88
+ onBlur={() => setFocused(false)}
89
+ onChange={onChange}
90
+ onFocus={() => setFocused(true)}
91
+ onSubmit={onSubmit}
92
+ placeholder={placeholder}
93
+ ref={inputRef}
94
+ value={value}
95
+ />
96
+ {!isCollapsed && onClearText && (
97
+ <Close
98
+ size={16}
99
+ onClick={onClearText}
100
+ sx={`
101
+ ${value.length === 0 ? 'visibility: hidden;' : ''}
102
+ padding: 4px;
103
+ &:hover {
104
+ cursor: pointer;
105
+ background-color: var(--color-grey-10);
106
+ border-radius: 50%;
107
+ }`}
108
+ />
109
+ )}
110
+ {size === 'large' && dropdownFilter && (
111
+ <>
112
+ <Separator />
113
+ {dropdownFilter}
114
+ </>
115
+ )}
116
+ </Wrapper>
117
+ );
118
+ };
119
+
120
+ const Wrapper = styled.div<
121
+ BoxProps & {
122
+ $isCollapsed: boolean;
123
+ $isFocused: boolean;
124
+ $size: SearchSize;
125
+ $sx?: string;
126
+ }
127
+ >`
128
+ ${Styles.flex}
129
+ ${Styles.padding}
130
+ display: flex;
131
+ background-color: var(--color-white);
132
+
133
+ & > * {
134
+ width: fit-content;
135
+ flex-shrink: 0;
136
+ }
137
+
138
+ ${({ $isCollapsed, $size }) =>
139
+ $isCollapsed
140
+ ? `
141
+ padding: 12px;
142
+ border-radius: 50%;
143
+ &:hover {
144
+ background-color: var(--color-grey-10);
145
+ cursor: pointer;
146
+ }`
147
+ : `
148
+ border-radius: 8px;
149
+ ${
150
+ $size === 'collapsed'
151
+ ? // collapsed
152
+ `
153
+ gap: 8px;
154
+ padding: 10px 16px;
155
+ border: 1px solid var(--color-grey-8);
156
+
157
+ &:hover {
158
+ border: 1px solid var(--color-grey-6);
159
+ }`
160
+ : $size === 'small'
161
+ ? // small
162
+ `
163
+ gap: 12px;
164
+ padding: 10px 16px;
165
+ border: 1px solid var(--color-grey-8);
166
+
167
+ &:hover {
168
+ border: 1px solid var(--color-grey-6);
169
+ }`
170
+ : $size === 'medium'
171
+ ? // medium
172
+ `
173
+ gap: 12px;
174
+ padding: 14px 16px;
175
+ border: 1px solid var(--color-grey-8);
176
+
177
+ &:hover {
178
+ border: 1px solid var(--color-grey-6);
179
+ }`
180
+ : // large
181
+ `
182
+ gap: 16px;
183
+ padding: 16px;
184
+ box-shadow: var(--elevation-1);
185
+
186
+ &:hover {
187
+ border: 1px solid var(--color-grey-6);
188
+ }`
189
+ }`}
190
+
191
+ ${({ $isFocused, $isCollapsed }) =>
192
+ $isFocused && !$isCollapsed
193
+ ? 'border: 1px solid var(--color-primary-blue-2) !important;'
194
+ : ''}
195
+
196
+ ${({ $sx }) => ($sx ? $sx : '')}
197
+ `;
198
+
199
+ const Input = styled.input.attrs(({ ref }) => ({
200
+ ref: ref,
201
+ }))<{
202
+ $isCollapsed: boolean;
203
+ }>`
204
+ flex: 1;
205
+ border-radius: 8px;
206
+ outline: none;
207
+ border: none;
208
+ font-size: 14px;
209
+ font-family: Inter;
210
+ line-height: 20px;
211
+ color: var(--color-grey-2);
212
+
213
+ ${({ $isCollapsed }) =>
214
+ $isCollapsed
215
+ ? 'width: 0px; height: 0px; padding: 0px !important; border: none !important;'
216
+ : ''}
217
+ `;
218
+
219
+ const Separator = styled.div`
220
+ width: 1px;
221
+ height: 20px;
222
+ background-color: var(--color-grey-9);
223
+ `;