Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Theme Mode Context #58

Merged
merged 3 commits into from
Dec 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 92 additions & 15 deletions src/components/containers/core/Root/Root.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@
* License: MIT
*/

import React, { Fragment } from "react";
import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
import { ThemeProvider, createGlobalStyle } from "styled-components";
import modernNormalize from "styled-modern-normalize";

import theme, { globals, normalize } from "styles/theme";
import theme, {
globals,
normalize,
MODE_BRIGHT_SNOW_FLURRY,
MODE_DARK_NIGHT_FROST,
THEME_KEY_MODE
} from "styles/theme";
import { SESSIONSTORAGE_KEY_THEME_MODE } from "config/stores/caches/constants";
import { readSessionCache, writeSessionCache } from "utils";

import "inter-ui/inter-ui.css";
import "typeface-rubik/index.css";
Expand All @@ -31,21 +39,90 @@ const GlobalStyle = createGlobalStyle`
`;

/**
* The root container with injected global CSS styles.
* The context provider component for global theme mode consumers.
*
* @since 0.2.0
*/
const GlobalThemeModeContext = React.createContext(MODE_BRIGHT_SNOW_FLURRY);

/**
* The context consumer component for the global theme mode.
*
* @since 0.2.0
*/
const GlobalThemeMode = GlobalThemeModeContext.Consumer;

/**
* The root container with injected global theme mode and CSS styles.
*
* @author Arctic Ice Studio <[email protected]>
* @author Sven Greb <[email protected]>
* @since 0.1.0
*/
const Root = ({ children }) => (
<Fragment>
<GlobalStyle />
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</Fragment>
);

Root.propTypes = {
children: PropTypes.node.isRequired
};

export default Root;
export default class Root extends Component {
static propTypes = {
children: PropTypes.node.isRequired
};

/**
* Constructs the compnent with the given `props` and persists the default theme mode if the key is not already stored
* in the session storage.
*
* @method constructor
* @param {object} props The React component's `props`.
* @since 0.2.0
*/
constructor(props) {
super(props);
if (!readSessionCache(SESSIONSTORAGE_KEY_THEME_MODE)) {
writeSessionCache(SESSIONSTORAGE_KEY_THEME_MODE, MODE_BRIGHT_SNOW_FLURRY);
}
}

state = {
themeMode: readSessionCache(SESSIONSTORAGE_KEY_THEME_MODE) || MODE_BRIGHT_SNOW_FLURRY
};

/**
* Toggles the global theme mode and persists it in the browser's session storage.
* The function is exposed through the global theme mode context provider component.
*
* @method toggleThemeMode
* @return {void}
* @since 0.2.0
*/
toggleThemeMode = () => {
/* eslint-disable-next-line babel/no-unused-expressions */
readSessionCache(SESSIONSTORAGE_KEY_THEME_MODE) === MODE_BRIGHT_SNOW_FLURRY
? writeSessionCache(SESSIONSTORAGE_KEY_THEME_MODE, MODE_DARK_NIGHT_FROST)
: writeSessionCache(SESSIONSTORAGE_KEY_THEME_MODE, MODE_BRIGHT_SNOW_FLURRY);
this.setState(({ themeMode }) => ({
themeMode: themeMode === MODE_BRIGHT_SNOW_FLURRY ? MODE_DARK_NIGHT_FROST : MODE_BRIGHT_SNOW_FLURRY
}));
};

render() {
const { children } = this.props;
const { themeMode } = this.state;

const themeModeContextValue = {
toggleThemeMode: this.toggleThemeMode,
[THEME_KEY_MODE]: themeMode
};
const composedTheme = {
...themeModeContextValue,
...theme
};

return (
<Fragment>
<GlobalThemeModeContext.Provider value={themeModeContextValue}>
<GlobalStyle theme={themeModeContextValue} />
<ThemeProvider theme={composedTheme}>{children}</ThemeProvider>
</GlobalThemeModeContext.Provider>
</Fragment>
);
}
}

export { GlobalThemeMode };
5 changes: 4 additions & 1 deletion src/components/containers/core/Root/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@
* License: MIT
*/

export { default } from "./Root";
import Root, { GlobalThemeMode } from "./Root";

export { GlobalThemeMode };
export default Root;
43 changes: 43 additions & 0 deletions src/config/stores/caches/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]>
* Copyright (C) 2018-present Sven Greb <[email protected]>
*
* Project: Nord Docs
* Repository: https://github.com/arcticicestudio/nord-docs
* License: MIT
*/

/**
* @file Provides internally cache related store constants.
* @author Arctic Ice Studio <[email protected]>
* @author Sven Greb <[email protected]>
* @since 0.2.0
*/

import { metadataNordDocs } from "data/project";

/**
* The prefix for keys stored in the session/local storage.
*
* @constant {string}
* @since 0.2.0
*/
const SESSIONSTORAGE_KEY_PREFIX = `${metadataNordDocs.name}`;

/**
* The separator for keys stored in the session/local storage.
*
* @constant {string}
* @since 0.2.0
*/
const SESSIONSTORAGE_KEY_SEPARATOR = ":";

/**
* The session/local storage key for the theme mode.
*
* @constant {string}
* @since 0.2.0
*/
const SESSIONSTORAGE_KEY_THEME_MODE = `${SESSIONSTORAGE_KEY_PREFIX}${SESSIONSTORAGE_KEY_SEPARATOR}themeMode`;

export { SESSIONSTORAGE_KEY_PREFIX, SESSIONSTORAGE_KEY_SEPARATOR, SESSIONSTORAGE_KEY_THEME_MODE };
19 changes: 19 additions & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]>
* Copyright (C) 2018-present Sven Greb <[email protected]>
*
* Project: Nord Docs
* Repository: https://github.com/arcticicestudio/nord-docs
* License: MIT
*/

/**
* @file Provides utility functions and classes.
* @author Arctic Ice Studio <[email protected]>
* @author Sven Greb <[email protected]>
* @since 0.2.0
*/

import { readSessionCache, writeSessionCache } from "./sessionCache";

export { readSessionCache, writeSessionCache };
60 changes: 60 additions & 0 deletions src/utils/sessionCache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]>
* Copyright (C) 2018-present Sven Greb <[email protected]>
*
* Project: Nord Docs
* Repository: https://github.com/arcticicestudio/nord-docs
* License: MIT
*/

/**
* @file Provides utility functions for session storage and caching.
* @author Arctic Ice Studio <[email protected]>
* @author Sven Greb <[email protected]>
* @since 0.2.0
*/

/**
* Checks if the global session storage object is available in the current environment.
*
* @private
* @method hasSessionStorage
* @return {Boolean} `true` if the global session storage object is available, `false` otherwise.
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
*/
const hasSessionStorage = () => typeof window !== "undefined" && window.sessionStorage !== "undefined";

/**
* Reads the value of the given key from the session storage (if available).
*
* @method readSessionCache
* @param {string} key The name of the key from which the value should be read.
* @return {string|null} The value of the given key, `null` otherwise if the global session storage is not available in
* the current environment.
*/
const readSessionCache = key => {
if (hasSessionStorage()) {
return sessionStorage.getItem(key);
}
return null;
};

/**
* Writes the given key-value pair to the session storage (if available).
*
* @method writeSessionCache
* @param {string} key The name of the key for the paired value.
* @param {string} value The value for the given key.
* @return {void}, `true` if the value has been set, `false` otherwise if the global session storage is not available
* in the current environment.
*/
const writeSessionCache = (key, value) => {
if (hasSessionStorage()) {
sessionStorage.setItem(key, value);
return true;
}
return false;
};

export { readSessionCache, writeSessionCache };