@zendeskgarden/react-theming 9.4.0 → 9.5.1

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,75 @@
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, useEffect, useCallback, useState } from 'react';
8
+ import PropTypes from 'prop-types';
9
+
10
+ const mediaQuery = typeof window === 'undefined' ? undefined : window.matchMedia('(prefers-color-scheme: dark)');
11
+ const useColorScheme = (initialState, colorSchemeKey) => {
12
+ const localStorage = typeof window === 'undefined' ? undefined : window.localStorage;
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
+ }, []);
26
+ const [state, setState] = useState(getState(localStorage?.getItem(colorSchemeKey) || initialState));
27
+ return {
28
+ isSystem: state.isSystem,
29
+ colorScheme: state.colorScheme,
30
+ setColorScheme: colorScheme => {
31
+ setState(getState(colorScheme));
32
+ localStorage?.setItem(colorSchemeKey, colorScheme);
33
+ }
34
+ };
35
+ };
36
+ const ColorSchemeContext = createContext(undefined);
37
+ const ColorSchemeProvider = _ref => {
38
+ let {
39
+ children,
40
+ colorSchemeKey = 'color-scheme',
41
+ initialColorScheme = 'system'
42
+ } = _ref;
43
+ const {
44
+ isSystem,
45
+ colorScheme,
46
+ setColorScheme
47
+ } = useColorScheme(initialColorScheme, colorSchemeKey);
48
+ const contextValue = useMemo(() => ({
49
+ colorScheme,
50
+ isSystem,
51
+ setColorScheme
52
+ }), [isSystem, colorScheme, setColorScheme]);
53
+ useEffect(() => {
54
+ const eventListener = () => {
55
+ setColorScheme('system');
56
+ };
57
+ if (isSystem) {
58
+ mediaQuery?.addEventListener('change', eventListener);
59
+ } else {
60
+ mediaQuery?.removeEventListener('change', eventListener);
61
+ }
62
+ return () => {
63
+ mediaQuery?.removeEventListener('change', eventListener);
64
+ };
65
+ }, [isSystem, setColorScheme]);
66
+ return React.createElement(ColorSchemeContext.Provider, {
67
+ value: contextValue
68
+ }, children);
69
+ };
70
+ ColorSchemeProvider.propTypes = {
71
+ colorSchemeKey: PropTypes.string,
72
+ initialColorScheme: PropTypes.oneOf(['light', 'dark', 'system'])
73
+ };
74
+
75
+ 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';
@@ -4,7 +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
- import { getScale, parseToRgba } from 'color2k';
7
+ import { getScale, toHex as toHex$1, parseToRgba } from 'color2k';
8
8
  import { getContrast, rgba, darken, lighten } from 'polished';
9
9
  import get from 'lodash.get';
10
10
  import memoize from 'lodash.memoize';
