@bosonprotocol/react-kit 0.36.3-alpha.2 → 0.36.3-alpha.4

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.
Files changed (32) hide show
  1. package/dist/cjs/components/form/Select.d.ts +48 -3
  2. package/dist/cjs/components/form/Select.d.ts.map +1 -1
  3. package/dist/cjs/components/form/Select.js +48 -21
  4. package/dist/cjs/components/form/Select.js.map +1 -1
  5. package/dist/cjs/components/form/Upload/BaseUpload.d.ts.map +1 -1
  6. package/dist/cjs/components/form/index.d.ts +1 -1
  7. package/dist/cjs/components/form/index.d.ts.map +1 -1
  8. package/dist/cjs/components/form/index.js +2 -3
  9. package/dist/cjs/components/form/index.js.map +1 -1
  10. package/dist/cjs/components/form/types.d.ts +2 -34
  11. package/dist/cjs/components/form/types.d.ts.map +1 -1
  12. package/dist/cjs/components/modal/components/common/VariationSelects.js +2 -2
  13. package/dist/cjs/components/modal/components/common/VariationSelects.js.map +1 -1
  14. package/dist/esm/components/form/Select.d.ts +48 -3
  15. package/dist/esm/components/form/Select.d.ts.map +1 -1
  16. package/dist/esm/components/form/Select.js +63 -24
  17. package/dist/esm/components/form/Select.js.map +1 -1
  18. package/dist/esm/components/form/Upload/BaseUpload.d.ts.map +1 -1
  19. package/dist/esm/components/form/index.d.ts +1 -1
  20. package/dist/esm/components/form/index.d.ts.map +1 -1
  21. package/dist/esm/components/form/index.js +1 -1
  22. package/dist/esm/components/form/index.js.map +1 -1
  23. package/dist/esm/components/form/types.d.ts +2 -34
  24. package/dist/esm/components/form/types.d.ts.map +1 -1
  25. package/dist/esm/components/modal/components/common/VariationSelects.js +2 -2
  26. package/dist/esm/components/modal/components/common/VariationSelects.js.map +1 -1
  27. package/package.json +2 -2
  28. package/src/components/form/Select.tsx +167 -52
  29. package/src/components/form/index.ts +1 -1
  30. package/src/components/form/types.ts +4 -44
  31. package/src/components/modal/components/common/VariationSelects.tsx +2 -2
  32. package/src/stories/selects/Select.stories.tsx +9 -2
@@ -1,30 +1,92 @@
1
- /* eslint @typescript-eslint/no-explicit-any: "off" */
2
1
  import React from "react";
3
2
  import { useField } from "formik";
4
- import Select, { GroupBase, StylesConfig } from "react-select";
3
+ import ReactSelect, {
4
+ GroupBase,
5
+ StylesConfig,
6
+ MultiValue,
7
+ SingleValue,
8
+ ActionMeta,
9
+ Props as ReactSelectProps,
10
+ PropsValue,
11
+ CSSObjectWithLabel
12
+ } from "react-select";
13
+ import { CSSProperties } from "react";
5
14
  import { checkIfValueIsEmpty } from "../../lib/object/checkIfValueIsEmpty";
6
15
  import { colors, getCssVar } from "../../theme";
7
16
  import { zIndex } from "../ui/zIndex";
8
-
9
17
  import Error from "./Error";
10
- import type { SelectDataProps, SelectProps } from "./types";
11
18
  import { useFixSelectFont } from "../../hooks/form/useFixSelectFont";
