You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

101 regels
3.0 KiB

  1. "use client";
  2. import * as React from "react";
  3. import createCache from "@emotion/cache";
  4. import { useServerInsertedHTML } from "next/navigation";
  5. import { CacheProvider as DefaultCacheProvider } from "@emotion/react";
  6. import type {
  7. EmotionCache,
  8. Options as OptionsOfCreateCache,
  9. } from "@emotion/cache";
  10. // Copied from https://github.com/mui/material-ui/blob/master/examples/material-ui-nextjs-ts/src/components/ThemeRegistry/EmotionCache.tsx
  11. export type NextAppDirEmotionCacheProviderProps = {
  12. /** This is the options passed to createCache() from 'import createCache from "@emotion/cache"' */
  13. options: Omit<OptionsOfCreateCache, "insertionPoint">;
  14. /** By default <CacheProvider /> from 'import { CacheProvider } from "@emotion/react"' */
  15. CacheProvider?: (props: {
  16. value: EmotionCache;
  17. children: React.ReactNode;
  18. }) => React.JSX.Element | null;
  19. children: React.ReactNode;
  20. };
  21. // Adapted from https://github.com/garronej/tss-react/blob/main/src/next/appDir.tsx
  22. export default function NextAppDirEmotionCacheProvider(
  23. props: NextAppDirEmotionCacheProviderProps,
  24. ) {
  25. const { options, CacheProvider = DefaultCacheProvider, children } = props;
  26. const [registry] = React.useState(() => {
  27. const cache = createCache(options);
  28. cache.compat = true;
  29. const prevInsert = cache.insert;
  30. let inserted: { name: string; isGlobal: boolean }[] = [];
  31. cache.insert = (...args) => {
  32. const [selector, serialized] = args;
  33. if (cache.inserted[serialized.name] === undefined) {
  34. inserted.push({
  35. name: serialized.name,
  36. isGlobal: !selector,
  37. });
  38. }
  39. return prevInsert(...args);
  40. };
  41. const flush = () => {
  42. const prevInserted = inserted;
  43. inserted = [];
  44. return prevInserted;
  45. };
  46. return { cache, flush };
  47. });
  48. useServerInsertedHTML(() => {
  49. const inserted = registry.flush();
  50. if (inserted.length === 0) {
  51. return null;
  52. }
  53. let styles = "";
  54. let dataEmotionAttribute = registry.cache.key;
  55. const globals: {
  56. name: string;
  57. style: string;
  58. }[] = [];
  59. inserted.forEach(({ name, isGlobal }) => {
  60. const style = registry.cache.inserted[name];
  61. if (typeof style !== "boolean") {
  62. if (isGlobal) {
  63. globals.push({ name, style });
  64. } else {
  65. styles += style;
  66. dataEmotionAttribute += ` ${name}`;
  67. }
  68. }
  69. });
  70. return (
  71. <React.Fragment>
  72. {globals.map(({ name, style }) => (
  73. <style
  74. key={name}
  75. data-emotion={`${registry.cache.key}-global ${name}`}
  76. // eslint-disable-next-line react/no-danger
  77. dangerouslySetInnerHTML={{ __html: style }}
  78. />
  79. ))}
  80. {styles && (
  81. <style
  82. data-emotion={dataEmotionAttribute}
  83. // eslint-disable-next-line react/no-danger
  84. dangerouslySetInnerHTML={{ __html: styles }}
  85. />
  86. )}
  87. </React.Fragment>
  88. );
  89. });
  90. return <CacheProvider value={registry.cache}>{children}</CacheProvider>;
  91. }