UNPKG

@atlaskit/app-provider

Version:

A top level provider for the Design System.

307 lines (302 loc) • 16.2 kB
/* index.tsx generated by @compiled/babel-plugin v3.0.2 */ "use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.ThemeProvider = ThemeProvider; require("./index.compiled.css"); var _runtime = require("@compiled/react/runtime"); var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _react = _interopRequireWildcard(require("react")); var _bindEventListener = require("bind-event-listener"); var _fg = require("@atlaskit/platform-feature-flags/fg"); var _constants = require("@atlaskit/tokens/constants"); var _customThemeOverrides = require("@atlaskit/tokens/custom-theme-overrides"); var _getThemeHtmlAttrs = require("@atlaskit/tokens/get-theme-html-attrs"); var _setGlobalTheme = require("@atlaskit/tokens/set-global-theme"); var _useIsAppProviderThemingEnabled = require("../use-is-app-provider-theming-enabled"); var _useIsInsideAppProvider = require("../use-is-inside-app-provider"); var _colorMode = require("./context/color-mode"); var _insideThemeProvider = require("./context/inside-theme-provider"); var _setColorModeContext = require("./context/set-color-mode-context"); var _theme = require("./context/theme"); var _useIsInsideThemeProvider = require("./hooks/use-is-inside-theme-provider"); var _getInlineThemeStyles = require("./utils/get-inline-theme-styles"); var _loadAndMountThemes = require("./utils/load-and-mount-themes"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } var defaultThemeSettings = { dark: 'dark', light: 'light', shape: 'shape', spacing: 'spacing', typography: 'typography' }; var isMatchMediaAvailable = typeof window !== 'undefined' && 'matchMedia' in window; var prefersDarkModeMql = isMatchMediaAvailable ? window.matchMedia('(prefers-color-scheme: dark)') : undefined; // TODO: currently 'auto' color mode will always return 'light' in SSR. // Additional work required: https://product-fabric.atlassian.net/browse/DSP-9781 function getReconciledColorMode(colorMode) { if (colorMode === 'auto') { return prefersDarkModeMql !== null && prefersDarkModeMql !== void 0 && prefersDarkModeMql.matches ? 'dark' : 'light'; } return colorMode; } var contentStyles = { body: "_1e0c1bgi" }; /** * __Theme provider__ * * Provides global theming configuration. */ function ThemeProvider(_ref) { var children = _ref.children, _ref$defaultColorMode = _ref.defaultColorMode, defaultColorMode = _ref$defaultColorMode === void 0 ? 'auto' : _ref$defaultColorMode, defaultTheme = _ref.defaultTheme; var _useState = (0, _react.useState)(defaultColorMode), _useState2 = (0, _slicedToArray2.default)(_useState, 2), chosenColorMode = _useState2[0], setChosenColorMode = _useState2[1]; var _useState3 = (0, _react.useState)(getReconciledColorMode(defaultColorMode)), _useState4 = (0, _slicedToArray2.default)(_useState3, 2), reconciledColorMode = _useState4[0], setReconciledColorMode = _useState4[1]; var isInsideAppProvider = (0, _useIsInsideAppProvider.useIsInsideAppProvider)(); var isAppProviderThemingEnabled = (0, _useIsAppProviderThemingEnabled.useIsAppProviderThemingEnabled)(); var isInsideThemeProvider = (0, _useIsInsideThemeProvider.useIsInsideThemeProvider)(); /** * A top-level ThemeProvider is detected by being the first ThemeProvider inside an AppProvider. * * This will not use sub-tree theming but instead set the global theme state using the * `@atlaskit/tokens` APIs, as it's required for styling root `html` and `body` elements * for compatibility with `@atlaskit/css-reset`. * * In the future we could consider moving away from DOM mutations and require AppProvider to wrap * `html` in order to apply global theme state, which would allow a more consistent approach to * theme loading. */ var isRootThemeProvider = isInsideAppProvider && !isInsideThemeProvider && isAppProviderThemingEnabled; /** * Each provider owns its own override stylesheet, so nested providers don't overwrite or remove * each other's overrides. `useId` is stable between the server render and hydration. * * Subtree providers also scope their override selector to their own element. Otherwise every * subtree using the same custom theme would match every provider's overrides, and the last * stylesheet in the document would win. The root provider targets `html`, which subtrees * inherit from. */ var customThemeOverridesStyleId = (0, _react.useId)(); var _getThemeAndOverrides = (0, _customThemeOverrides.getThemeAndOverrides)(defaultTheme, isRootThemeProvider ? undefined : customThemeOverridesStyleId), normalizedDefaultTheme = _getThemeAndOverrides.theme, inlineStyles = _getThemeAndOverrides.inlineStyles; var _useState5 = (0, _react.useState)(function () { return _objectSpread(_objectSpread({}, defaultThemeSettings), normalizedDefaultTheme); }), _useState6 = (0, _slicedToArray2.default)(_useState5, 2), theme = _useState6[0], setTheme = _useState6[1]; var _useState7 = (0, _react.useState)(false), _useState8 = (0, _slicedToArray2.default)(_useState7, 2), hasHoistedInlineThemeStyles = _useState8[0], setHasHoistedInlineThemeStyles = _useState8[1]; var setColorMode = (0, _react.useCallback)(function (colorMode) { setChosenColorMode(colorMode); setReconciledColorMode(getReconciledColorMode(colorMode)); }, []); var setPartialTheme = (0, _react.useCallback)(function (nextTheme) { setTheme(function (theme) { return _objectSpread(_objectSpread({}, theme), nextTheme); }); }, []); var lastSetGlobalThemePromiseRef = (0, _react.useRef)(null); /** * Custom theme overrides apply regardless of `platform-static-theme-loading`: that gate only * controls how the theme stylesheets themselves are loaded. */ var inlineThemeStylesOverrides = inlineStyles; (0, _react.useLayoutEffect)(function () { return function () { var _document$head$queryS; (_document$head$queryS = document.head.querySelector("style[data-theme-overrides=\"".concat(customThemeOverridesStyleId, "\"]"))) === null || _document$head$queryS === void 0 || _document$head$queryS.remove(); }; }, [customThemeOverridesStyleId]); (0, _react.useLayoutEffect)(function () { var isStaticThemeLoadingEnabled = (0, _fg.fg)('platform-static-theme-loading'); if (isStaticThemeLoadingEnabled && !hasHoistedInlineThemeStyles) { (0, _getInlineThemeStyles.getInlineThemeStyles)(theme, chosenColorMode).forEach(function (_ref2) { var id = _ref2.id, css = _ref2.css; if (document.head.querySelector("style[data-theme=\"".concat(id, "\"]"))) { return; } var style = document.createElement('style'); style.dataset.theme = id; style.textContent = css; document.head.appendChild(style); }); } var existingOverrideStyle = document.head.querySelector("style[data-theme-overrides=\"".concat(customThemeOverridesStyleId, "\"]")); if (inlineThemeStylesOverrides) { if (existingOverrideStyle) { existingOverrideStyle.textContent = inlineThemeStylesOverrides; } else { var style = document.createElement('style'); style.dataset.themeOverrides = customThemeOverridesStyleId; style.textContent = inlineThemeStylesOverrides; document.head.appendChild(style); } } else { existingOverrideStyle === null || existingOverrideStyle === void 0 || existingOverrideStyle.remove(); } // Only re-render to drop the inline styles when there are any to drop. if (!hasHoistedInlineThemeStyles && (isStaticThemeLoadingEnabled || inlineThemeStylesOverrides)) { setHasHoistedInlineThemeStyles(true); } }, [chosenColorMode, customThemeOverridesStyleId, hasHoistedInlineThemeStyles, inlineThemeStylesOverrides, theme]); (0, _react.useEffect)(function () { if (isRootThemeProvider) { /** * We need to wait for any previous `setGlobalTheme` calls to finish before calling it again. * This is to prevent race conditions as `setGlobalTheme` is async and mutates the DOM (e.g. sets the * `data-color-mode` attribute on the root element). * * Since we can't safely abort the `setGlobalTheme` execution, we need to wait for it to properly finish before * applying the new theme. * * Without this, we can end up in the following scenario: * 1. app loads with the default 'light' theme, kicking off `setGlobalTheme` * 2. app switches to 'dark' theme after retrieving value persisted in local storage, calling `setGlobalTheme` again * 3. `setGlobalTheme` function execution for `dark` finishes before the initial `light` execution * 4. `setGlobalTheme` function execution for `light` then finishes, resulting in the 'light' theme being applied. */ var cleanupLastFnCall = /*#__PURE__*/function () { var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() { var unbindFn; return _regenerator.default.wrap(function (_context) { while (1) switch (_context.prev = _context.next) { case 0: if (!lastSetGlobalThemePromiseRef.current) { _context.next = 2; break; } _context.next = 1; return lastSetGlobalThemePromiseRef.current; case 1: unbindFn = _context.sent; unbindFn(); lastSetGlobalThemePromiseRef.current = null; case 2: case "end": return _context.stop(); } }, _callee); })); return function cleanupLastFnCall() { return _ref3.apply(this, arguments); }; }(); var safelySetGlobalTheme = /*#__PURE__*/function () { var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() { var promise; return _regenerator.default.wrap(function (_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: _context2.next = 1; return cleanupLastFnCall(); case 1: promise = (0, _setGlobalTheme.setGlobalTheme)(_objectSpread(_objectSpread({}, theme), {}, { colorMode: reconciledColorMode })); lastSetGlobalThemePromiseRef.current = promise; case 2: case "end": return _context2.stop(); } }, _callee2); })); return function safelySetGlobalTheme() { return _ref4.apply(this, arguments); }; }(); safelySetGlobalTheme(); return function cleanup() { cleanupLastFnCall(); }; } // For other theme providers (whether outside AppProvider or nested inside a ThemeProvider), // we treat them as sub-tree themes that do not load global theme state. (0, _loadAndMountThemes.loadAndMountThemes)(theme); }, [isInsideAppProvider, isInsideThemeProvider, isRootThemeProvider, reconciledColorMode, theme]); (0, _react.useEffect)(function () { if (!prefersDarkModeMql) { return; } var unbindListener = (0, _bindEventListener.bind)(prefersDarkModeMql, { type: 'change', listener: function listener(event) { if (chosenColorMode === 'auto') { setReconciledColorMode(event.matches ? 'dark' : 'light'); } } }); return unbindListener; }, [chosenColorMode]); var attrs = _objectSpread(_objectSpread({}, (0, _getThemeHtmlAttrs.getThemeHtmlAttrs)(_objectSpread(_objectSpread({}, theme), {}, { colorMode: reconciledColorMode }))), {}, (0, _defineProperty2.default)({}, _constants.SUBTREE_THEME_ATTRIBUTE, true), inlineThemeStylesOverrides ? (0, _defineProperty2.default)({}, _customThemeOverrides.CUSTOM_THEME_SCOPE_ATTRIBUTE, customThemeOverridesStyleId) : {}); var inlineThemeStyles = (0, _fg.fg)('platform-static-theme-loading') && !hasHoistedInlineThemeStyles ? (0, _getInlineThemeStyles.getInlineThemeStyles)(theme, chosenColorMode) : []; return /*#__PURE__*/_react.default.createElement(_insideThemeProvider.InsideThemeProviderContext.Provider, { value: true }, /*#__PURE__*/_react.default.createElement(_colorMode.ColorModeContext.Provider, { value: reconciledColorMode }, /*#__PURE__*/_react.default.createElement(_setColorModeContext.SetColorModeContext.Provider, { value: setColorMode }, /*#__PURE__*/_react.default.createElement(_theme.ThemeContext.Provider, { value: theme }, /*#__PURE__*/_react.default.createElement(_theme.SetThemeContext.Provider, { value: setPartialTheme }, !isRootThemeProvider ? /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({}, attrs, { className: (0, _runtime.ax)([contentStyles.body]) }), !hasHoistedInlineThemeStyles && inlineThemeStylesOverrides && /*#__PURE__*/ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles _react.default.createElement("style", { "data-theme-overrides": customThemeOverridesStyleId }, inlineThemeStylesOverrides), inlineThemeStyles.map(function (_ref6) { var id = _ref6.id, css = _ref6.css; return ( /*#__PURE__*/ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles _react.default.createElement("style", { "data-theme": id, key: id }, css) ); }), children) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, !hasHoistedInlineThemeStyles && inlineThemeStylesOverrides && /*#__PURE__*/ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles _react.default.createElement("style", { "data-theme-overrides": customThemeOverridesStyleId }, inlineThemeStylesOverrides), inlineThemeStyles.map(function (_ref7) { var id = _ref7.id, css = _ref7.css; return ( /*#__PURE__*/ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles _react.default.createElement("style", { "data-theme": id, key: id }, css) ); }), children)))))); }