12
- export type { SelectProps } from "./types";
19
+ export { GroupBase } from "react-select";
20
+
21
+ // Base theme type with proper CSS types
22
+ type SelectTheme = Partial<{
23
+ control: Partial<CSSProperties> &
24
+ Partial<{
25
+ disabled: Partial<CSSProperties>;
26
+ hover: Partial<CSSProperties>;
27
+ focus: Partial<CSSProperties>;
28
+ error: Partial<CSSProperties>;
29
+ }>;
30
+ option: Partial<CSSProperties> &
31
+ Partial<{
32
+ selected: Partial<CSSProperties>;
33
+ disabled: Partial<CSSProperties>;
34
+ focus: Partial<CSSProperties>;
35
+ error: Partial<CSSObjectWithLabel>;
36
+ }>;
37
+ placeholder: Partial<CSSProperties> & Partial<{ error: CSSObjectWithLabel }>;
38
+ input: Partial<CSSProperties> & Partial<{ error: CSSObjectWithLabel }>;
39
+ singleValue: Partial<CSSProperties> & Partial<{ error: CSSObjectWithLabel }>;
40
+ multiValue: Partial<CSSProperties> & Partial<{ error: CSSObjectWithLabel }>;
41
+ }>;
42
+ export type DefaultSelectOption = {
43
+ label: string;
44
+ value: string | number;
45
+ disabled?: boolean;
46
+ };
47
+
48
+ // Base option type that all options must extend
49
+ export interface SelectOption extends Record<string, unknown> {
50
+ label: string;
51
+ value: string | number;
52
+ disabled?: boolean;
53
+ }
54
+
55
+ // Type-safe props with conditional types based on IsMulti
56
+ export type SelectProps<
57
+ Option extends SelectOption = DefaultSelectOption,
58
+ IsMulti extends boolean = false,
59
+ Group extends GroupBase<Option> = GroupBase<Option>
60
+ > = Omit<ReactSelectProps<Option, IsMulti, Group>, "styles" | "theme"> & {
61
+ name: string;
62
+ errorMessage?: string;
63
+ label?: string;
64
+ theme?: Partial<SelectTheme>;
65
+ reactSelectTheme?: ReactSelectProps<Option, IsMulti, Group>["theme"];
66
+ };
13
67
 
