@wise/components-theming 0.1.2-next-b9f447bdaa.2571 → 0.1.2-next-9ff5b52e35.2579

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.
@@ -5,9 +5,10 @@ declare const componentThemes: readonly [
5
5
  "personal"
6
6
  ];
7
7
  type ComponentTheme = (typeof componentThemes)[number];
8
- type ThemeProviderProps = PropsWithChildren<{
9
- theme: ComponentTheme | undefined;
10
- }>;
8
+ type Theming = {
9
+ theme?: ComponentTheme;
10
+ };
11
+ type ThemeProviderProps = PropsWithChildren<Theming>;
11
12
  declare const ThemeProvider: ({ theme, children }: ThemeProviderProps) => JSX.Element;
12
13
  interface ThemeHookValue {
13
14
  theme: ComponentTheme;
@@ -16,5 +17,5 @@ interface ThemeHookValue {
16
17
  className: string;
17
18
  }
18
19
  declare const useTheme: () => ThemeHookValue;
19
- export type { ComponentTheme };
20
+ export type { ComponentTheme, Theming };
20
21
  export { ThemeProvider, useTheme };
package/dist/cjs/index.js CHANGED
@@ -7,36 +7,6 @@ var modernThemes = ['personal'];
7
7
  var darkThemes = [];
8
8
  var DEFAULT_COMPONENT_THEME = 'light';
9
9
 
10
- var ThemeContext = /*#__PURE__*/react.createContext(DEFAULT_COMPONENT_THEME);
11
- var ThemeProvider = function ThemeProvider(_ref) {
12
- var _ref$theme = _ref.theme,
13
- theme = _ref$theme === void 0 ? DEFAULT_COMPONENT_THEME : _ref$theme,
14
- children = _ref.children;
15
- var themedChildren = react.Children.map(children, function (child) {
16
- var _child$props;
17
-
18
- if (! /*#__PURE__*/react.isValidElement(child)) {
19
- if (process.env.NODE_ENV !== 'production') {
20
- // eslint-disable-next-line no-console
21
- console.warn('[ThemeProvider] Trying to inject `className` to an invalid React element, this will be skipped!');
22
- }
23
-
24
- return child;
25
- } // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
26
-
27
-
28
- var classNames = ["np-theme-".concat(theme), child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.className].filter(Boolean).join(' ');
29
- return /*#__PURE__*/react.cloneElement(child, {
30
- className: classNames // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
-
32
- });
33
- });
34
- return jsxRuntime.jsx(ThemeContext.Provider, {
35
- value: theme,
36
- children: themedChildren
37
- });
38
- };
39
-
40
10
  var isThemeModern = function isThemeModern(theme) {
41
11
  return modernThemes.includes(theme);
42
12
  };
@@ -61,6 +31,58 @@ var useTheme = function useTheme() {
61
31
  }, [theme]);
62
32
  };
63
33
 
34
+ var ThemeScript = /*#__PURE__*/react.memo(function () {
35
+ var _useTheme = useTheme(),
36
+ className = _useTheme.className;
37
+
38
+ var scriptSource = react.useMemo(function () {
39
+ return "!(function () {try {document.documentElement.classList.add('".concat(className, "');} catch (e) {}})();");
40
+ }, [className]); // eslint-disable-next-line react/no-danger
41
+
42
+ return jsxRuntime.jsx("script", {
43
+ dangerouslySetInnerHTML: {
44
+ __html: scriptSource
45
+ }
46
+ });
47
+ }, // Never re-render this component
48
+ function () {
49
+ return true;
50
+ });
51
+
52
+ var ThemedChildren = function ThemedChildren(_ref) {
53
+ var children = _ref.children;
54
+
55
+ var _useTheme = useTheme(),
56
+ className = _useTheme.className;
57
+
58
+ return jsxRuntime.jsx("div", {
59
+ className: className,
60
+ children: children
61
+ });
62
+ };
63
+
64
+ var ThemeContext = /*#__PURE__*/react.createContext(undefined);
65
+ var ThemeProvider = function ThemeProvider(_ref) {
66
+ var _ref$theme = _ref.theme,
67
+ theme = _ref$theme === void 0 ? DEFAULT_COMPONENT_THEME : _ref$theme,
68
+ children = _ref.children;
69
+ var isRootProvider = react.useContext(ThemeContext) === undefined;
70
+
71
+ if (isRootProvider) {
72
+ return jsxRuntime.jsxs(ThemeContext.Provider, {
73
+ value: theme,
74
+ children: [jsxRuntime.jsx(ThemeScript, {}), children]
75
+ });
76
+ }
77
+
78
+ return jsxRuntime.jsx(ThemeContext.Provider, {
79
+ value: theme,
80
+ children: jsxRuntime.jsx(ThemedChildren, {
81
+ children: children
82
+ })
83
+ });
84
+ };
85
+
64
86
  exports.ThemeProvider = ThemeProvider;
65
87
  exports.useTheme = useTheme;
