UNPKG

react-css-theme-switcher

Version:
193 lines (158 loc) 5.69 kB
import { useState, useCallback, useEffect, useMemo, createElement, useContext, createContext } from 'react'; function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function findCommentNode(comment) { var head = document.head; for (var i = 0; i < head.childNodes.length; i++) { var _node$nodeValue; var node = head.childNodes[i]; if (node.nodeType === 8 && (node == null ? void 0 : (_node$nodeValue = node.nodeValue) == null ? void 0 : _node$nodeValue.trim()) === comment) { return node; } } return null; } function isElement(o) { return typeof HTMLElement === 'object' ? o instanceof HTMLElement //DOM2 : o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string'; } function arrayToObject(array) { var obj = {}; array.forEach(function (el) { return obj[el] = el; }); return obj; } function createLinkElement(attributes) { var linkElement = document.createElement('link'); for (var _i = 0, _Object$entries = Object.entries(attributes); _i < _Object$entries.length; _i++) { var _Object$entries$_i = _Object$entries[_i], attribute = _Object$entries$_i[0], value = _Object$entries$_i[1]; if (attribute === 'onload') { linkElement.onload = attributes.onload; continue; } // @ts-ignore linkElement[attribute] = value; } return linkElement; } var Status; (function (Status) { Status["idle"] = "idle"; Status["loading"] = "loading"; Status["loaded"] = "loaded"; })(Status || (Status = {})); var ThemeSwitcherContext = /*#__PURE__*/createContext(undefined); function ThemeSwitcherProvider(_ref) { var themeMap = _ref.themeMap, insertionPoint = _ref.insertionPoint, defaultTheme = _ref.defaultTheme, _ref$id = _ref.id, id = _ref$id === void 0 ? 'current-theme-style' : _ref$id, _ref$attr = _ref.attr, attr = _ref$attr === void 0 ? 'data-theme' : _ref$attr, rest = _objectWithoutPropertiesLoose(_ref, ["themeMap", "insertionPoint", "defaultTheme", "id", "attr"]); var _React$useState = useState(Status.idle), status = _React$useState[0], setStatus = _React$useState[1]; var _React$useState2 = useState(), currentTheme = _React$useState2[0], setCurrentTheme = _React$useState2[1]; var insertStyle = useCallback(function (linkElement) { if (insertionPoint || insertionPoint === null) { var insertionPointElement = isElement(insertionPoint) ? insertionPoint : findCommentNode(insertionPoint); if (!insertionPointElement) { console.warn("Insertion point '" + insertionPoint + "' does not exist. Be sure to add comment on head and that it matches the insertionPoint"); return document.head.appendChild(linkElement); } var parentNode = insertionPointElement.parentNode; if (parentNode) { return parentNode.insertBefore(linkElement, insertionPointElement.nextSibling); } } else { return document.head.appendChild(linkElement); } }, [insertionPoint]); var switcher = useCallback(function (_ref2) { var theme = _ref2.theme; if (theme === currentTheme) return; if (themeMap[theme]) { setStatus(Status.loading); var linkElement = createLinkElement({ type: 'text/css', rel: 'stylesheet', id: id + '_temp', href: themeMap[theme], onload: function onload() { var previousStyle = document.getElementById(id); if (previousStyle) { previousStyle.remove(); } var nextStyle = document.getElementById(id + '_temp'); if (nextStyle) { nextStyle.setAttribute('id', id); } setStatus(Status.loaded); } }); insertStyle(linkElement); setCurrentTheme(theme); } else { return console.warn('Could not find specified theme'); } document.body.setAttribute(attr, theme); }, [themeMap, insertStyle, attr, id, currentTheme]); useEffect(function () { if (defaultTheme) { switcher({ theme: defaultTheme }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [defaultTheme]); useEffect(function () { var themes = Object.keys(themeMap); themes.map(function (theme) { var themeAssetId = "theme-prefetch-" + theme; if (!document.getElementById(themeAssetId)) { var stylePrefetch = document.createElement('link'); stylePrefetch.rel = 'prefetch'; stylePrefetch.type = 'text/css'; stylePrefetch.id = themeAssetId; stylePrefetch.href = themeMap[theme]; insertStyle(stylePrefetch); } return ''; }); }, [themeMap, insertStyle]); var value = useMemo(function () { return { switcher: switcher, status: status, currentTheme: currentTheme, themes: arrayToObject(Object.keys(themeMap)) }; }, [switcher, status, currentTheme, themeMap]); return createElement(ThemeSwitcherContext.Provider, Object.assign({ value: value }, rest)); } function useThemeSwitcher() { var context = useContext(ThemeSwitcherContext); if (!context) { throw new Error('To use `useThemeSwitcher`, component must be within a ThemeSwitcherProvider'); } return context; } export { ThemeSwitcherProvider, useThemeSwitcher }; //# sourceMappingURL=react-css-theme-switcher.esm.js.map