@fibery/ui-kit 1.0.2 → 1.0.5

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,9 +1,10 @@
1
1
  {
2
2
  "name": "@fibery/ui-kit",
3
- "version": "1.0.2",
3
+ "version": "1.0.5",
4
4
  "main": "index.ts",
5
5
  "private": false,
6
6
  "files": [
7
+ "src/antd/styles.ts",
7
8
  "src/Button.d.ts",
8
9
  "src/Button.js",
9
10
  "src/designSystem.ts",
@@ -16,6 +17,7 @@
16
17
  "src/loading-sausage.tsx",
17
18
  "src/icons/**/*.ts*",
18
19
  "src/Select",
20
+ "src/error-alert.tsx",
19
21
  "src/Pallete.ts"
20
22
  ],
21
23
  "license": "UNLICENSED",
@@ -42,14 +44,15 @@
42
44
  "md5": "2.2.1",
43
45
  "moment": "2.20.1",
44
46
  "prop-types": "15.7.2",
47
+ "rc-input-number": "^7.3.4",
45
48
  "rc-menu": "9.3.2",
46
49
  "rc-progress": "3.2.4",
47
50
  "react-color": "2.13.8",
48
51
  "react-day-picker": "7.4.8",
49
52
  "react-popper": "2.2.5",
50
- "react-select": "3.0.8",
53
+ "react-select": "5.3.2",
51
54
  "react-select-country-list": "2.2.1",
52
- "react-windowed-select": "2.0.5",
55
+ "react-windowed-select": "5.0.0",
53
56
  "screenfull": "6.0.1"
54
57
  },
55
58
  "peerDependencies": {
@@ -64,6 +67,7 @@
64
67
  "@types/chroma-js": "2.1.3",
65
68
  "@types/color-hash": "1.0.2",
66
69
  "@types/emoji-mart": "3.0.8",
70
+ "csstype": "3.0.8",
67
71
  "enzyme": "3.11.0",
68
72
  "enzyme-adapter-react-16": "1.15.6",
69
73
  "fs-extra": "10.0.0",
@@ -72,6 +76,7 @@
72
76
  "jest-junit": "13.0.0",
73
77
  "svg-parser": "2.0.4",
74
78
  "svgo": "2.8.0",
79
+ "typescript": "4.7.4",
75
80
  "unist-util-reduce": "0.2.2"
76
81
  },