66
88
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/const.ts","../../src/ThemeProvider.tsx","../../src/helpers.ts","../../src/useTheme.ts"],"sourcesContent":["export const componentThemes = ['light', 'personal'] as const;\nexport const modernThemes = ['personal'] as const;\nexport const darkThemes = [] as const;\n\nexport type ComponentTheme = typeof componentThemes[number];\nexport type ModernTheme = typeof modernThemes[number];\nexport type DarkTheme = typeof darkThemes[number];\n\nexport const DEFAULT_COMPONENT_THEME = 'light' as const;\n","import { Children, cloneElement, createContext, isValidElement, PropsWithChildren } from 'react';\n\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME } from './const';\nimport { getThemeClassName } from './helpers';\n\nexport const ThemeContext = createContext<ComponentTheme>(DEFAULT_COMPONENT_THEME);\n\ntype ThemeProviderProps = PropsWithChildren<{\n theme: ComponentTheme | undefined;\n}>;\n\nexport const ThemeProvider = ({\n theme = DEFAULT_COMPONENT_THEME,\n children,\n}: ThemeProviderProps) => {\n const themedChildren = Children.map(children, (child) => {\n if (!isValidElement(child)) {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(\n '[ThemeProvider] Trying to inject `className` to an invalid React element, this will be skipped!',\n );\n }\n return child;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const classNames = [`np-theme-${theme}`, child?.props?.className].filter(Boolean).join(' ');\n\n return cloneElement(child, {\n className: classNames,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any);\n });\n\n return <ThemeContext.Provider value={theme}>{themedChildren}</ThemeContext.Provider>;\n};\n","import { ComponentTheme, DarkTheme, ModernTheme, modernThemes, darkThemes } from './const';\n\nexport const isThemeModern = (theme: ComponentTheme): theme is ModernTheme =>\n modernThemes.includes(theme as ModernTheme);\n\nexport const isThemeDark = (theme: ComponentTheme): theme is DarkTheme =>\n darkThemes.includes(theme as DarkTheme);\n\nexport const getThemeClassName = (theme: ComponentTheme) => `np-theme-${theme}`;\n","import { useContext, useMemo } from 'react';\n\nimport { ThemeContext } from './ThemeProvider';\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME } from './const';\nimport { getThemeClassName, isThemeDark, isThemeModern } from './helpers';\n\ninterface ThemeHookValue {\n theme: ComponentTheme;\n isModern: boolean;\n isDark: boolean;\n className: string;\n}\n\nexport const useTheme = (): ThemeHookValue => {\n const theme = useContext(ThemeContext) ?? DEFAULT_COMPONENT_THEME;\n\n return useMemo(\n () => ({\n theme,\n isModern: isThemeModern(theme),\n isDark: isThemeDark(theme),\n className: getThemeClassName(theme),\n }),\n [theme],\n );\n};\n"],"names":["modernThemes","darkThemes","DEFAULT_COMPONENT_THEME","ThemeContext","createContext","ThemeProvider","theme","children","themedChildren","Children","map","child","isValidElement","process","env","NODE_ENV","console","warn","classNames","props","className","filter","Boolean","join","cloneElement","_jsx","Provider","value","isThemeModern","includes","isThemeDark","getThemeClassName","useTheme","useContext","useMemo","isModern","isDark"],"mappings":";;;;;AACO,IAAMA,YAAY,GAAG,CAAC,UAAD,CAArB,CAAA;AACA,IAAMC,UAAU,GAAG,EAAnB,CAAA;AAMA,IAAMC,uBAAuB,GAAG,OAAhC;;ACHA,IAAMC,YAAY,gBAAGC,mBAAa,CAAiBF,uBAAjB,CAAlC,CAAA;AAMMG,IAAAA,aAAa,GAAG,SAAhBA,aAAgB,CAGJ,IAAA,EAAA;AAAA,EAAA,IAAA,UAAA,GAAA,IAAA,CAFvBC,KAEuB;MAFvBA,KAEuB,2BAFfJ,uBAEe,GAAA,UAAA;MADvBK,QACuB,QADvBA,QACuB,CAAA;EACvB,IAAMC,cAAc,GAAGC,cAAQ,CAACC,GAAT,CAAaH,QAAb,EAAuB,UAACI,KAAD,EAAU;AAAA,IAAA,IAAA,YAAA,CAAA;;AACtD,IAAA,IAAI,eAACC,oBAAc,CAACD,KAAD,CAAnB,EAA4B;AAC1B,MAAA,IAAIE,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAA7B,EAA2C;AACzC;QACAC,OAAO,CAACC,IAAR,CACE,iGADF,CAAA,CAAA;AAGD,OAAA;;AACD,MAAA,OAAON,KAAP,CAAA;AACD,KATqD;;;IAYtD,IAAMO,UAAU,GAAG,CAAaZ,WAAAA,CAAAA,MAAAA,CAAAA,KAAb,GAAsBK,KAAtB,KAAA,IAAA,IAAsBA,KAAtB,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAA,CAAA,YAAA,GAAsBA,KAAK,CAAEQ,KAA7B,MAAsB,IAAA,IAAA,YAAA,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAA,YAAA,CAAcC,SAApC,CAAA,CAA+CC,MAA/C,CAAsDC,OAAtD,CAA+DC,CAAAA,IAA/D,CAAoE,GAApE,CAAnB,CAAA;IAEA,oBAAOC,kBAAY,CAACb,KAAD,EAAQ;MACzBS,SAAS,EAAEF,UADc;;AAAA,KAAR,CAAnB,CAAA;AAID,GAlBsB,CAAvB,CAAA;AAoBA,EAAA,OAAOO,cAACtB,CAAAA,YAAY,CAACuB,QAAd,EAAsB;AAACC,IAAAA,KAAK,EAAErB,KAAR;IAAaC,QAAGC,EAAAA,cAAAA;AAAhB,GAAtB,CAAP,CAAA;AACD;;AClCM,IAAMoB,aAAa,GAAG,SAAhBA,aAAgB,CAACtB,KAAD,EAAA;AAAA,EAAA,OAC3BN,YAAY,CAAC6B,QAAb,CAAsBvB,KAAtB,CAD2B,CAAA;AAAA,CAAtB,CAAA;AAGA,IAAMwB,WAAW,GAAG,SAAdA,WAAc,CAACxB,KAAD,EAAA;AAAA,EAAA,OACzBL,UAAU,CAAC4B,QAAX,CAAoBvB,KAApB,CADyB,CAAA;AAAA,CAApB,CAAA;AAGA,IAAMyB,iBAAiB,GAAG,SAApBA,iBAAoB,CAACzB,KAAD,EAAA;AAAA,EAAA,OAAA,WAAA,CAAA,MAAA,CAAuCA,KAAvC,CAAA,CAAA;AAAA,CAA1B;;ACKM0B,IAAAA,QAAQ,GAAG,SAAXA,QAAW,GAAqB;AAAA,EAAA,IAAA,WAAA,CAAA;;AAC3C,EAAA,IAAM1B,KAAK,GAAG2B,CAAAA,WAAAA,GAAAA,gBAAU,CAAC9B,YAAD,CAAb,qDAA+BD,uBAA1C,CAAA;AAEA,EAAA,OAAOgC,aAAO,CACZ,YAAA;IAAA,OAAO;AACL5B,MAAAA,KAAK,EAALA,KADK;AAEL6B,MAAAA,QAAQ,EAAEP,aAAa,CAACtB,KAAD,CAFlB;AAGL8B,MAAAA,MAAM,EAAEN,WAAW,CAACxB,KAAD,CAHd;MAILc,SAAS,EAAEW,iBAAiB,CAACzB,KAAD,CAAA;KAJ9B,CAAA;AAAA,GADY,EAOZ,CAACA,KAAD,CAPY,CAAd,CAAA;AASD;;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../src/const.ts","../../src/helpers.ts","../../src/useTheme.ts","../../src/ThemeScript.tsx","../../src/ThemedChildren.tsx","../../src/ThemeProvider.tsx"],"sourcesContent":["export const componentThemes = ['light', 'personal'] as const;\nexport const modernThemes = ['personal'] as const;\nexport const darkThemes = [] as const;\n\nexport type ComponentTheme = typeof componentThemes[number];\nexport type ModernTheme = typeof modernThemes[number];\nexport type DarkTheme = typeof darkThemes[number];\n\nexport const DEFAULT_COMPONENT_THEME = 'light' as const;\n\nexport type Theming = {\n theme?: ComponentTheme;\n};\n","import { ComponentTheme, DarkTheme, ModernTheme, modernThemes, darkThemes } from './const';\n\nexport const isThemeModern = (theme: ComponentTheme): theme is ModernTheme =>\n modernThemes.includes(theme as ModernTheme);\n\nexport const isThemeDark = (theme: ComponentTheme): theme is DarkTheme =>\n darkThemes.includes(theme as DarkTheme);\n\nexport const getThemeClassName = (theme: ComponentTheme) => `np-theme-${theme}`;\n","import { useContext, useMemo } from 'react';\n\nimport { ThemeContext } from './ThemeProvider';\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME } from './const';\nimport { getThemeClassName, isThemeDark, isThemeModern } from './helpers';\n\ninterface ThemeHookValue {\n theme: ComponentTheme;\n isModern: boolean;\n isDark: boolean;\n className: string;\n}\n\nexport const useTheme = (): ThemeHookValue => {\n const theme = useContext(ThemeContext) ?? DEFAULT_COMPONENT_THEME;\n\n return useMemo(\n () => ({\n theme,\n isModern: isThemeModern(theme),\n isDark: isThemeDark(theme),\n className: getThemeClassName(theme),\n }),\n [theme],\n );\n};\n","import { memo, useMemo } from 'react';\n\nimport { useTheme } from './useTheme';\n\nexport const ThemeScript = memo(\n () => {\n const { className } = useTheme();\n\n const scriptSource = useMemo(() => {\n return `!(function () {try {document.documentElement.classList.add('${className}');} catch (e) {}})();`;\n }, [className]);\n\n // eslint-disable-next-line react/no-danger\n return <script dangerouslySetInnerHTML={{ __html: scriptSource }} />;\n },\n // Never re-render this component\n () => true,\n);\n","import { ReactNode } from 'react';\n\nimport { useTheme } from './useTheme';\n\ntype ThemedChildrenProps = { children: ReactNode };\n\nexport const ThemedChildren = ({ children }: ThemedChildrenProps) => {\n const { className } = useTheme();\n\n return <div className={className}>{children}</div>;\n};\n","import { createContext, PropsWithChildren, useContext } from 'react';\n\nimport { ThemeScript } from './ThemeScript';\nimport { ThemedChildren } from './ThemedChildren';\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME, Theming } from './const';\n\nexport const ThemeContext = createContext<ComponentTheme | undefined>(undefined);\n\ntype ThemeProviderProps = PropsWithChildren<Theming>;\n\nexport const ThemeProvider = ({\n theme = DEFAULT_COMPONENT_THEME,\n children,\n}: ThemeProviderProps) => {\n const isRootProvider = useContext(ThemeContext) === undefined;\n\n if (isRootProvider) {\n return (\n <ThemeContext.Provider value={theme}>\n <ThemeScript />\n {children}\n </ThemeContext.Provider>\n );\n }\n\n return (\n <ThemeContext.Provider value={theme}>\n <ThemedChildren>{children}</ThemedChildren>\n </ThemeContext.Provider>\n );\n};\n"],"names":["modernThemes","darkThemes","DEFAULT_COMPONENT_THEME","isThemeModern","theme","includes","isThemeDark","getThemeClassName","useTheme","useContext","ThemeContext","useMemo","isModern","isDark","className","ThemeScript","memo","scriptSource","_jsx","dangerouslySetInnerHTML","__html","ThemedChildren","children","createContext","undefined","ThemeProvider","isRootProvider","_jsxs","Provider","value"],"mappings":"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"}
@@ -5,9 +5,10 @@ declare const componentThemes: readonly [
5
5
  "personal"
6
6
  ];
7
7
  type ComponentTheme = (typeof componentThemes)[number];
8
- type ThemeProviderProps = PropsWithChildren<{
9
- theme: ComponentTheme | undefined;
10
- }>;
8
+ type Theming = {
9
+ theme?: ComponentTheme;
10
+ };
11
+ type ThemeProviderProps = PropsWithChildren<Theming>;
11
12
  declare const ThemeProvider: ({ theme, children }: ThemeProviderProps) => JSX.Element;
12
13
  interface ThemeHookValue {
13
14
  theme: ComponentTheme;
@@ -16,5 +17,5 @@ interface ThemeHookValue {
16
17
  className: string;
17
18
  }
18
19
  declare const useTheme: () => ThemeHookValue;
19
- export type { ComponentTheme };
20
+ export type { ComponentTheme, Theming };
20
21
  export { ThemeProvider, useTheme };
package/dist/es/index.js CHANGED
@@ -1,40 +1,10 @@
1
- import { jsx } from 'react/jsx-runtime';
2
- import { Children, isValidElement, cloneElement, createContext, useContext, useMemo } from 'react';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useContext, useMemo, memo, createContext } from 'react';
3
3
 
4
4
  var modernThemes = ['personal'];
5
5
  var darkThemes = [];
6
6
  var DEFAULT_COMPONENT_THEME = 'light';
7
7
 
8
- var ThemeContext = /*#__PURE__*/createContext(DEFAULT_COMPONENT_THEME);
9
- var ThemeProvider = function ThemeProvider(_ref) {
10
- var _ref$theme = _ref.theme,
11
- theme = _ref$theme === void 0 ? DEFAULT_COMPONENT_THEME : _ref$theme,
12
- children = _ref.children;
13
- var themedChildren = Children.map(children, function (child) {
14
- var _child$props;
15
-
16
- if (! /*#__PURE__*/isValidElement(child)) {
17
- if (process.env.NODE_ENV !== 'production') {
18
- // eslint-disable-next-line no-console
19
- console.warn('[ThemeProvider] Trying to inject `className` to an invalid React element, this will be skipped!');
20
- }
21
-
22
- return child;
23
- } // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
24
-
25
-
26
- var classNames = ["np-theme-".concat(theme), child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.className].filter(Boolean).join(' ');
27
- return /*#__PURE__*/cloneElement(child, {
28
- className: classNames // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
-
30
- });
31
- });
32
- return jsx(ThemeContext.Provider, {
33
- value: theme,
34
- children: themedChildren
35
- });
36
- };
37
-
38
8
  var isThemeModern = function isThemeModern(theme) {
39
9
  return modernThemes.includes(theme);
40
10
  };
@@ -59,5 +29,57 @@ var useTheme = function useTheme() {
59
29
  }, [theme]);
60
30
  };
