@bosonprotocol/react-kit 0.33.0-alpha.11 → 0.33.0-alpha.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.
@@ -11,11 +11,19 @@ import Error from "./Error";
11
11
  import type { InputProps } from "./types";
12
12
  import { SelectDataProps } from "./types";
13
13
  import { theme as importedTheme } from "../../theme";
14
+ import { checkIfValueIsEmpty } from "../../lib/object/checkIfValueIsEmpty";
14
15
  export type { Country as CountryCode } from "react-phone-number-input";
15
16
 
16
17
  const colors = importedTheme.colors.light;
17
- const customStyles = {
18
- control: (provided: any, state: any) => {
18
+ const customStyles = (
19
+ error: unknown,
20
+ customTheme: CountrySelectProps["theme"]
21
+ ): StylesConfig<
22
+ SelectDataProps<string>,
23
+ false,
24
+ GroupBase<SelectDataProps<string>>
25
+ > => ({
26
+ control: (provided, state: any) => {
19
27
  const before = state.selectProps.label
20
28
  ? {
21
29
  ":before": {
@@ -25,26 +33,29 @@ const customStyles = {
25
33
  }
26
34
  }
27
35
  : null;
28
- const { theme } = state;
29
36
  return {
30
37
  ...provided,
31
- borderRadius: theme.borderRadius,
32
- height: theme.controlHeight,
33
38
  alignContent: "center",
39
+ borderRadius: customTheme?.control?.borderRadius ?? 0,
40
+ height: customTheme?.control?.height,
34
41
  padding: "0.4rem 1rem",
35
42
  boxShadow: "none",
43
+ background: colors.lightGrey,
44
+ ...customTheme?.control,
45
+ border: state.isFocused
46
+ ? customTheme?.control?.focus?.border ?? `1px solid ${colors.secondary}`
47
+ : !checkIfValueIsEmpty(error)
48
+ ? customTheme?.control?.error?.border ?? `1px solid ${colors.orange}`
49
+ : customTheme?.control?.border ?? `1px solid ${colors.border}`,
36
50
  ":hover": {
37
- borderColor: theme.colors.controlHoverBorderColor,
38
- borderWidth: "1px"
51
+ borderColor: colors.secondary,
52
+ borderWidth: "1px",
53
+ ...customTheme?.control?.hover
39
54
  },
40
- background: theme.colors.controlBackground,
41
- border: state.isFocused
42
- ? `1px solid ${theme.colors.controlFocusBorderColor}`
43
- : `1px solid ${theme.colors.controlUnfocusedBorderColor}`,
44
55
  ...before
45
56
  };
46
57
  },
47
- container: (provided: any, state: any) => {
58
+ container: (provided, state) => {
48
59
  return {
49
60
  ...provided,
50
61
  zIndex: state.isFocused ? zIndex.Select + 1 : zIndex.Select,
@@ -52,20 +63,25 @@ const customStyles = {
52
63
  width: "100%"
53
64
  };
54
65
  },
55
- option: (provided: any, state: any) => {
56
- const { theme } = state;
66
+ option: (provided, state: any) => {
57
67
  return {
58
68
  ...provided,
69
+ ...customTheme?.option,
59
70
  cursor: state.isDisabled ? "not-allowed" : "pointer",
60
- opacity: state.isDisabled ? "0.5" : "1",
71
+ opacity: state.isDisabled
72
+ ? customTheme?.option?.disabled?.opacity ?? "0.5"
73
+ : customTheme?.option?.opacity ?? "1",
61
74
  background:
62
75
  state.isOptionSelected || state.isSelected || state.isFocused
63
- ? theme.colors.selectedOptionBackground
64
- : theme.colors.unselectedOptionBackground,
76
+ ? customTheme?.option?.selected?.background ?? colors.lightGrey
77
+ : customTheme?.option?.background ?? colors.white,
65
78
  color:
66
79
  state.isOptionSelected || state.isSelected
67
- ? theme.colors.selectedOptionColor
68
- : theme.colors.unselectedOptionColor
80
+ ? customTheme?.option?.selected?.color ?? colors.secondary
81
+ : customTheme?.option?.color ?? colors.black,
82
+ ...(state.isDisabled && customTheme?.option?.disabled),
83
+ ...((state.isOptionSelected || state.isSelected) &&
84
+ customTheme?.option?.selected)
69
85
  };
70
86
  },
71
87
  menu: (provided) => ({
@@ -74,12 +90,20 @@ const customStyles = {
74
90
  }),
75
91
  indicatorSeparator: () => ({
76
92
  display: "none"
93
+ }),
94
+ placeholder: (provided) => ({
95
+ ...provided,
96
+ ...customTheme?.placeholder
97
+ }),
98
+ input: (provided) => ({
99
+ ...provided,
100
+ ...customTheme?.input
101
+ }),
102
+ singleValue: (provided) => ({
103
+ ...provided,
104
+ ...customTheme?.singleValue
77
105
  })
78
- } satisfies StylesConfig<
79
- SelectDataProps<string>,
80
- false,
81
- GroupBase<SelectDataProps<string>>
82
- >;
106
+ });
83
107
 
84
108
  const ControlGrid = styled.div`
85
109
  display: flex;
@@ -122,38 +146,52 @@ const PhoneWrapper = styled.div`
122
146
 
123
147
  export type CountrySelectProps = InputProps & {
124
148
  countries?: CountryCode[];
149
+ fieldValueIsCountryCode?: boolean; // if true, the field.value will be the countryCodeOrName, otherwise the country name
125
150
  theme?: Partial<{
126
- controlHeight: CSSProperties["height"];
127
- borderRadius: CSSProperties["borderRadius"];
128
- controlHoverBorderColor: CSSProperties["borderColor"];
129
- controlBackground: CSSProperties["background"];
130
- controlFocusBorderColor: CSSProperties["borderColor"];
131
- controlUnfocusedBorderColor: CSSProperties["borderColor"];
132
- selectedOptionBackground: CSSProperties["background"];
133
- unselectedOptionBackground: CSSProperties["background"];
134
- selectedOptionColor: CSSProperties["color"];
135
- unselectedOptionColor: CSSProperties["color"];
151
+ control: Partial<CSSProperties> &
152
+ Partial<{
153
+ hover: Partial<CSSProperties>;
154
+ focus: Partial<CSSProperties>;
155
+ error: Partial<CSSProperties>;
156
+ }>;
157
+ option: Partial<CSSProperties> &
158
+ Partial<{
159
+ selected: Partial<CSSProperties>;
160
+ disabled: Partial<CSSProperties>;
161
+ }>;
162
+ placeholder: Partial<CSSProperties>;
163
+ input: Partial<CSSProperties>;
164
+ singleValue: Partial<CSSProperties>;
136
165
  }>;
137
166
  };
138
-
167
+ type CountryName = string;
139
168
  export function CountrySelect({
140
169
  name,
141
170
  countries,
142
171
  theme: selectTheme,
172
+ fieldValueIsCountryCode,
143
173
  ...rest
144
174
  }: CountrySelectProps) {
145
175
  const { status } = useFormikContext();
146
176
  const [initialized, setInitialized] = useState<boolean>(false);
147
177
  const [field, meta, helpers] = useField(name);
148
- const errorText = meta.error || status?.[name];
149
- const errorMessage = errorText && meta.touched ? errorText : "";
150
- const displayError = typeof errorMessage === "string" && errorMessage !== "";
178
+ const errorMessage = meta.error || status?.[name];
179
+ const displayErrorMessage =
180
+ meta.error && meta.touched && !errorMessage
181
+ ? meta.error
182
+ : meta.error && meta.touched && errorMessage
183
+ ? errorMessage
184
+ : "";
185
+ const displayError =
186
+ typeof displayErrorMessage === "string" && displayErrorMessage !== "";
151
187
  const [phone, setPhone] = useState<string | undefined>(undefined);
152
- const [countryCode, setCountryCode] = useState<CountryCode | undefined>();
188
+ const [countryCodeOrName, setCountryCodeOrName] = useState<
189
+ CountryCode | CountryName | undefined
190
+ >();
153
191
 
154
192
  useEffect(() => {
155
193
  if (!initialized && field.value) {
156
- setCountryCode(field.value as CountryCode);
194
+ setCountryCodeOrName(field.value);
157
195
  setInitialized(true);
158
196
  }
159
197
  }, [field.value, initialized]); // eslint-disable-line
@@ -167,94 +205,83 @@ export function CountrySelect({
167
205
  <div></div>
168
206
  ))}
169
207
  addInternationalOption={false}
170
- country={countryCode}
208
+ country={countryCodeOrName}
171
209
  value={phone}
172
210
  onChange={(value) => setPhone((value || "").replace(/\+/g, ""))}
173
211
  countries={countries}
174
- countrySelectComponent={({ iconComponent: Icon, ...props }) => (
175
- <>
176
- <div>
177
- <Select
178
- {...rest}
179
- {...props}
180
- isDisabled={rest.disabled}
181
- theme={(theme) => ({
182
- ...theme,
183
- controlHeight: selectTheme?.controlHeight,
184
- borderRadius: selectTheme?.borderRadius || 0,
185
- colors: {
186
- ...theme.colors,
187
- controlHoverBorderColor:
188
- selectTheme?.controlHoverBorderColor ||
189
- colors.secondary,
190
- controlBackground:
191
- selectTheme?.controlBackground || colors.lightGrey,
192
- controlFocusBorderColor:
193
- selectTheme?.controlFocusBorderColor ||
194
- colors.secondary,
195
- controlUnfocusedBorderColor:
196
- selectTheme?.controlUnfocusedBorderColor ||
197
- colors.border,
198
- selectedOptionBackground:
199
- selectTheme?.selectedOptionBackground ||
200
- colors.lightGrey,
201
- unselectedOptionBackground:
202
- selectTheme?.unselectedOptionBackground || colors.white,
203
- selectedOptionColor:
204
- selectTheme?.selectedOptionColor || colors.secondary,
205
- unselectedOptionColor:
206
- selectTheme?.unselectedOptionColor || colors.black
207
- }
208
- })}
209
- styles={customStyles}
210
- name="countrySelect"
211
- value={(props?.options || []).find(
212
- (o: SelectDataProps) => o.value === countryCode
213
- )}
214
- onChange={(o: SelectDataProps) => {
215
- setCountryCode(o.value as CountryCode);
216
- helpers.setValue(o.label);
217
- }}
218
- components={{
219
- Control: (props) => {
220
- const country =
221
- (props?.getValue()[0] as any)?.value || null;
222
- return (
223
- <components.Control {...props}>
224
- <ControlGrid>
225
- {country ? (
226
- <Icon country={country as CountryCode} label="" />
227
- ) : (
228
- <GlobeHemisphereWest />
229
- )}
230
- {props.children as any}
231
- </ControlGrid>
232
- </components.Control>
233
- );
234
- },
235
- Option: (props) => {
236
- const country = (props?.data as any)?.value || null;
237
- return (
238
- <components.Option {...props}>
239
- <OptionGrid>
240
- {country ? (
241
- <Icon
242
- country={country as CountryCode}
243
- label={props.label}
244
- />
245
- ) : (
246
- <GlobeHemisphereWest />
247
- )}
248
- {props.label}
249
- </OptionGrid>
250
- </components.Option>
251
- );
252
- }
253
- }}
254
- />
255
- </div>
256
- </>
257
- )}
212
+ countrySelectComponent={({ iconComponent: Icon, ...props }) => {
213
+ const value = (props?.options || []).find((o: SelectDataProps) =>
214
+ fieldValueIsCountryCode
215
+ ? o.value === countryCodeOrName
216
+ : o.label === countryCodeOrName
217
+ );
218
+ return (
219
+ <>
220
+ <div>
221
+ <Select
222
+ {...rest}
223
+ {...props}
224
+ isDisabled={rest.disabled}
225
+ styles={customStyles(displayErrorMessage, selectTheme)}
226
+ name="countrySelect"
227
+ value={value}
228
+ onChange={(o: SelectDataProps) => {
229
+ if (!meta.touched) {
230
+ helpers.setTouched(true);
231
+ }
232
+ const value = fieldValueIsCountryCode ? o.value : o.label;
233
+ setCountryCodeOrName(value);
234
+ helpers.setValue(value);
235
+ }}
236
+ onBlur={() => {
237
+ if (!meta.touched) {
238
+ helpers.setTouched(true);
239
+ }
240
+ }}
241
+ components={{
242
+ Control: (props) => {
243
+ const country =
244
+ (props?.getValue()[0] as any)?.value || null;
245
+ return (
246
+ <components.Control {...props}>
247
+ <ControlGrid>
248
+ {country ? (
249
+ <Icon
250
+ country={country as CountryCode}
251
+ label=""
252
+ />
253
+ ) : (
254
+ <GlobeHemisphereWest />
255
+ )}
256
+ {props.children as any}
257
+ </ControlGrid>
258
+ </components.Control>
259
+ );
260
+ },
261
+ Option: (props) => {
262
+ const country = (props?.data as any)?.value || null;
263
+ return (
264
+ <components.Option {...props}>
265
+ <OptionGrid>
266
+ {country ? (
267
+ <Icon
268
+ country={country as CountryCode}
269
+ label={props.label}
270
+ />
271
+ ) : (
272
+ <GlobeHemisphereWest />
273
+ )}
274
+ {props.label}
275
+ </OptionGrid>
276
+ </components.Option>
277
+ );
278
+ }
279
+ }}
280
+ />
281
+ </div>
282
+ </>
283
+ );
284
+ }}
258
285
  />
259
286
  </PhoneWrapper>
260
287
  <Error display={!rest.hideError && displayError} message={errorMessage} />
@@ -11,13 +11,14 @@ import type { SelectDataProps, SelectProps } from "./types";
11
11
  export type { SelectProps } from "./types";
12
12
  const colors = theme.colors.light;
13
13
 
14
- const customStyles = (error: any, customTheme: SelectProps["theme"]) => ({
14
+ const customStyles = (error: unknown, customTheme: SelectProps["theme"]) => ({
15
15
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
16
  singleValue: (provided: any, state: any) => {
17
17
  return {
18
18
  ...provided,
19
19
  color: colors.darkGrey,
20
- fontSize: "13.33px"
20
+ fontSize: "13.33px",
21
+ ...customTheme?.singleValue
21
22
  };
22
23
  },
23
24
  control: (provided: any, state: any) => {
@@ -69,10 +70,21 @@ const customStyles = (error: any, customTheme: SelectProps["theme"]) => ({
69
70
  color:
70
71
  state.isOptionSelected || state.isSelected
71
72
  ? customTheme?.option?.selected?.color ?? colors.secondary
72
- : customTheme?.option?.color ?? colors.black
73
+ : customTheme?.option?.color ?? colors.black,
74
+ ...(state.isDisabled && customTheme?.option?.disabled),
75
+ ...((state.isOptionSelected || state.isSelected) &&
76
+ customTheme?.option?.selected)
73
77
  }),
74
78
  indicatorSeparator: () => ({
75
79
  display: "none"
80
+ }),
81
+ placeholder: (provided: any) => ({
82
+ ...provided,
83
+ ...customTheme?.placeholder
84
+ }),
85
+ input: (provided: any) => ({
86
+ ...provided,
87
+ ...customTheme?.input
76
88
  })
77
89
  });
78
90
 
@@ -90,37 +90,20 @@ export interface SelectProps extends BaseProps {
90
90
  onChange?: (option: SelectDataProps<string>) => void;
91
91
  label?: string;
92
92
  theme?: Partial<{
93
- control: Partial<{
94
- background: CSSProperties["background"];
95
- borderRadius: CSSProperties["borderRadius"];
96
- padding: CSSProperties["padding"];
97
- boxShadow: CSSProperties["boxShadow"];
98
- borderWidth: CSSProperties["borderWidth"];
99
- border: CSSProperties["border"];
100
- focus: Partial<{
101
- border: CSSProperties["border"];
93
+ control: Partial<CSSProperties> &
94
+ Partial<{
95
+ hover: Partial<CSSProperties>;
96
+ focus: Partial<CSSProperties>;
97
+ error: Partial<CSSProperties>;
102
98
  }>;
103
- hover: Partial<{
104
- borderColor: CSSProperties["borderColor"];
105
- borderWidth: CSSProperties["borderWidth"];
106
- border: CSSProperties["border"];
99
+ option: Partial<CSSProperties> &
100
+ Partial<{
101
+ selected: Partial<CSSProperties>;
102
+ disabled: Partial<CSSProperties>;
107
103
  }>;
108
- error: Partial<{
109
- border: CSSProperties["border"];
110
- }>;
111
- }>;
112
- option: Partial<{
113
- opacity: CSSProperties["opacity"];
114
- background: CSSProperties["background"];
115
- color: CSSProperties["color"];
116
- selected: Partial<{
117
- background: CSSProperties["background"];
118
- color: CSSProperties["color"];
119
- }>;
120
- disabled: Partial<{
121
- opacity: CSSProperties["opacity"];
122
- }>;
123
- }>;
104
+ placeholder: Partial<CSSProperties>;
105
+ input: Partial<CSSProperties>;
106
+ singleValue: Partial<CSSProperties>;
124
107
  }>;
125
108
  }
126
109
 
@@ -74,7 +74,14 @@ export default {
74
74
  initialValues={{ [inputName]: "" }}
75
75
  initialTouched={{ [inputName]: true }}
76
76
  >
77
- <Story args={{ ...args, name: inputName }} />
77
+ {({ values }) => {
78
+ return (
79
+ <>
80
+ <Story args={{ ...args, name: inputName }} />
81
+ <div>selected value: {JSON.stringify(values)}</div>
82
+ </>
83
+ );
84
+ }}
78
85
  </Formik>
79
86
  );
80
87
  }
@@ -95,16 +102,47 @@ export const CustomTheme = {
95
102
  args: {
96
103
  ...BASE_ARGS,
97
104
  theme: {
98
- controlHeight: "45px",
99
- borderRadius: "30px",
100
- controlBackground: colors.lightGrey,
101
- controlFocusBorderColor: colors.blue,
102
- controlHoverBorderColor: colors.red,
103
- controlUnfocusedBorderColor: colors.border,
104
- selectedOptionBackground: colors.lightGrey,
105
- selectedOptionColor: colors.orange,
106
- unselectedOptionBackground: colors.white,
107
- unselectedOptionColor: colors.black
105
+ control: {
106
+ color: "yellow",
107
+ height: undefined,
108
+ background: colors.arsenic,
109
+ borderRadius: "16px",
110
+ padding: "3px",
111
+ boxShadow: "1px 2px 3px 4px blue",
112
+ borderWidth: undefined,
113
+ border: "1px solid green",
114
+ focus: {
115
+ border: "1px solid red"
116
+ },
117
+ hover: {
118
+ borderColor: "purple",
119
+ borderWidth: "1px"
120
+ },
121
+ error: {
122
+ border: "1px solid orange"
123
+ }
124
+ },
125
+ option: {
126
+ opacity: "1",
127
+ background: "pink",
128
+ color: "brown",
129
+ selected: {
130
+ background: "yellow",
131
+ color: "cyan"
132
+ },
133
+ disabled: {
134
+ opacity: "0.8"
135
+ }
136
+ },
137
+ placeholder: {
138
+ color: "red"
139
+ },
140
+ input: {
141
+ color: "cyan"
142
+ },
143
+ singleValue: {
144
+ color: "orange"
145
+ }
108
146
  }
109
147
  } satisfies CountrySelectProps
110
148
  };