@zendeskgarden/react-theming 9.4.0 → 9.5.0

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/README.md CHANGED
@@ -42,7 +42,37 @@ complex, depending on your needs:
42
42
  behavior and RTL layout of Garden's tabs component with an alternate visual
43
43
  design (i.e. closer to the look of browser tabs).
44
44
 
45
- ### RTL
45
+ #### Color scheme
46
+
47
+ The `ColorSchemeProvider` and `useColorScheme` hook add the capability for a
48
+ user to persist a preferred system color scheme (`'light'`, `'dark'`, or
49
+ `'system'`). See
50
+ [Storybook](https://zendeskgarden.github.io/react-components/?path=/docs/packages-theming-colorschemeprovider--color-scheme-provider)
51
+ for more details.
52
+
53
+ ```jsx
54
+ import {
55
+ useColorScheme,
56
+ ColorSchemeProvider,
57
+ ThemeProvider,
58
+ DEFAULT_THEME
59
+ } from '@zendeskgarden/react-theming';
60
+
61
+ const ThemedApp = ({ children }) => {
62
+ const { colorScheme } = useColorScheme();
63
+ const theme = { ...DEFAULT_THEME, colors: { ...DEFAULT_THEME.colors, base: colorScheme } };
64
+
65
+ return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
66
+ };
67
+
68
+ const App = ({ children }) => (
69
+ <ColorSchemeProvider>
70
+ <ThemedApp>{children}</ThemedApp>
71
+ </ColorSchemeProvider>
72
+ );
73
+ ```
74
+
75
+ #### RTL
46
76
 
47
77
  ```jsx
48
78
  import { ThemeProvider, DEFAULT_THEME } from '@zendeskgarden/react-theming';
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Copyright Zendesk, Inc.
3
+ *
4
+ * Use of this source code is governed under the Apache License, Version 2.0
5
+ * found at http://www.apache.org/licenses/LICENSE-2.0.
6
+ */
7
+ import React, { createContext, useMemo, useCallback, useState, useEffect } from 'react';
8
+
9
+ const useColorScheme = function (initialState) {
10
+ let colorSchemeKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'color-scheme';
11
+ const localStorage = typeof window === 'undefined' ? undefined : window.localStorage;
12
+ const mediaQuery = typeof window === 'undefined' ? undefined : window.matchMedia('(prefers-color-scheme: dark)');
13
+ const getState = useCallback(_state => {
14
+ const isSystem = _state === 'system' || _state === undefined || _state === null;
15
+ let colorScheme;
16
+ if (isSystem) {
17
+ colorScheme = mediaQuery?.matches ? 'dark' : 'light';
18
+ } else {
19
+ colorScheme = _state;
20
+ }
21
+ return {
22
+ isSystem,
23
+ colorScheme
24
+ };
25
+ }, [mediaQuery?.matches]);
26
+ const [state, setState] = useState(getState(localStorage?.getItem(colorSchemeKey) || initialState));
27
+ useEffect(() => {
28
+ const eventListener = () => {
29
+ setState(getState('system'));
30
+ };
31
+ if (state.isSystem) {
32
+ mediaQuery?.addEventListener('change', eventListener);
33
+ } else {
34
+ mediaQuery?.removeEventListener('change', eventListener);
35
+ }
36
+ return () => {
37
+ mediaQuery?.removeEventListener('change', eventListener);
38
+ };
39
+ }, [getState, state.isSystem, mediaQuery]);
40
+ return {
41
+ isSystem: state.isSystem,
42
+ colorScheme: state.colorScheme,
43
+ setColorScheme: colorScheme => {
44
+ setState(getState(colorScheme));
45
+ localStorage?.setItem(colorSchemeKey, colorScheme);
46
+ }
47
+ };
48
+ };
49
+ const ColorSchemeContext = createContext(undefined);
50
+ const ColorSchemeProvider = _ref => {
51
+ let {
52
+ children,
53
+ colorSchemeKey,
54
+ initialColorScheme
55
+ } = _ref;
56
+ const {
57
+ isSystem,
58
+ colorScheme,
59
+ setColorScheme
60
+ } = useColorScheme(initialColorScheme, colorSchemeKey);
61
+ const contextValue = useMemo(() => ({
62
+ colorScheme,
63
+ isSystem,
64
+ setColorScheme
65
+ }), [isSystem, colorScheme, setColorScheme]);
66
+ return React.createElement(ColorSchemeContext.Provider, {
67
+ value: contextValue
68
+ }, children);
69
+ };
70
+
71
+ export { ColorSchemeContext, ColorSchemeProvider };
package/dist/esm/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  * Use of this source code is governed under the Apache License, Version 2.0
5
5
  * found at http://www.apache.org/licenses/LICENSE-2.0.
6
6
  */
7
+ export { ColorSchemeProvider } from './elements/ColorSchemeProvider.js';
7
8
  export { ThemeProvider } from './elements/ThemeProvider.js';
8
9
  export { default as DEFAULT_THEME } from './elements/theme/index.js';
9
10
  export { default as PALETTE } from './elements/palette/index.js';
@@ -19,6 +20,7 @@ export { default as getLineHeight } from './utils/getLineHeight.js';
19
20
  export { getMenuPosition } from './utils/getMenuPosition.js';
20
21
  export { default as mediaQuery } from './utils/mediaQuery.js';
21
22
  export { default as arrowStyles } from './utils/arrowStyles.js';
23
+ export { useColorScheme } from './utils/useColorScheme.js';
22
24
  export { useDocument } from './utils/useDocument.js';
23
25
  export { useWindow } from './utils/useWindow.js';
24
26
  export { useText } from './utils/useText.js';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Copyright Zendesk, Inc.
3
+ *
4
+ * Use of this source code is governed under the Apache License, Version 2.0
5
+ * found at http://www.apache.org/licenses/LICENSE-2.0.
6
+ */
7
+ import { useContext } from 'react';
8
+ import { ColorSchemeContext } from '../elements/ColorSchemeProvider.js';
9
+
10
+ const useColorScheme = () => {
11
+ const context = useContext(ColorSchemeContext);
12
+ if (!context) {
13
+ throw new Error('Error: this component must be rendered within a <ColorSchemeProvider>.');
14
+ }
15
+ return context;
16
+ };
17
+
18
+ export { useColorScheme };
package/dist/index.cjs.js CHANGED
@@ -20,6 +20,68 @@ var styled__default = /*#__PURE__*/_interopDefault(styled);
20
20
  var get__default = /*#__PURE__*/_interopDefault(get);
21
21
  var memoize__default = /*#__PURE__*/_interopDefault(memoize);
22
22
 
23
+ const useColorScheme$1 = function (initialState) {
24
+ let colorSchemeKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'color-scheme';
25
+ const localStorage = typeof window === 'undefined' ? undefined : window.localStorage;
26
+ const mediaQuery = typeof window === 'undefined' ? undefined : window.matchMedia('(prefers-color-scheme: dark)');
27
+ const getState = React.useCallback(_state => {
28
+ const isSystem = _state === 'system' || _state === undefined || _state === null;
29
+ let colorScheme;
30
+ if (isSystem) {
31
+ colorScheme = mediaQuery?.matches ? 'dark' : 'light';
32
+ } else {
33
+ colorScheme = _state;
34
+ }
35
+ return {
36
+ isSystem,
37
+ colorScheme
38
+ };
39
+ }, [mediaQuery?.matches]);
40
+ const [state, setState] = React.useState(getState(localStorage?.getItem(colorSchemeKey) || initialState));
41
+ React.useEffect(() => {
42
+ const eventListener = () => {
43
+ setState(getState('system'));
44
+ };
45
+ if (state.isSystem) {
46
+ mediaQuery?.addEventListener('change', eventListener);
47
+ } else {
48
+ mediaQuery?.removeEventListener('change', eventListener);
49
+ }
50
+ return () => {
51
+ mediaQuery?.removeEventListener('change', eventListener);
52
+ };
53
+ }, [getState, state.isSystem, mediaQuery]);
54
+ return {
55
+ isSystem: state.isSystem,
56
+ colorScheme: state.colorScheme,
57
+ setColorScheme: colorScheme => {
58
+ setState(getState(colorScheme));
59
+ localStorage?.setItem(colorSchemeKey, colorScheme);
60
+ }
61
+ };
62
+ };
63
+ const ColorSchemeContext = React.createContext(undefined);
64
+ const ColorSchemeProvider = _ref => {
65
+ let {
66
+ children,
67
+ colorSchemeKey,
68
+ initialColorScheme
69
+ } = _ref;
70
+ const {
71
+ isSystem,
72
+ colorScheme,
73
+ setColorScheme
74
+ } = useColorScheme$1(initialColorScheme, colorSchemeKey);
75
+ const contextValue = React.useMemo(() => ({
76
+ colorScheme,
77
+ isSystem,
78
+ setColorScheme
79
+ }), [isSystem, colorScheme, setColorScheme]);
80
+ return React__default.default.createElement(ColorSchemeContext.Provider, {
81
+ value: contextValue
82
+ }, children);
83
+ };
84
+
23
85
  const PALETTE = {
24
86
  black: '#000',
25
87
  white: '#fff',
@@ -1333,6 +1395,14 @@ function arrowStyles(position) {
1333
1395
  return styled.css(["position:relative;&::before,&::after{position:absolute;border-width:inherit;border-style:inherit;background-color:inherit;width:", ";height:", ";content:'';box-sizing:inherit;}&::before{border-color:inherit;clip-path:polygon(100% ", "px,", "px 100%,100% 100%);}&::after{border-color:transparent;background-clip:content-box;clip-path:polygon(100% ", "px,", "px 100%,100% 100%);}", ";", ";"], squareSizePx, squareSizePx, beforeOffset, beforeOffset, afterOffset, afterOffset, positionStyles(position, squareSizeRounded, inset, shift), options.animationModifier && animationStyles$1(position, options.animationModifier));
1334
1396
  }
1335
1397
 
1398
+ const useColorScheme = () => {
1399
+ const context = React.useContext(ColorSchemeContext);
1400
+ if (!context) {
1401
+ throw new Error('Error: this component must be rendered within a <ColorSchemeProvider>.');
1402
+ }
1403
+ return context;
1404
+ };
1405
+
1336
1406
  const useDocument = theme => {
1337
1407
  const [controlledDocument, setControlledDocument] = React.useState();
1338
1408
  React.useEffect(() => {
@@ -1486,6 +1556,7 @@ const MENU_POSITION = ['top', 'right', 'bottom', 'left'];
1486
1556
  const PLACEMENT = ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'end', 'end-top', 'end-bottom', 'start', 'start-top', 'start-bottom'];
1487
1557
 
1488
1558
  exports.ARROW_POSITION = ARROW_POSITION;
1559
+ exports.ColorSchemeProvider = ColorSchemeProvider;
1489
1560
  exports.DEFAULT_THEME = DEFAULT_THEME;
1490
1561
  exports.MENU_POSITION = MENU_POSITION;
1491
1562
  exports.PALETTE = PALETTE;
@@ -1507,6 +1578,7 @@ exports.getMenuPosition = getMenuPosition;
1507
1578
  exports.mediaQuery = mediaQuery;
1508
1579
  exports.menuStyles = menuStyles;
1509
1580
  exports.retrieveComponentStyles = retrieveComponentStyles;
1581
+ exports.useColorScheme = useColorScheme;
1510
1582
  exports.useDocument = useDocument;
1511
1583
  exports.useText = useText;
1512
1584
  exports.useWindow = useWindow;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright Zendesk, Inc.
3
+ *
4
+ * Use of this source code is governed under the Apache License, Version 2.0
5
+ * found at http://www.apache.org/licenses/LICENSE-2.0.
6
+ */
7
+ import React, { PropsWithChildren } from 'react';
8
+ import { IColorSchemeContext, IColorSchemeProviderProps } from '../types';
9
+ export declare const ColorSchemeContext: React.Context<IColorSchemeContext | undefined>;
10
+ export declare const ColorSchemeProvider: ({ children, colorSchemeKey, initialColorScheme }: PropsWithChildren<IColorSchemeProviderProps>) => React.JSX.Element;
@@ -4,6 +4,7 @@
4
4
  * Use of this source code is governed under the Apache License, Version 2.0
5
5
  * found at http://www.apache.org/licenses/LICENSE-2.0.
6
6
  */
7
+ export { ColorSchemeProvider } from './elements/ColorSchemeProvider';
7
8
  export { ThemeProvider } from './elements/ThemeProvider';
8
9
  export { default as DEFAULT_THEME } from './elements/theme';
9
10
  export { default as PALETTE } from './elements/palette';
@@ -19,10 +20,11 @@ export { default as getLineHeight } from './utils/getLineHeight';
19
20
  export { getMenuPosition } from './utils/getMenuPosition';
20
21
  export { default as mediaQuery } from './utils/mediaQuery';
21
22
  export { default as arrowStyles } from './utils/arrowStyles';
23
+ export { useColorScheme } from './utils/useColorScheme';
22
24
  export { useDocument } from './utils/useDocument';
23
25
  export { useWindow } from './utils/useWindow';
24
26
  export { useText } from './utils/useText';
25
27
  export { default as menuStyles } from './utils/menuStyles';
26
28
  export { focusStyles, SELECTOR_FOCUS_VISIBLE } from './utils/focusStyles';
27
29
  export { StyledBaseIcon } from './utils/StyledBaseIcon';
28
- export { ARROW_POSITION, MENU_POSITION, PLACEMENT, type IGardenTheme, type IStyledBaseIconProps, type IThemeProviderProps, type ArrowPosition, type CheckeredBackgroundParameters, type ColorParameters, type FocusBoxShadowParameters, type FocusStylesParameters, type MenuPosition, type Placement } from './types';
30
+ export { ARROW_POSITION, MENU_POSITION, PLACEMENT, type IColorSchemeContext, type IColorSchemeProviderProps, type IGardenTheme, type IStyledBaseIconProps, type IThemeProviderProps, type ArrowPosition, type CheckeredBackgroundParameters, type ColorParameters, type ColorScheme, type FocusBoxShadowParameters, type FocusStylesParameters, type MenuPosition, type Placement } from './types';
@@ -165,6 +165,28 @@ export interface IGardenTheme {
165
165
  xxl: string;
166
166
  };
167
167
  }
168
+ export type ColorScheme = IGardenTheme['colors']['base'] | 'system';
169
+ export interface IColorSchemeContext {
170
+ /** Returns the current color scheme */
171
+ colorScheme: IGardenTheme['colors']['base'];
172
+ /** Indicates whether the `colorScheme` is determined by the system */
173
+ isSystem: boolean;
174
+ /** Provides the mechanism for updating the current color scheme */
175
+ setColorScheme: (colorScheme: ColorScheme) => void;
176
+ }
177
+ export interface IColorSchemeProviderProps {
178
+ /**
179
+ * Sets the initial color scheme and provides `localStorage` persistence (see
180
+ * the `useColorScheme` hook). A user's stored preference overrides this
181
+ * value.
182
+ */
183
+ initialColorScheme?: ColorScheme;
184
+ /**
185
+ * Specifies the key used to store the user's preferred color scheme in
186
+ * `localStorage`
187
+ */
188
+ colorSchemeKey?: string;
189
+ }
168
190
  export interface IThemeProviderProps extends Partial<ThemeProviderProps<IGardenTheme>> {
169
191
  /**
170
192
  * Provides values for component styling. See styled-components
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright Zendesk, Inc.
3
+ *
4
+ * Use of this source code is governed under the Apache License, Version 2.0
5
+ * found at http://www.apache.org/licenses/LICENSE-2.0.
6
+ */
7
+ /**
8
+ * Provides the current color scheme for the context `ThemeProvider`.
9
+ *
10
+ * @returns {object} Current color scheme accessor and mutator.
11
+ */
12
+ export declare const useColorScheme: () => import("..").IColorSchemeContext;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zendeskgarden/react-theming",
3
- "version": "9.4.0",
3
+ "version": "9.5.0",
4
4
  "description": "Theming utilities and components within the Garden Design System",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Zendesk Garden <garden@zendesk.com>",
@@ -47,5 +47,5 @@
47
47
  "access": "public"
48
48
  },
49
49
  "zendeskgarden:src": "src/index.ts",
50
- "gitHead": "02e3f240b6f0c776fdae785254d6fe90cbfc37e4"
50
+ "gitHead": "43546784a9aa985332ddcc6dd09209a11e2c03ff"
51
51
  }