61
31
 
32
+ var ThemeScript = /*#__PURE__*/memo(function () {
33
+ var _useTheme = useTheme(),
34
+ className = _useTheme.className;
35
+
36
+ var scriptSource = useMemo(function () {
37
+ return "!(function () {try {document.documentElement.classList.add('".concat(className, "');} catch (e) {}})();");
38
+ }, [className]); // eslint-disable-next-line react/no-danger
39
+
40
+ return jsx("script", {
41
+ dangerouslySetInnerHTML: {
42
+ __html: scriptSource
43
+ }
44
+ });
45
+ }, // Never re-render this component
46
+ function () {
47
+ return true;
48
+ });
49
+
50
+ var ThemedChildren = function ThemedChildren(_ref) {
51
+ var children = _ref.children;
52
+
53
+ var _useTheme = useTheme(),
54
+ className = _useTheme.className;
55
+
56
+ return jsx("div", {
57
+ className: className,
58
+ children: children
59
+ });
60
+ };
61
+
62
+ var ThemeContext = /*#__PURE__*/createContext(undefined);
63
+ var ThemeProvider = function ThemeProvider(_ref) {
64
+ var _ref$theme = _ref.theme,
65
+ theme = _ref$theme === void 0 ? DEFAULT_COMPONENT_THEME : _ref$theme,
66
+ children = _ref.children;
67
+ var isRootProvider = useContext(ThemeContext) === undefined;
68
+
69
+ if (isRootProvider) {
70
+ return jsxs(ThemeContext.Provider, {
71
+ value: theme,
72
+ children: [jsx(ThemeScript, {}), children]
73
+ });
74
+ }
75
+
76
+ return jsx(ThemeContext.Provider, {
77
+ value: theme,
78
+ children: jsx(ThemedChildren, {
79
+ children: children
80
+ })
81
+ });
82
+ };
83
+
62
84
  export { ThemeProvider, useTheme };