@@ -97,7 +97,7 @@ const generateColorScale = memoize(color => {
97
97
  const colors = [];
98
98
  const contrastRatios = [];
99
99
  for (let i = 0; i <= scaleSize; i++) {
100
- const _color = scale(i);
100
+ const _color = toHex$1(scale(i));
101
101
  colors.push(_color);
102
102
  contrastRatios.push(getContrast('#FFF', _color));
103
103
  }
@@ -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
@@ -7,6 +7,7 @@
7
7
  'use strict';
8
8
 
9
9
  var React = require('react');
10
+ var PropTypes = require('prop-types');
10
11
  var styled = require('styled-components');
11
12
  var color2k = require('color2k');
12
13
  var polished = require('polished');
@@ -16,10 +17,76 @@ var memoize = require('lodash.memoize');
16
17
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
17
18
 
18
19
  var React__default = /*#__PURE__*/_interopDefault(React);
20
+ var PropTypes__default = /*#__PURE__*/_interopDefault(PropTypes);
19
21
  var styled__default = /*#__PURE__*/_interopDefault(styled);
20
22
  var get__default = /*#__PURE__*/_interopDefault(get);
21
23
  var memoize__default = /*#__PURE__*/_interopDefault(memoize);
22
24
 
25
+ const mediaQuery$1 = typeof window === 'undefined' ? undefined : window.matchMedia('(prefers-color-scheme: dark)');
26
+ const useColorScheme$1 = (initialState, colorSchemeKey) => {
27
+ const localStorage = typeof window === 'undefined' ? undefined : window.localStorage;
28
+ const getState = React.useCallback(_state => {
29
+ const isSystem = _state === 'system' || _state === undefined || _state === null;
30
+ let colorScheme;
31
+ if (isSystem) {
32
+ colorScheme = mediaQuery$1?.matches ? 'dark' : 'light';
33
+ } else {
34
+ colorScheme = _state;
35
+ }
36
+ return {
37
+ isSystem,
38
+ colorScheme
39
+ };
40
+ }, []);
41
+ const [state, setState] = React.useState(getState(localStorage?.getItem(colorSchemeKey) || initialState));
42
+ return {
43
+ isSystem: state.isSystem,
44
+ colorScheme: state.colorScheme,
45
+ setColorScheme: colorScheme => {
46
+ setState(getState(colorScheme));
47
+ localStorage?.setItem(colorSchemeKey, colorScheme);
48
+ }
49
+ };
50
+ };
51
+ const ColorSchemeContext = React.createContext(undefined);
52
+ const ColorSchemeProvider = _ref => {
53
+ let {
54
+ children,
55
+ colorSchemeKey = 'color-scheme',
56
+ initialColorScheme = 'system'
57
+ } = _ref;
58
+ const {
59
+ isSystem,
60
+ colorScheme,
61
+ setColorScheme
62
+ } = useColorScheme$1(initialColorScheme, colorSchemeKey);
63
+ const contextValue = React.useMemo(() => ({
64
+ colorScheme,
65
+ isSystem,
66
+ setColorScheme
67
+ }), [isSystem, colorScheme, setColorScheme]);
68
+ React.useEffect(() => {
69
+ const eventListener = () => {
70
+ setColorScheme('system');
71
+ };
72
+ if (isSystem) {
73
+ mediaQuery$1?.addEventListener('change', eventListener);
74
+ } else {
75
+ mediaQuery$1?.removeEventListener('change', eventListener);
76
+ }
77
+ return () => {
78
+ mediaQuery$1?.removeEventListener('change', eventListener);
79
+ };
80
+ }, [isSystem, setColorScheme]);
81
+ return React__default.default.createElement(ColorSchemeContext.Provider, {
82
+ value: contextValue
83
+ }, children);
84
+ };
85
+ ColorSchemeProvider.propTypes = {
86
+ colorSchemeKey: PropTypes__default.default.string,
87
+ initialColorScheme: PropTypes__default.default.oneOf(['light', 'dark', 'system'])
88
+ };
89
+
23
90
  const PALETTE = {
24
91
  black: '#000',
25
92
  white: '#fff',
@@ -652,7 +719,7 @@ const generateColorScale = memoize__default.default(color => {
652
719
  const colors = [];
653
720
  const contrastRatios = [];
654
721
  for (let i = 0; i <= scaleSize; i++) {
655
- const _color = scale(i);
722
+ const _color = color2k.toHex(scale(i));
656
723
  colors.push(_color);
657
724
  contrastRatios.push(polished.getContrast('#FFF', _color));
658
725
  }
@@ -1333,6 +1400,14 @@ function arrowStyles(position) {
1333
1400
  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
1401
  }
1335
1402
 
1403
+ const useColorScheme = () => {
1404
+ const context = React.useContext(ColorSchemeContext);
1405
+ if (!context) {
1406
+ throw new Error('Error: this component must be rendered within a <ColorSchemeProvider>.');
1407
+ }
1408
+ return context;
1409
+ };
1410
+
1336
1411
  const useDocument = theme => {
1337
1412
  const [controlledDocument, setControlledDocument] = React.useState();
1338
1413
  React.useEffect(() => {
@@ -1486,6 +1561,7 @@ const MENU_POSITION = ['top', 'right', 'bottom', 'left'];
1486
1561
  const PLACEMENT = ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'end', 'end-top', 'end-bottom', 'start', 'start-top', 'start-bottom'];
1487
1562
 
1488
1563
  exports.ARROW_POSITION = ARROW_POSITION;
1564
+ exports.ColorSchemeProvider = ColorSchemeProvider;
1489
1565
  exports.DEFAULT_THEME = DEFAULT_THEME;
1490
1566
  exports.MENU_POSITION = MENU_POSITION;
1491
1567
  exports.PALETTE = PALETTE;
@@ -1507,6 +1583,7 @@ exports.getMenuPosition = getMenuPosition;
1507
1583
  exports.mediaQuery = mediaQuery;
1508
1584
  exports.menuStyles = menuStyles;
1509
1585
  exports.retrieveComponentStyles = retrieveComponentStyles;
1586
+ exports.useColorScheme = useColorScheme;
1510
1587
  exports.useDocument = useDocument;
1511
1588
  exports.useText = useText;
1512
1589
  exports.useWindow = useWindow;
@@ -0,0 +1,17 @@
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 PropTypes from 'prop-types';
9
+ import { IColorSchemeContext, IColorSchemeProviderProps } from '../types';
10
+ export declare const ColorSchemeContext: React.Context<IColorSchemeContext | undefined>;
11
+ export declare const ColorSchemeProvider: {
12
+ ({ children, colorSchemeKey, initialColorScheme }: PropsWithChildren<IColorSchemeProviderProps>): React.JSX.Element;
13
+ propTypes: {
14
+ colorSchemeKey: PropTypes.Requireable<string>;
15
+ initialColorScheme: PropTypes.Requireable<string>;
16
+ };
17
+ };
@@ -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.1",
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": "2571d0225b784bb7c765316ed584d289d35d4605"
51
51
  }