77
82
  "jest": {
package/src/Button.js CHANGED
@@ -82,7 +82,7 @@ const getMainColor = ({color, dangerous, theme}) => {
82
82
  return color;
83
83
  }
84
84
  if (dangerous) {
85
- return colors.danger;
85
+ return theme.danger;
86
86
  }
87
87
  if (!theme) {
88
88
  return colors.primary;
@@ -90,7 +90,7 @@ const getMainColor = ({color, dangerous, theme}) => {
90
90
  if (theme.mode === "old") {
91
91
  return theme.primary;
92
92
  }
93
- return theme.iconColor;
93
+ return theme.buttonColor;
94
94
  };
95
95
 
96
96
  const getBorderColor = ({mainColor, borderless, primary}) => {
@@ -100,7 +100,7 @@ const getBorderColor = ({mainColor, borderless, primary}) => {
100
100
  if (primary) {
101
101
  return mainColor;
102
102
  }
103
- return getOpacities(mainColor).notSoThin;
103
+ return getOpacities(mainColor).opacity15;
104
104
  };
105
105
 
106
106
  const getHoverBorderColor = ({mainColor, borderless, primary, hoverBgColor}) => {
@@ -115,7 +115,7 @@ const getHoverBorderColor = ({mainColor, borderless, primary, hoverBgColor}) =>
115
115
 
116
116
  const getTextColor = ({mainColor, primary}) => {
117
117
  if (primary) {
118
- return colors.inversedTextColor;
118
+ return themeVars.buttonPrimaryTextColor;
119
119
  }
120
120
  return mainColor;
121
121
  };
@@ -139,7 +139,7 @@ const getHoverBgColor = ({mainColor, borderless, primary}) => {
139
139
 
140
140
  const getSpinnerColor = ({mainColor, primary}) => {
141
141
  if (primary) {
142
- return themeVars.inversedTextColor;
142
+ return colors.inversedTextColor;
143
143
  }
144
144
  return mainColor;
145
145
  };
@@ -354,7 +354,7 @@ export function ActionsButtonCompact({inverted, onClick}) {
354
354
  size={":button-size/small"}
355
355
  Icon={MoreCompactIcon}
356
356
  onClick={onClick}
357
- color={inverted ? colors.inversedTextColor : theme.primary}
357
+ color={inverted ? colors.inversedTextColor : theme.buttonColor}
358
358
  borderless
359
359
  />
360
360
  );
@@ -0,0 +1,87 @@
1
+ import {useCallback, ReactNode} from "react";
2
+ import {css} from "@linaria/core";
3
+ import {components} from "react-windowed-select";
4
+ import RemoveIcon from "../icons/react/Remove";
5
+ import {OptionProps, DropdownIndicatorProps, ClearIndicatorProps, MultiValueRemoveProps, GroupBase} from "react-select";
6
+ import {Button} from "../Button";
7
+ import {themeVars, space} from "../designSystem";
8
+ import ArrowBottom from "../icons/react/ArrowBottom";
9
+ import {dropdownIndicatorSize, expanderStyle} from "./styles";
10
+
11
+ export const Option = <
12
+ TOption,
13
+ IsMulti extends boolean = boolean,
14
+ Group extends GroupBase<TOption> = GroupBase<TOption>
15
+ >({
16
+ children,
17
+ ...props
18
+ }: OptionProps<TOption, IsMulti, Group>) => {
19
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
+ const {onMouseMove, onMouseOver, ...rest} = props.innerProps;
21
+ const newProps = Object.assign(props, {innerProps: rest});
22
+ return <components.Option {...newProps}>{children}</components.Option>;
23
+ };
24
+
25
+ export function DropdownIndicator<
26
+ TOption,
27
+ IsMulti extends boolean = boolean,
28
+ Group extends GroupBase<TOption> = GroupBase<TOption>
29
+ >(props: DropdownIndicatorProps<TOption, IsMulti, Group>) {
30
+ return (
31
+ <div
32
+ className={expanderStyle}
33
+ style={{
34
+ transform: props.selectProps.menuIsOpen ? "rotate(180deg)" : "",
35
+ }}
36
+ >
37
+ <ArrowBottom iconSize={dropdownIndicatorSize} color={themeVars.disabledTextColor} />
38
+ </div>
39
+ );
40
+ }
41
+
42
+ export function ClearIndicator<
43
+ TOption,
44
+ IsMulti extends boolean = boolean,
45
+ Group extends GroupBase<TOption> = GroupBase<TOption>
46
+ >({
47
+ innerProps,
48
+ selectProps,
49
+ }: ClearIndicatorProps<TOption, IsMulti, Group> | MultiValueRemoveProps<TOption, IsMulti, Group>) {
50
+ const onMouseDown = innerProps.onMouseDown;
51
+ const wrappedOnMouseDown = useCallback(
52
+ (e) => {
53
+ onMouseDown?.(e);
54
+ // This prevents false positive triggering in rc-trigger
55
+ e.nativeEvent.stopImmediatePropagation();
56
+ },
57
+ [onMouseDown]
58
+ );
59
+ return (
60
+ <div {...innerProps}>
61
+ <Button
62
+ borderless
63
+ Icon={RemoveIcon}
64
+ disabled={selectProps.isDisabled}
65
+ size={":button-size/super-small"}
66
+ onMouseDown={wrappedOnMouseDown}
67
+ />
68
+ </div>
69
+ );
70
+ }
71
+
72
+ export const NoOptionsMessage = ({children}: {children: ReactNode}) => {
73
+ return (
74
+ <div
75
+ className={css`
76
+ ${{
77
+ paddingLeft: space.l + 2,
78
+ paddingRight: space.l + 2,
79
+ paddingTop: space.m,
80
+ paddingBottom: space.m,
81
+ }}
82
+ `}
83
+ >
84
+ {children}
85
+ </div>
86
+ );
87
+ };
@@ -0,0 +1,210 @@
1
+ import cn from "classnames";
2
+ import {useCallback, forwardRef} from "react";
3
+ import BaseSelect, {
4
+ GroupHeadingProps,
5
+ MenuProps,
6
+ StylesConfig,
7
+ Props as BaseSelectProps,
8
+ GroupBase,
9
+ OptionsOrGroups,
10
+ OptionProps,
11
+ SingleValueProps,
12
+ MultiValueProps,
13
+ } from "react-select";
14
+ import BaseCreatableSelect from "react-select/creatable";
15
+ import WindowedSelect, {components, WindowedMenuList} from "react-windowed-select";
16
+ import {createInlineTheme} from "../designSystem";
17
+ import {useTheme} from "../ThemeProvider";
18
+ import {ClearIndicator, DropdownIndicator, NoOptionsMessage, Option as OptionComponent} from "./components";
19
+ import {componentsStyles, singleLineComponentsStyle} from "./styles";
20
+
21
+ export type {OptionsOrGroups, GroupBase, StylesConfig, OptionProps, SingleValueProps, MultiValueProps};
22
+
23
+ function GroupHeading<
24
+ Option = unknown,
25
+ IsMulti extends boolean = boolean,
26
+ Group extends GroupBase<Option> = GroupBase<Option>
27
+ >(props: GroupHeadingProps<Option, IsMulti, Group>) {
28
+ if (!props.children) {
29
+ return null;
30
+ }
31
+ return <components.GroupHeading {...props} />;
32
+ }
33
+
34
+ function Menu<Option = unknown, IsMulti extends boolean = boolean, Group extends GroupBase<Option> = GroupBase<Option>>(
35
+ props: MenuProps<Option, IsMulti, Group>
36
+ ) {
37
+ const theme = useTheme();
38
+ const {innerProps, className} = props;
39
+ const overridedInnerProps = {...innerProps, style: {...innerProps.style, ...createInlineTheme(theme)}};
40
+ return <components.Menu {...props} innerProps={overridedInnerProps} className={cn(className, "react-select-menu")} />;
41
+ }
42
+
43
+ export {components};
44
+
45
+ export function combineStyles<
46
+ Option,
47
+ IsMulti extends boolean = boolean,
48
+ Group extends GroupBase<Option> = GroupBase<Option>
49
+ >(stylesArray: StylesConfig<Option, IsMulti, Group>[]): StylesConfig<Option, IsMulti, Group> {
50
+ return stylesArray.reduce<StylesConfig<Option, IsMulti, Group>>((acc, style) => {
51
+ Object.keys(style).forEach((key) => {
52
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
53
+ // @ts-ignore
54
+ if (acc[key]) {
55
+ // @ts-ignore
56
+ const temp = acc[key];
57
+ // @ts-ignore
58
+ acc[key] = (provided, state) => style[key](temp(provided, state), state);
59
+ } else {
60
+ // @ts-ignore
61
+ acc[key] = (provided, state) => style[key](provided, state);
62
+ }
63
+ /* eslint-enable @typescript-eslint/ban-ts-comment */
64
+ });
65
+ return acc;
66
+ }, {});
67
+ }
68
+
69
+ export type SelectProps<
70
+ Option = unknown,
71
+ IsMulti extends boolean = boolean,
72
+ Group extends GroupBase<Option> = GroupBase<Option>
73
+ > = Omit<Omit<Omit<BaseSelectProps<Option, IsMulti, Group>, "isMulti">, "backspaceRemovesValue">, "isDisabled"> & {
74
+ virtualized?: boolean;
75
+ isCollectionMode?: IsMulti;
76
+ disabled?: boolean;
77
+ };
78
+ export function SingleRowSelect<
79
+ Option = unknown,
80
+ IsMulti extends boolean = boolean,
81
+ Group extends GroupBase<Option> = GroupBase<Option>
82
+ >({styles = {}, ...rest}: SelectProps<Option, IsMulti, Group>) {
83
+ return (
84
+ <Select<Option, IsMulti, Group>
85
+ {...rest}
86
+ styles={combineStyles<Option, IsMulti, Group>([
87
+ singleLineComponentsStyle as unknown as StylesConfig<Option, IsMulti, Group>,
88
+ styles,
89
+ ])}
90
+ />
91
+ );
92
+ }
93
+
94
+ export const Select = forwardRef(function Select<
95
+ Option = unknown,
96
+ IsMulti extends boolean = boolean,
97
+ Group extends GroupBase<Option> = GroupBase<Option>
98
+ >(
99
+ {
100
+ components,
101
+ isCollectionMode,
102
+ menuPortalTarget,
103
+ onKeyDown,
104
+ styles = {},
105
+ virtualized = true,
106
+ ...rest
107
+ }: SelectProps<Option, IsMulti, Group>,
108
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
+ forwardedRef: any
110
+ ) {
111
+ const handleKeyDown = useCallback(
112
+ (e) => {
113
+ switch (e.key) {
114
+ case "Escape":
115
+ if (rest.menuIsOpen) {
116
+ e.stopPropagation();
117
+ }
118
+ break;
119
+ case "Home":
120
+ e.preventDefault();
121
+ if (e.shiftKey) {
122
+ e.target.selectionStart = 0;
123
+ } else {
124
+ e.target.setSelectionRange(0, 0);
125
+ }
126
+ break;
127
+ case "End": {
128
+ e.preventDefault();
129
+ const len = e.target.value.length;
130
+ if (e.shiftKey) {
131
+ e.target.selectionEnd = len;
132
+ } else {
133
+ e.target.setSelectionRange(len, len);
134
+ }
135
+ break;
136
+ }
137
+ default:
138
+ }
139
+ onKeyDown && onKeyDown(e);
140
+ },
141
+ [onKeyDown, rest.menuIsOpen]
142
+ );
143
+
144
+ const SelectComponent = (virtualized ? WindowedSelect : BaseSelect) as unknown as typeof BaseSelect;
145
+
146
+ return (
147
+ <SelectComponent
148
+ ref={forwardedRef}
149
+ menuPortalTarget={menuPortalTarget}
150
+ menuPlacement={"auto"}
151
+ styles={combineStyles<Option, IsMulti, Group>([
152
+ componentsStyles as unknown as StylesConfig<Option, IsMulti, Group>,
153
+ styles,
154
+ ])}
155
+ isMulti={isCollectionMode}
156
+ backspaceRemovesValue={isCollectionMode}
157
+ tabSelectsValue={false}
158
+ components={{
159
+ Menu,
160
+ DropdownIndicator,
161
+ ClearIndicator,
162
+ Option: OptionComponent,
163
+ MultiValueRemove: ClearIndicator,
164
+ NoOptionsMessage,
165
+ GroupHeading,
166
+ ...components,
167
+ }}
168
+ {...rest}
169
+ onKeyDown={handleKeyDown}
170
+ isDisabled={rest.disabled}
171
+ />
172
+ );
173
+ // eslint-disable-next-line no-use-before-define
174
+ }) as <Option = unknown, IsMulti extends boolean = boolean, Group extends GroupBase<Option> = GroupBase<Option>>(
175
+ // eslint-disable-next-line no-use-before-define,@typescript-eslint/no-explicit-any
176
+ props: SelectProps<Option, IsMulti, Group> & {ref?: any}
177
+ ) => JSX.Element;
178
+
179
+ export function CreatableSelect<
180
+ Option = unknown,
181
+ IsMulti extends boolean = boolean,
182
+ Group extends GroupBase<Option> = GroupBase<Option>
183
+ >({components, isCollectionMode, menuPortalTarget, styles = {}, ...rest}: SelectProps<Option, IsMulti, Group>) {
184
+ return (
185
+ <BaseCreatableSelect
186
+ menuPortalTarget={menuPortalTarget}
187
+ menuPlacement={"auto"}
188
+ styles={combineStyles<Option, IsMulti, Group>([
189
+ componentsStyles as unknown as StylesConfig<Option, IsMulti, Group>,
190
+ styles,
191
+ ])}
192
+ isMulti={isCollectionMode}
193
+ backspaceRemovesValue={isCollectionMode}
194
+ tabSelectsValue={false}
195
+ components={{
196
+ Menu,
197
+ MenuList: WindowedMenuList,
198
+ DropdownIndicator,
199
+ ClearIndicator,
200
+ Option: OptionComponent,
201
+ MultiValueRemove: ClearIndicator,
202
+ NoOptionsMessage,
203
+ GroupHeading,
204
+ ...components,
205
+ }}
206
+ {...rest}
207
+ isDisabled={rest.disabled}
208
+ />
209
+ );
210
+ }
@@ -1,4 +1,6 @@
1
1
  import {css} from "@linaria/core";
2
+ import {Property} from "csstype";
3
+ import type {StylesConfig, CSSObjectWithLabel, ControlProps} from "react-select";
2
4
  import {inputOverrides} from "../antd/styles";
3
5
  import {border, layout, space, textStyles, themeVars, transition} from "../designSystem";
4
6
 
@@ -12,8 +14,12 @@ export const expanderStyle = css`
12
14
  }}
13
15
  `;
14
16
 
15
- function createControlStyle({isSingleLine}) {
16
- const childDivStyle = {
17
+ function createControlStyle({
18
+ isSingleLine,
19
+ }: {
20
+ isSingleLine: boolean;
21
+ }): (base: CSSObjectWithLabel, props: ControlProps) => CSSObjectWithLabel {
22
+ const childDivStyle: CSSObjectWithLabel = {
17
23
  padding: 0,
18
24
  overflow: "visible",
19
25
  };
@@ -22,23 +28,24 @@ function createControlStyle({isSingleLine}) {
22
28
  childDivStyle.flexWrap = "nowrap";
23
29
  }
24
30
 
25
- return (provided, state) => ({
26
- display: "flex",
27
- flexWrap: isSingleLine ? "nowrap" : "wrap",
28
- justifyContent: "space-between",
29
- width: "100%",
30
- minHeight: layout.inputHeight,
31
- ...inputOverrides.main,
32
- ...(state.isFocused && inputOverrides.focus),
33
- ...(state.isDisabled && inputOverrides.disabled),
34
- ":hover": !state.isFocused && !state.isDisabled && inputOverrides.hover,
35
- paddingLeft: space.l,
36
- paddingRight: space.l,
37
- "& > div": childDivStyle,
38
- });
31
+ return (provided, state) =>
32
+ ({
33
+ display: "flex",
34
+ flexWrap: isSingleLine ? "nowrap" : "wrap",
35
+ justifyContent: "space-between",
36
+ width: "100%",
37
+ ...inputOverrides.main,
38
+ minHeight: layout.inputHeight,
39
+ ...(state.isFocused && inputOverrides.focus),
40
+ ...(state.isDisabled && inputOverrides.disabled),
41
+ ":hover": !state.isFocused && !state.isDisabled ? inputOverrides.hover : {},
42
+ paddingLeft: space.l,
43
+ paddingRight: space.l,
44
+ "& > div": childDivStyle,
45
+ } as unknown as CSSObjectWithLabel);
39
46
  }
40
47
 
41
- export const componentsStyles = {
48
+ export const componentsStyles: StylesConfig = {
42
49
  option: (provided, state) => {
43
50
  const disabledStyles = {
44
51
  cursor: "not-allowed",
@@ -83,6 +90,12 @@ export const componentsStyles = {
83
90
  overflow: "visible",
84
91
  color: themeVars.textColor,
85
92
  }),
93
+ valueContainer(provided) {
94
+ return {
95
+ ...provided,
96
+ minWidth: 10,
97
+ };
98
+ },
86
99
  multiValue: (provided) => ({
87
100
  ...provided,
88
101
  color: themeVars.textColor,
@@ -117,7 +130,7 @@ export const componentsStyles = {
117
130
  ...provided,
118
131
  zIndex: 1050,
119
132
  backgroundColor: themeVars.actionMenuInnerBg,
120
- boxShadow: themeVars.actionMenuShadow,
133
+ boxShadow: themeVars.actionMenuShadow as unknown as Property.BoxShadow,
121
134
  borderRadius: border.radius6,
122
135
  }),
123
136
  container: (provided, state) => ({
@@ -126,8 +139,12 @@ export const componentsStyles = {
126
139
  cursor: state.isDisabled ? "not-allowed" : "default",
127
140
  }),
128
141
  indicatorSeparator: () => ({}),
142
+ input: (provided) => ({
143
+ ...provided,
144
+ color: themeVars.textColor,
145
+ }),
129
146
  };
130
147
 
131
- export const singleLineComponentsStyle = {
148
+ export const singleLineComponentsStyle: StylesConfig = {
132
149
  control: createControlStyle({isSingleLine: true}),
133
150
  };
@@ -1,14 +1,103 @@
1
- import {createContext, useContext, ReactNode} from "react";
2
- import {ThemeColors, getThemeColors, colors} from "./designSystem";
1
+ import {createContext, ReactNode, useContext, useEffect, useMemo, useState} from "react";
2
+ import {colors, createInlineTheme, getThemeColors, ThemeColors} from "./designSystem";
3
+ import {
4
+ subscribeOnThemeModeChange,
5
+ subscribeOnThemePreferenceChange,
6
+ ThemeMode,
7
+ ThemePreference,
8
+ } from "./theme-settings";
3
9
 
4
- const FiberyThemeContext = createContext<ThemeColors>(getThemeColors(colors.brandColors.blue));
10
+ const FiberyThemeModeContext = createContext<{themeMode: ThemeMode; themePreference: ThemePreference}>({
11
+ themeMode: "light",
12
+ themePreference: "auto",
13
+ });
14
+ const FiberyThemeContext = createContext<ThemeColors>(getThemeColors(colors.brandColors.blue, "light"));
5
15
 
6
- function FiberyThemeProvider({theme, children}: {theme: ThemeColors; children: ReactNode}): JSX.Element {
16
+ function ThemeProvider({theme, children}: {theme: ThemeColors; children: ReactNode}): JSX.Element {
7
17
  return <FiberyThemeContext.Provider value={theme}>{children}</FiberyThemeContext.Provider>;
8
18
  }
9
19
 
20
+ function ThemeModeProvider({
21
+ oldTheming = true,
22
+ initialThemeMode,
23
+ initialPreference,
24
+ children,
25
+ }: {
26
+ oldTheming: boolean;
27
+ initialThemeMode: ThemeMode;
28
+ initialPreference: ThemePreference;
29
+ children: ReactNode;
30
+ }): JSX.Element {
31
+ const [themeMode, setThemeMode] = useState<ThemeMode>(initialThemeMode);
32
+ const [themePreference, setThemePreference] = useState<ThemePreference>(initialPreference);
33
+ useEffect(() => {
34
+ if (!oldTheming) {
35
+ const unsubscribeMode = subscribeOnThemeModeChange(setThemeMode);
36
+ const unsubscribePreference = subscribeOnThemePreferenceChange(setThemePreference);
37
+ return () => {
38
+ unsubscribeMode();
39
+ unsubscribePreference();
40
+ };
41
+ }
42
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
43
+ return () => {};
44
+ }, [oldTheming]);
45
+ const value = useMemo(
46
+ () => ({
47
+ themeMode,
48
+ themePreference,
49
+ }),
50
+ [themeMode, themePreference]
51
+ );
52
+ return <FiberyThemeModeContext.Provider value={value}>{children}</FiberyThemeModeContext.Provider>;
53
+ }
54
+
10
55
  export function useTheme(): ThemeColors {
11
56
  return useContext(FiberyThemeContext);
12
57
  }
13
58
 
14
- export {FiberyThemeProvider as ThemeProvider};
59
+ export function useThemeMode(): ThemeMode {
60
+ return useContext(FiberyThemeModeContext).themeMode;
61
+ }
62
+
63
+ export function useThemePreference(): ThemePreference {
64
+ return useContext(FiberyThemeModeContext).themePreference;
65
+ }
66
+
67
+ export function applyThemeToRoot(theme: ThemeColors) {
68
+ const inlineTheme = createInlineTheme(theme);
69
+ Object.entries(inlineTheme).forEach(([p, v]) => {
70
+ document.body.style.setProperty(p, v);
71
+ });
72
+ }
73
+
74
+ export function RootThemeProvider({children}: {children: JSX.Element}): JSX.Element {
75
+ const themeMode = useThemeMode();
76
+ const theme = getThemeColors(colors.brandColors.blue, themeMode);
77
+ useEffect(() => {
78
+ applyThemeToRoot(theme);
79
+ }, [theme]);
80
+ return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
81
+ }
82
+
83
+ interface WithThemeModeProps {
84
+ themeMode: ThemeMode;
85
+ }
86
+
87
+ export function withThemeMode<T extends WithThemeModeProps = WithThemeModeProps>(
88
+ WrappedComponent: React.ComponentType<T>
89
+ ) {
90
+ const displayName = WrappedComponent.displayName || WrappedComponent.name || "Component";
91
+
92
+ const ComponentWithThemeMode = (props: Omit<T, keyof WithThemeModeProps>) => {
93
+ const themeMode = useThemeMode();
94
+
95
+ return <WrappedComponent {...(props as T)} themeMode={themeMode} />;
96
+ };
97
+
98
+ ComponentWithThemeMode.displayName = `withThemeMode(${displayName})`;
99
+
100
+ return ComponentWithThemeMode;
101
+ }
102
+
103
+ export {ThemeProvider, ThemeModeProvider};