63
85
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/const.ts","../../src/ThemeProvider.tsx","../../src/helpers.ts","../../src/useTheme.ts"],"sourcesContent":["export const componentThemes = ['light', 'personal'] as const;\nexport const modernThemes = ['personal'] as const;\nexport const darkThemes = [] as const;\n\nexport type ComponentTheme = typeof componentThemes[number];\nexport type ModernTheme = typeof modernThemes[number];\nexport type DarkTheme = typeof darkThemes[number];\n\nexport const DEFAULT_COMPONENT_THEME = 'light' as const;\n","import { Children, cloneElement, createContext, isValidElement, PropsWithChildren } from 'react';\n\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME } from './const';\nimport { getThemeClassName } from './helpers';\n\nexport const ThemeContext = createContext<ComponentTheme>(DEFAULT_COMPONENT_THEME);\n\ntype ThemeProviderProps = PropsWithChildren<{\n theme: ComponentTheme | undefined;\n}>;\n\nexport const ThemeProvider = ({\n theme = DEFAULT_COMPONENT_THEME,\n children,\n}: ThemeProviderProps) => {\n const themedChildren = Children.map(children, (child) => {\n if (!isValidElement(child)) {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(\n '[ThemeProvider] Trying to inject `className` to an invalid React element, this will be skipped!',\n );\n }\n return child;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const classNames = [`np-theme-${theme}`, child?.props?.className].filter(Boolean).join(' ');\n\n return cloneElement(child, {\n className: classNames,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any);\n });\n\n return <ThemeContext.Provider value={theme}>{themedChildren}</ThemeContext.Provider>;\n};\n","import { ComponentTheme, DarkTheme, ModernTheme, modernThemes, darkThemes } from './const';\n\nexport const isThemeModern = (theme: ComponentTheme): theme is ModernTheme =>\n modernThemes.includes(theme as ModernTheme);\n\nexport const isThemeDark = (theme: ComponentTheme): theme is DarkTheme =>\n darkThemes.includes(theme as DarkTheme);\n\nexport const getThemeClassName = (theme: ComponentTheme) => `np-theme-${theme}`;\n","import { useContext, useMemo } from 'react';\n\nimport { ThemeContext } from './ThemeProvider';\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME } from './const';\nimport { getThemeClassName, isThemeDark, isThemeModern } from './helpers';\n\ninterface ThemeHookValue {\n theme: ComponentTheme;\n isModern: boolean;\n isDark: boolean;\n className: string;\n}\n\nexport const useTheme = (): ThemeHookValue => {\n const theme = useContext(ThemeContext) ?? DEFAULT_COMPONENT_THEME;\n\n return useMemo(\n () => ({\n theme,\n isModern: isThemeModern(theme),\n isDark: isThemeDark(theme),\n className: getThemeClassName(theme),\n }),\n [theme],\n );\n};\n"],"names":["modernThemes","darkThemes","DEFAULT_COMPONENT_THEME","ThemeContext","createContext","ThemeProvider","theme","children","themedChildren","Children","map","child","isValidElement","process","env","NODE_ENV","console","warn","classNames","props","className","filter","Boolean","join","cloneElement","_jsx","Provider","value","isThemeModern","includes","isThemeDark","getThemeClassName","useTheme","useContext","useMemo","isModern","isDark"],"mappings":";;;AACO,IAAMA,YAAY,GAAG,CAAC,UAAD,CAArB,CAAA;AACA,IAAMC,UAAU,GAAG,EAAnB,CAAA;AAMA,IAAMC,uBAAuB,GAAG,OAAhC;;ACHA,IAAMC,YAAY,gBAAGC,aAAa,CAAiBF,uBAAjB,CAAlC,CAAA;AAMMG,IAAAA,aAAa,GAAG,SAAhBA,aAAgB,CAGJ,IAAA,EAAA;AAAA,EAAA,IAAA,UAAA,GAAA,IAAA,CAFvBC,KAEuB;MAFvBA,KAEuB,2BAFfJ,uBAEe,GAAA,UAAA;MADvBK,QACuB,QADvBA,QACuB,CAAA;EACvB,IAAMC,cAAc,GAAGC,QAAQ,CAACC,GAAT,CAAaH,QAAb,EAAuB,UAACI,KAAD,EAAU;AAAA,IAAA,IAAA,YAAA,CAAA;;AACtD,IAAA,IAAI,eAACC,cAAc,CAACD,KAAD,CAAnB,EAA4B;AAC1B,MAAA,IAAIE,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAA7B,EAA2C;AACzC;QACAC,OAAO,CAACC,IAAR,CACE,iGADF,CAAA,CAAA;AAGD,OAAA;;AACD,MAAA,OAAON,KAAP,CAAA;AACD,KATqD;;;IAYtD,IAAMO,UAAU,GAAG,CAAaZ,WAAAA,CAAAA,MAAAA,CAAAA,KAAb,GAAsBK,KAAtB,KAAA,IAAA,IAAsBA,KAAtB,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAA,CAAA,YAAA,GAAsBA,KAAK,CAAEQ,KAA7B,MAAsB,IAAA,IAAA,YAAA,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAA,YAAA,CAAcC,SAApC,CAAA,CAA+CC,MAA/C,CAAsDC,OAAtD,CAA+DC,CAAAA,IAA/D,CAAoE,GAApE,CAAnB,CAAA;IAEA,oBAAOC,YAAY,CAACb,KAAD,EAAQ;MACzBS,SAAS,EAAEF,UADc;;AAAA,KAAR,CAAnB,CAAA;AAID,GAlBsB,CAAvB,CAAA;AAoBA,EAAA,OAAOO,GAACtB,CAAAA,YAAY,CAACuB,QAAd,EAAsB;AAACC,IAAAA,KAAK,EAAErB,KAAR;IAAaC,QAAGC,EAAAA,cAAAA;AAAhB,GAAtB,CAAP,CAAA;AACD;;AClCM,IAAMoB,aAAa,GAAG,SAAhBA,aAAgB,CAACtB,KAAD,EAAA;AAAA,EAAA,OAC3BN,YAAY,CAAC6B,QAAb,CAAsBvB,KAAtB,CAD2B,CAAA;AAAA,CAAtB,CAAA;AAGA,IAAMwB,WAAW,GAAG,SAAdA,WAAc,CAACxB,KAAD,EAAA;AAAA,EAAA,OACzBL,UAAU,CAAC4B,QAAX,CAAoBvB,KAApB,CADyB,CAAA;AAAA,CAApB,CAAA;AAGA,IAAMyB,iBAAiB,GAAG,SAApBA,iBAAoB,CAACzB,KAAD,EAAA;AAAA,EAAA,OAAA,WAAA,CAAA,MAAA,CAAuCA,KAAvC,CAAA,CAAA;AAAA,CAA1B;;ACKM0B,IAAAA,QAAQ,GAAG,SAAXA,QAAW,GAAqB;AAAA,EAAA,IAAA,WAAA,CAAA;;AAC3C,EAAA,IAAM1B,KAAK,GAAG2B,CAAAA,WAAAA,GAAAA,UAAU,CAAC9B,YAAD,CAAb,qDAA+BD,uBAA1C,CAAA;AAEA,EAAA,OAAOgC,OAAO,CACZ,YAAA;IAAA,OAAO;AACL5B,MAAAA,KAAK,EAALA,KADK;AAEL6B,MAAAA,QAAQ,EAAEP,aAAa,CAACtB,KAAD,CAFlB;AAGL8B,MAAAA,MAAM,EAAEN,WAAW,CAACxB,KAAD,CAHd;MAILc,SAAS,EAAEW,iBAAiB,CAACzB,KAAD,CAAA;KAJ9B,CAAA;AAAA,GADY,EAOZ,CAACA,KAAD,CAPY,CAAd,CAAA;AASD;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../src/const.ts","../../src/helpers.ts","../../src/useTheme.ts","../../src/ThemeScript.tsx","../../src/ThemedChildren.tsx","../../src/ThemeProvider.tsx"],"sourcesContent":["export const componentThemes = ['light', 'personal'] as const;\nexport const modernThemes = ['personal'] as const;\nexport const darkThemes = [] as const;\n\nexport type ComponentTheme = typeof componentThemes[number];\nexport type ModernTheme = typeof modernThemes[number];\nexport type DarkTheme = typeof darkThemes[number];\n\nexport const DEFAULT_COMPONENT_THEME = 'light' as const;\n\nexport type Theming = {\n theme?: ComponentTheme;\n};\n","import { ComponentTheme, DarkTheme, ModernTheme, modernThemes, darkThemes } from './const';\n\nexport const isThemeModern = (theme: ComponentTheme): theme is ModernTheme =>\n modernThemes.includes(theme as ModernTheme);\n\nexport const isThemeDark = (theme: ComponentTheme): theme is DarkTheme =>\n darkThemes.includes(theme as DarkTheme);\n\nexport const getThemeClassName = (theme: ComponentTheme) => `np-theme-${theme}`;\n","import { useContext, useMemo } from 'react';\n\nimport { ThemeContext } from './ThemeProvider';\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME } from './const';\nimport { getThemeClassName, isThemeDark, isThemeModern } from './helpers';\n\ninterface ThemeHookValue {\n theme: ComponentTheme;\n isModern: boolean;\n isDark: boolean;\n className: string;\n}\n\nexport const useTheme = (): ThemeHookValue => {\n const theme = useContext(ThemeContext) ?? DEFAULT_COMPONENT_THEME;\n\n return useMemo(\n () => ({\n theme,\n isModern: isThemeModern(theme),\n isDark: isThemeDark(theme),\n className: getThemeClassName(theme),\n }),\n [theme],\n );\n};\n","import { memo, useMemo } from 'react';\n\nimport { useTheme } from './useTheme';\n\nexport const ThemeScript = memo(\n () => {\n const { className } = useTheme();\n\n const scriptSource = useMemo(() => {\n return `!(function () {try {document.documentElement.classList.add('${className}');} catch (e) {}})();`;\n }, [className]);\n\n // eslint-disable-next-line react/no-danger\n return <script dangerouslySetInnerHTML={{ __html: scriptSource }} />;\n },\n // Never re-render this component\n () => true,\n);\n","import { ReactNode } from 'react';\n\nimport { useTheme } from './useTheme';\n\ntype ThemedChildrenProps = { children: ReactNode };\n\nexport const ThemedChildren = ({ children }: ThemedChildrenProps) => {\n const { className } = useTheme();\n\n return <div className={className}>{children}</div>;\n};\n","import { createContext, PropsWithChildren, useContext } from 'react';\n\nimport { ThemeScript } from './ThemeScript';\nimport { ThemedChildren } from './ThemedChildren';\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME, Theming } from './const';\n\nexport const ThemeContext = createContext<ComponentTheme | undefined>(undefined);\n\ntype ThemeProviderProps = PropsWithChildren<Theming>;\n\nexport const ThemeProvider = ({\n theme = DEFAULT_COMPONENT_THEME,\n children,\n}: ThemeProviderProps) => {\n const isRootProvider = useContext(ThemeContext) === undefined;\n\n if (isRootProvider) {\n return (\n <ThemeContext.Provider value={theme}>\n <ThemeScript />\n {children}\n </ThemeContext.Provider>\n );\n }\n\n return (\n <ThemeContext.Provider value={theme}>\n <ThemedChildren>{children}</ThemedChildren>\n </ThemeContext.Provider>\n );\n};\n"],"names":["modernThemes","darkThemes","DEFAULT_COMPONENT_THEME","isThemeModern","theme","includes","isThemeDark","getThemeClassName","useTheme","useContext","ThemeContext","useMemo","isModern","isDark","className","ThemeScript","memo","scriptSource","_jsx","dangerouslySetInnerHTML","__html","ThemedChildren","children","createContext","undefined","ThemeProvider","isRootProvider","_jsxs","Provider","value"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wise/components-theming",
3
- "version": "0.1.2-next-b9f447bdaa.2571+b9f447bdaa",
3
+ "version": "0.1.2-next-9ff5b52e35.2579+9ff5b52e35",
4
4
  "description": "Provides theming support for the Wise Design system components",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -43,5 +43,5 @@
43
43
  "dist/",
44
44
  "src/"
45
45
  ],
46
- "gitHead": "b9f447bdaae7d30c977407848eb645c07cc81bcd"
46
+ "gitHead": "9ff5b52e35d76e7718fd67c8b0d59cb75885da12"
47
47
  }
