react-router-theme
Version:
Theme support for React Router and Remix
90 lines (89 loc) • 3.89 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.themeCookieResponse = exports.action = exports.loader = exports.createThemeCookie = exports.getThemeFromCookie = exports.useTheme = void 0;
const react_1 = require("react");
/**
* See Readme: https://www.npmjs.com/package/react-router-theme
*/
const useTheme = (loaderData, fetcher, defaultTheme) => {
let initialTheme;
if (!loaderData || !("theme" in loaderData)) {
throw new Error("Provided loader data does not contain theme.");
}
if (typeof loaderData.theme === "string") {
initialTheme = loaderData.theme;
}
else if (loaderData.theme === null) {
initialTheme = defaultTheme !== null && defaultTheme !== void 0 ? defaultTheme : "default";
}
else {
throw new Error("Provider loader data contains an invalid value for theme.");
}
const [theme, setTheme] = (0, react_1.useState)(initialTheme);
const changeTheme = (theme) => {
setTheme(theme);
localStorage.setItem("theme", theme);
fetcher.submit({ action: "themeChange", theme: theme }, { method: "POST" });
};
// Sync theme updates across windows and tabs using local storage
const localStorageEventHandler = (event) => {
if (event.key !== "theme" || event.oldValue === event.newValue || event.newValue === null)
return;
setTheme(event.newValue);
};
(0, react_1.useEffect)(() => {
window.addEventListener("storage", localStorageEventHandler);
return () => window.removeEventListener("storage", localStorageEventHandler);
}, []);
return [theme, changeTheme];
};
exports.useTheme = useTheme;
/**
* @param req incoming request in loader
* @returns the value of the theme cookie if found, otherwise NULL
*/
const getThemeFromCookie = (req) => {
const cookieHeader = req.headers.get("Cookie");
if (!cookieHeader)
return null;
const themeMatch = cookieHeader.match(/theme=([^;]+)/);
if (!themeMatch)
return null;
return themeMatch[1];
};
exports.getThemeFromCookie = getThemeFromCookie;
const createThemeCookie = (formData) => {
const theme = formData.get("theme");
if (!theme)
throw new Error("No theme specified in action form data");
return `theme=${theme}; Path=/; Max-Age=31536000`;
};
exports.createThemeCookie = createThemeCookie;
const loader = (args) => __awaiter(void 0, void 0, void 0, function* () {
return { theme: (0, exports.getThemeFromCookie)(args.request) };
});
exports.loader = loader;
const action = (args) => __awaiter(void 0, void 0, void 0, function* () {
const formData = yield args.request.formData();
if (formData.get("action") === "themeChange")
return (0, exports.themeCookieResponse)(formData);
return new Response("Unknown action. Create a custom action function to handle non-theme related requests.", { status: 500 });
});
exports.action = action;
const themeCookieResponse = (formData) => {
return new Response(null, {
headers: {
"Set-Cookie": (0, exports.createThemeCookie)(formData),
},
});
};
exports.themeCookieResponse = themeCookieResponse;