14
- const customStyles = <Option extends Record<string, unknown> = SelectDataProps>(
68
+ // Custom styles function with proper typing
69
+ const customStyles = <
70
+ Option extends SelectOption,
71
+ IsMulti extends boolean,
72
+ Group extends GroupBase<Option>
73
+ >(
15
74
  error: unknown,
16
- customTheme: SelectProps["theme"]
17
- ): StylesConfig<Option, boolean, GroupBase<Option>> => ({
18
- control: (provided, state: any) => {
19
- const before = state.selectProps.label
20
- ? {
21
- ":before": {
22
- content: `"${state.selectProps.label}"`,
23
- fontWeight: "600",
24
- paddingLeft: "1rem"
75
+ customTheme?: Partial<SelectTheme>
76
+ ): StylesConfig<Option, IsMulti, Group> => ({
77
+ control: (provided, state) => {
78
+ const before =
79
+ "label" in state.selectProps && state.selectProps.label
80
+ ? {
81
+ ":before": {
82
+ content: `"${state.selectProps.label}"`,
83
+ fontWeight: "600",
84
+ paddingLeft: "1rem"
85
+ }
25
86
  }
26
- }
27
- : null;
87
+ : null;
88
+ const defaultBorderColor =
89
+ customTheme?.control?.borderColor || colors.border;
28
90
  return {
29
91
  ...provided,
30
92
  borderRadius: 0,
@@ -32,48 +94,71 @@ const customStyles = <Option extends Record<string, unknown> = SelectDataProps>(
32
94
  boxShadow: "none",
33
95
  background: getCssVar("--background-color"),
34
96
  ...customTheme?.control,
35
- border: state.isFocused
36
- ? (customTheme?.control?.focus?.border ?? `1px solid ${colors.violet}`)
37
- : !checkIfValueIsEmpty(error)
38
- ? (customTheme?.control?.error?.border ??
39
- `1px solid ${colors.orange}`)
40
- : (customTheme?.control?.border ?? `1px solid ${colors.border}`),
41
- ":hover": {
42
- borderColor: colors.violet,
43
- borderWidth: "1px",
44
- ...customTheme?.control?.hover
45
- },
97
+ cursor: state.isDisabled ? "not-allowed" : "default",
98
+ opacity: state.isDisabled
99
+ ? (customTheme?.control?.disabled?.opacity ?? "0.5")
100
+ : (customTheme?.control?.opacity ?? "1"),
101
+ border: state.isDisabled
102
+ ? `1px solid ${defaultBorderColor}`
103
+ : state.isFocused
104
+ ? (customTheme?.control?.focus?.border ??
105
+ `1px solid ${colors.violet}`)
106
+ : !checkIfValueIsEmpty(error)
107
+ ? (customTheme?.control?.error?.border ??
108
+ `1px solid ${colors.orange}`)
109
+ : (customTheme?.control?.border ??
110
+ `1px solid ${defaultBorderColor}`),
111
+ ...(state.isDisabled
112
+ ? {
113
+ ":hover": {
114
+ border: `1px solid ${defaultBorderColor}`
115
+ }
116
+ }
117
+ : {
118
+ ":hover": {
119
+ borderColor: colors.violet,
120
+ borderWidth: "1px",
121
+ ...customTheme?.control?.hover
122
+ }
123
+ }),
46
124
  ...before
47
125
  };
48
126
  },
49
- container: (provided, state: any) => ({
127
+ container: (provided, state) => ({
50
128
  ...provided,
129
+ pointerEvents: "initial",
51
130
  zIndex: state.isFocused ? zIndex.Select + 1 : zIndex.Select,
52
131
  position: "relative",
53
132
  width: "100%"
54
133
  }),
55
- option: (provided, state: any) => {
134
+ option: (provided, state) => {
56
135
  return {
57
136
  ...provided,
58
- cursor: state.isDisabled ? "not-allowed" : "pointer",
137
+ cursor: state.isDisabled ? "not-allowed" : "default",
59
138
  opacity: state.isDisabled
60
139
  ? (customTheme?.option?.disabled?.opacity ?? "0.5")
61
140
  : (customTheme?.option?.opacity ?? "1"),
62
141
  background:
63
- state.isOptionSelected || state.isSelected || state.isFocused
142
+ state.isSelected || state.isFocused
64
143
  ? (customTheme?.option?.selected?.background ?? colors.greyLight)
65
144
  : (customTheme?.option?.background ?? colors.white),
66
- color:
67
- state.isOptionSelected || state.isSelected
68
- ? (customTheme?.option?.selected?.color ?? colors.violet)
69
- : (customTheme?.option?.color ?? colors.black),
145
+ color: state.isSelected
146
+ ? (customTheme?.option?.selected?.color ?? colors.violet)
147
+ : (customTheme?.option?.color ?? colors.black),
70
148
  ...(state.isDisabled && customTheme?.option?.disabled),
71
- ...((state.isOptionSelected || state.isSelected) &&
72
- customTheme?.option?.selected),
149
+ ...(state.isSelected && customTheme?.option?.selected),
73
150
  ...(state.isFocused && customTheme?.option?.focus),
74
151
  ...(!checkIfValueIsEmpty(error) && customTheme?.option?.error)
75
152
  };
76
153
  },
154
+ indicatorsContainer: (provided, state) => {
155
+ return {
156
+ ...provided,
157
+ ...(state.isDisabled && {
158
+ pointerEvents: "none"
159
+ })
160
+ };
161
+ },
77
162
  indicatorSeparator: () => ({
78
163
  display: "none"
79
164
  }),
@@ -95,26 +180,44 @@ const customStyles = <Option extends Record<string, unknown> = SelectDataProps>(
95
180
  ...customTheme?.singleValue,
96
181
  ...(!checkIfValueIsEmpty(error) && customTheme?.singleValue?.error)
97
182
  };
183
+ },
184
+ multiValue: (provided, state) => {
185
+ return {
186
+ ...provided,
187
+ ...customTheme?.multiValue,
188
+ ...(state.isDisabled && {
189
+ pointerEvents: "none"
190
+ }),
191
+ ...(!checkIfValueIsEmpty(error) && customTheme?.multiValue?.error)
192
+ };
98
193
  }
99
194
  });
100
-
101
- export default function SelectComponent<
102
- M extends boolean,
103
- Option extends Record<string, unknown> = SelectDataProps
195
+ export type DefaultSelectProps<IsMulti extends boolean = false> = SelectProps<
196
+ DefaultSelectOption,
197
+ IsMulti,
198
+ GroupBase<DefaultSelectOption>
199
+ >;
200
+ export function Select<
201
+ Option extends SelectOption = DefaultSelectOption,
202
+ IsMulti extends boolean = false,
203
+ Group extends GroupBase<Option> = GroupBase<Option>
104
204
  >({
105
205
  name,
106
206
  options,
107
207
  placeholder = "Choose...",
108
208
  isClearable = false,
109
209
  isSearchable = true,
110
- disabled = false,
210
+ isDisabled = false,
111
211
  errorMessage,
112
212
  onChange,
213
+ onBlur,
113
214
  theme,
215
+ reactSelectTheme,
114
216
  isMulti,
115
217
  ...props
116
- }: SelectProps<M, Option>) {
218
+ }: SelectProps<Option, IsMulti, Group>) {
117
219
  const [field, meta, helpers] = useField(name);
220
+
118
221
  const displayErrorMessage =
119
222
  meta.error && meta.touched && !errorMessage
120
223
  ? meta.error
@@ -126,43 +229,55 @@ export default function SelectComponent<
126
229
  typeof displayErrorMessage === "string" && displayErrorMessage !== "";
127
230
 
128
231
  const handleChange = (
129
- option: Parameters<NonNullable<typeof onChange>>[0],
130
- actionMeta: Parameters<NonNullable<typeof onChange>>[1]
232
+ option: IsMulti extends true ? MultiValue<Option> : SingleValue<Option>,
233
+ actionMeta: ActionMeta<Option>
131
234
  ) => {
235
+ if (isDisabled) {
236
+ return;
237
+ }
132
238
  if (!meta.touched) {
133
239
  helpers.setTouched(true);
134
240
  }
135
241
  helpers.setValue(option);
136
242
  onChange?.(option, actionMeta);
137
243
  };
138
- const handleBlur = () => {
244
+
245
+ const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {
139
246
  if (!meta.touched) {
140
247
  helpers.setTouched(true);
141
248
  }
249
+ field.onBlur(event);
250
+ onBlur?.(event);
142
251
  };
252
+
143
253
  const { jsx, selectClassName } = useFixSelectFont({
144
254
  selectClassName: "boson-select"
145
255
  });
256
+
146
257
  return (
147
258
  <>
148
259
  {jsx}
149
- <Select
150
- styles={customStyles<Option>(displayErrorMessage, theme)}
260
+ <ReactSelect<Option, IsMulti, Group>
261
+ styles={customStyles<Option, IsMulti, Group>(
262
+ displayErrorMessage,
263
+ theme
264
+ )}
151
265
  {...field}
152
266
  {...props}
267
+ theme={reactSelectTheme}
153
268
  className={selectClassName}
154
269
  isMulti={isMulti}
155
270
  placeholder={placeholder}
156
271
  options={options}
157
- value={field.value}
272
+ value={field.value as PropsValue<Option>}
158
273
  onChange={handleChange}
159
274
  onBlur={handleBlur}
160
275
  isSearchable={isSearchable}
161
276
  isClearable={isClearable}
162
- isDisabled={disabled}
277
+ isDisabled={isDisabled}
163
278
  isOptionDisabled={(option) => !!option.disabled}
164
279
  />
165
- <Error display={displayError} message={displayErrorMessage} />{" "}
280
+ <Error display={displayError} message={displayErrorMessage} />
166
281
  </>
167
282
  );
168
283
  }
@@ -6,7 +6,7 @@ export { FormField, FormFieldProps } from "./FormField";
6
6
  export * from "./BaseInput";
7
7
  export { default as Input, InputProps } from "./Input";
8
8
  export { default as Phone, PhoneProps } from "./Phone";
9
- export { default as Select, SelectProps } from "./Select";
9
+ export * from "./Select";
10
10
  export * from "./CountrySelect";
11
11
  export * from "./BaseTagsInput";
12
12
  export * from "./BaseTextArea";
@@ -1,10 +1,5 @@
1
1
  import { ReactNode } from "react";
2
- import {
3
- ActionMeta,
4
- CSSObjectWithLabel,
5
- MultiValue,
6
- SingleValue
7
- } from "react-select";
2
+ import { SingleValue } from "react-select";
8
3
  import { CSSProperties } from "styled-components";
9
4
  import { ImageEditorModalProps } from "./Upload/ImageEditorModal/ImageEditorModal";
10
5
  import type {
@@ -106,8 +101,7 @@ export type SupportedReactSelectProps<
106
101
  Option extends Record<string, unknown> = SelectDataProps
107
102
  > = Pick<
108
103
  StateManagerProps<Option, M extends undefined ? false : boolean>,
109
- | "formatGroupLabel"
110
- | "formatOptionLabel"
104
+ // | "formatOptionLabel"
111
105
  | "menuPlacement"
112
106
  | "menuPosition"
113
107
  | "menuIsOpen"
@@ -126,43 +120,9 @@ export type SupportedReactSelectProps<
126
120
  | "closeMenuOnSelect"
127
121
  | "captureMenuScroll"
128
122
  | "defaultMenuIsOpen"
123
+ | "placeholder"
124
+ | "hideSelectedOptions"
129
125
  >;
130
- export type SelectProps<
131
- M extends boolean | undefined = false,
132
- Option extends Record<string, unknown> = SelectDataProps
133
- > = BaseProps & {
134
- isMulti?: M;
135
- disabled?: boolean;
136
- isClearable?: boolean;
137
- isSearchable?: boolean;
138
- options: Array<Option> | Readonly<Array<Option>>;
139
- errorMessage?: string;
140
- onChange?: (
141
- option: M extends true ? MultiValue<Option> : SingleValue<Option>,
142
- actionMeta?: ActionMeta<Option>
143
- ) => void;
144
- label?: string;
145
- theme?: Partial<{
146
- control: Partial<CSSProperties> &
147
- Partial<{
148
- hover: Partial<CSSProperties>;
149
- focus: Partial<CSSProperties>;
150
- error: Partial<CSSProperties>;
151
- }>;
152
- option: Partial<CSSProperties> &
153
- Partial<{
154
- selected: Partial<CSSProperties>;
155
- disabled: Partial<CSSProperties>;
156
- focus: Partial<CSSProperties>;
157
- error: Partial<CSSObjectWithLabel>;
158
- }>;
159
- placeholder: Partial<CSSProperties> &
160
- Partial<{ error: CSSObjectWithLabel }>;
161
- input: Partial<CSSProperties> & Partial<{ error: CSSObjectWithLabel }>;
162
- singleValue: Partial<CSSProperties> &
163
- Partial<{ error: CSSObjectWithLabel }>;
164
- }>;
165
- } & SupportedReactSelectProps<M, Option>;
166
126
 
167
127
  export type UploadProps = BaseProps & {
168
128
  accept?: string;
@@ -354,7 +354,7 @@ export default function VariationSelects({
354
354
  setLastChangedVariation("color");
355
355
  submitForm();
356
356
  }}
357
- disabled={disabled}
357
+ isDisabled={disabled}
358
358
  />
359
359
  </>
360
360
  )}
@@ -373,7 +373,7 @@ export default function VariationSelects({
373
373
  setLastChangedVariation("size");
374
374
  submitForm();
375
375
  }}
376
- disabled={disabled}
376
+ isDisabled={disabled}
377
377
  />
378
378
  </>
379
379
  )}
@@ -29,7 +29,10 @@ export default {
29
29
  disable: true // remove name input in controls
30
30
  }
31
31
  },
32
- disabled: { control: "boolean" },
32
+ isDisabled: { control: "boolean" },
33
+ isMulti: { control: "boolean" },
34
+ isClearable: { control: "boolean" },
35
+ isSearchable: { control: "boolean" },
33
36
  placeholder: { control: "text" }
34
37
  },
35
38
  decorators: [
@@ -58,7 +61,7 @@ const BASE_ARGS = {
58
61
  name: inputName,
59
62
  options: [
60
63
  { label: "first option", value: "1" },
61
- { label: "second option", value: "2" },
64
+ { label: "second option", value: "2", disabled: true },
62
65
  { label: "third option", value: "3" }
63
66
  ]
64
67
  } as SelectProps;
@@ -114,3 +117,7 @@ export const WithError = {
114
117
  placeholder: "this is a placeholder"
115
118
  } satisfies SelectProps
116
119
  };
120
+
121
+ export const WithLabel = {
122
+ args: { ...BASE_ARGS, label: "my label" } satisfies SelectProps
123
+ };