@@ -1,7 +1,8 @@
1
- import { render, screen } from '@testing-library/react';
1
+ import { render, screen, act, waitFor } from '@testing-library/react';
2
2
  import React from 'react';
3
3
 
4
4
  import { ThemeProvider } from './ThemeProvider';
5
+
5
6
  import {} from './const';
6
7
 
7
8
  const InValidElement = React.createContext<undefined>(undefined).Provider;
@@ -14,28 +15,6 @@ const ComponentWithClassname = ({
14
15
  }) => <div className={className} {...rest} />;
15
16
 
16
17
  describe('ThemeProvider', () => {
17
- it('appends theme to children', () => {
18
- render(
19
- <ThemeProvider theme="light">
20
- <div>children 1</div>
21
- <div>children 2</div>
22
- </ThemeProvider>,
23
- );
24
-
25
- expect(screen.getByText('children 1')).toHaveClass('np-theme-light');
26
- expect(screen.getByText('children 2')).toHaveClass('np-theme-light');
27
- });
28
-
29
- it('concatenate className with children', () => {
30
- render(
31
- <ThemeProvider theme="light">
32
- <div className="other class names">children</div>
33
- </ThemeProvider>,
34
- );
35
-
36
- expect(screen.getByText('children')).toHaveClass('np-theme-light other class names');
37
- });
38
-
39
18
  it('can nest theme providers', () => {
40
19
  render(
41
20
  <ThemeProvider theme="light">
@@ -49,37 +28,29 @@ describe('ThemeProvider', () => {
49
28
  </ThemeProvider>,
50
29
  );
51
30
 
52
- expect(screen.getAllByText('light')[0]).toHaveClass('np-theme-light');
53
- expect(screen.getByText('personal')).toHaveClass('np-theme-personal');
54
- expect(screen.getAllByText('light')[1]).toHaveClass('np-theme-light');
55
- });
56
-
57
- it('can append className to a component', () => {
58
- render(
59
- <ThemeProvider theme="personal">
60
- <ComponentWithClassname data-testid="button">Click me!</ComponentWithClassname>
61
- </ThemeProvider>,
62
- );
63
-
64
- expect(screen.getByTestId('button')).toHaveClass('np-theme-personal');
31
+ // The first provider will not have a className attached to it
32
+ expect(screen.getAllByText('light')[0]).not.toHaveClass('np-theme-light');
33
+ // eslint-disable-next-line testing-library/no-node-access
34
+ expect(screen.getAllByText('light')[0].parentElement).not.toHaveClass('np-theme-light');
35
+ // eslint-disable-next-line testing-library/no-node-access
36
+ expect(screen.getByText('personal').parentElement).toHaveClass('np-theme-personal');
37
+ // eslint-disable-next-line testing-library/no-node-access
38
+ expect(screen.getAllByText('light')[1].parentElement).toHaveClass('np-theme-light');
65
39
  });
66
40
 
67
- it('displays console warning when children is not a valid React element', () => {
68
- jest.spyOn(console, 'warn').mockImplementation(() => jest.fn());
69
-
70
- render(
71
- <ThemeProvider theme="personal">
72
- <InValidElement value={undefined}>
73
- <ComponentWithClassname data-testid="button">Click me!</ComponentWithClassname>
74
- </InValidElement>
75
- ,
41
+ it('matches snapshot', () => {
42
+ const { asFragment } = render(
43
+ <ThemeProvider theme="light">
44
+ <div>light</div>
45
+ <ThemeProvider theme="personal">
46
+ <div>personal</div>
47
+ <ThemeProvider theme="light">
48
+ <div>light</div>
49
+ </ThemeProvider>
50
+ </ThemeProvider>
76
51
  </ThemeProvider>,
77
52
  );
78
53
 
79
- expect(screen.getByTestId('button')).not.toHaveClass('np-theme-personal');
80
- // eslint-disable-next-line no-console
81
- expect(console.warn).toHaveBeenCalledWith(
82
- '[ThemeProvider] Trying to inject `className` to an invalid React element, this will be skipped!',
83
- );
54
+ expect(asFragment()).toMatchSnapshot();
84
55
  });
85
56
  });
@@ -1,37 +1,31 @@
1
- import { Children, cloneElement, createContext, isValidElement, PropsWithChildren } from 'react';
1
+ import { createContext, PropsWithChildren, useContext } from 'react';
2
2
 
3
- import { ComponentTheme, DEFAULT_COMPONENT_THEME } from './const';
4
- import { getThemeClassName } from './helpers';
3
+ import { ThemeScript } from './ThemeScript';
4
+ import { ThemedChildren } from './ThemedChildren';
5
+ import { ComponentTheme, DEFAULT_COMPONENT_THEME, Theming } from './const';
5
6
 
6
- export const ThemeContext = createContext<ComponentTheme>(DEFAULT_COMPONENT_THEME);
7
+ export const ThemeContext = createContext<ComponentTheme | undefined>(undefined);
7
8
 
8
- type ThemeProviderProps = PropsWithChildren<{
9
- theme: ComponentTheme | undefined;
10
- }>;
9
+ type ThemeProviderProps = PropsWithChildren<Theming>;
11
10
 
12
11
  export const ThemeProvider = ({
13
12
  theme = DEFAULT_COMPONENT_THEME,
14
13
  children,
15
14
  }: ThemeProviderProps) => {
16
- const themedChildren = Children.map(children, (child) => {
17
- if (!isValidElement(child)) {
18
- if (process.env.NODE_ENV !== 'production') {
19
- // eslint-disable-next-line no-console
20
- console.warn(
21
- '[ThemeProvider] Trying to inject `className` to an invalid React element, this will be skipped!',
22
- );
23
- }
24
- return child;
25
- }
15
+ const isRootProvider = useContext(ThemeContext) === undefined;
26
16
 
27
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
28
- const classNames = [`np-theme-${theme}`, child?.props?.className].filter(Boolean).join(' ');
17
+ if (isRootProvider) {
18
+ return (
19
+ <ThemeContext.Provider value={theme}>
20
+ <ThemeScript />
21
+ {children}
22
+ </ThemeContext.Provider>
23
+ );
24
+ }
29
25
 
30
- return cloneElement(child, {
31
- className: classNames,
32
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
- } as any);
34
- });
35
-
36
- return <ThemeContext.Provider value={theme}>{themedChildren}</ThemeContext.Provider>;
26
+ return (
27
+ <ThemeContext.Provider value={theme}>
28
+ <ThemedChildren>{children}</ThemedChildren>
29
+ </ThemeContext.Provider>
30
+ );
37
31
  };
@@ -0,0 +1,18 @@
1
+ import { memo, useMemo } from 'react';
2
+
3
+ import { useTheme } from './useTheme';
4
+
5
+ export const ThemeScript = memo(
6
+ () => {
7
+ const { className } = useTheme();
8
+
9
+ const scriptSource = useMemo(() => {
10
+ return `!(function () {try {document.documentElement.classList.add('${className}');} catch (e) {}})();`;
11
+ }, [className]);
12
+
13
+ // eslint-disable-next-line react/no-danger
14
+ return <script dangerouslySetInnerHTML={{ __html: scriptSource }} />;
15
+ },
16
+ // Never re-render this component
17
+ () => true,
18
+ );
@@ -0,0 +1,11 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ import { useTheme } from './useTheme';
4
+
5
+ type ThemedChildrenProps = { children: ReactNode };
6
+
7
+ export const ThemedChildren = ({ children }: ThemedChildrenProps) => {
8
+ const { className } = useTheme();
9
+
10
+ return <div className={className}>{children}</div>;
11
+ };
@@ -0,0 +1,26 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ThemeProvider matches snapshot 1`] = `
4
+ <DocumentFragment>
5
+ <script>
6
+ !(function () {try {document.documentElement.classList.add('np-theme-light');} catch (e) {}})();
7
+ </script>
8
+ <div>
9
+ light
10
+ </div>
11
+ <div
12
+ class="np-theme-personal"
13
+ >
14
+ <div>
15
+ personal
16
+ </div>
17
+ <div
18
+ class="np-theme-light"
19
+ >
20
+ <div>
21
+ light
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </DocumentFragment>
26
+ `;
package/src/const.ts CHANGED
@@ -7,3 +7,7 @@ export type ModernTheme = typeof modernThemes[number];
7
7
  export type DarkTheme = typeof darkThemes[number];
8
8
 
9
9
  export const DEFAULT_COMPONENT_THEME = 'light' as const;
10
+
11
+ export type Theming = {
12
+ theme?: ComponentTheme;
13
+ };
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { ComponentTheme } from './const';
1
+ export type { ComponentTheme, Theming } from './const';
2
2
 
3
3
  export { ThemeProvider } from './ThemeProvider';
4
4
  export { useTheme } from './useTheme';