html.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /* eslint-disable max-lines-per-function */
  2. /* argus-disable dangerMethod */
  3. import React from 'react';
  4. import PropTypes from 'prop-types';
  5. const darkmodeProcesser = () => {
  6. if (!window) {
  7. return;
  8. }
  9. function cache(key, value) {
  10. try {
  11. localStorage.setItem(key, JSON.stringify(value));
  12. } catch (error) {
  13. console.log(error);
  14. }
  15. }
  16. function getCache(key) {
  17. try {
  18. return JSON.parse(localStorage.getItem(key));
  19. } catch (error) {
  20. console.log(error);
  21. return undefined;
  22. }
  23. }
  24. const setMode = mode => {
  25. if (!window) {
  26. return;
  27. }
  28. const { body } = document;
  29. cache('semiMode', mode);
  30. const attr = 'theme-mode';
  31. if (mode === 'dark') {
  32. body.setAttribute(attr, mode);
  33. } else {
  34. body.removeAttribute(attr);
  35. }
  36. window.themeStorage.darkModeListener.map(listener => listener(mode));
  37. };
  38. const getValue = () => {
  39. const mql = window.matchMedia('(prefers-color-scheme: dark)');
  40. const match = mql.matches;
  41. return match ? 'dark' : 'light';
  42. };
  43. const cacheMode = getCache('semiMode');
  44. const mode = cacheMode ? cacheMode : getValue();
  45. document.addEventListener('readystatechange', () => {
  46. if (document.readyState === 'interactive') {
  47. setMode(mode);
  48. }
  49. });
  50. if (!window.themeStorage) {
  51. window.themeStorage = {
  52. darkModeListener: []
  53. };
  54. }
  55. window.themeStorage.setMode = setMode;
  56. };
  57. const semiThemeProcesser = () => {
  58. function cache(key, value) {
  59. try {
  60. localStorage.setItem(key, JSON.stringify(value));
  61. } catch (error) {
  62. console.log(error);
  63. }
  64. }
  65. function getCache(key) {
  66. try {
  67. return JSON.parse(localStorage.getItem(key));
  68. } catch (error) {
  69. console.log(error);
  70. return undefined;
  71. }
  72. }
  73. const defaultGlobal = {
  74. dark: {
  75. '--semi-color-primary': 'rgba(var(--semi-blue-5), 1)',
  76. '--semi-color-primary-hover': 'rgba(var(--semi-blue-6), 1)',
  77. '--semi-color-primary-active': 'rgba(var(--semi-blue-7), 1)',
  78. '--semi-color-primary-disabled': 'rgba(var(--semi-blue-2), 1)',
  79. '--semi-color-primary-light-default': 'rgba(var(--semi-blue-5), .2)',
  80. '--semi-color-primary-light-hover': 'rgba(var(--semi-blue-5), .3)',
  81. '--semi-color-primary-light-active': 'rgba(var(--semi-blue-5), .4)',
  82. '--semi-color-focus-border': 'rgba(var(--semi-blue-5), 1)'
  83. },
  84. light: {
  85. '--semi-color-primary': 'rgba(var(--semi-blue-5), 1)',
  86. '--semi-color-primary-hover': 'rgba(var(--semi-blue-6), 1)',
  87. '--semi-color-primary-active': 'rgba(var(--semi-blue-7), 1)',
  88. '--semi-color-primary-disabled': 'rgba(var(--semi-blue-2), 1)',
  89. '--semi-color-primary-light-default': 'rgba(var(--semi-blue-0), 1)',
  90. '--semi-color-primary-light-hover': 'rgba(var(--semi-blue-1), 1)',
  91. '--semi-color-primary-light-active': 'rgba(var(--semi-blue-2), 1)',
  92. '--semi-color-focus-border': 'rgba(var(--semi-blue-5), 1)'
  93. }
  94. };
  95. const _conventStr = obj => {
  96. let str = '';
  97. Object.keys(obj).forEach(key => {
  98. const value = obj[key];
  99. str = `${str}${key}: ${value};`;
  100. });
  101. return str;
  102. };
  103. const paletteObj = getCache('semiPaletteObj') ? getCache('semiPaletteObj') : { light: {}, dark: {} };
  104. const globalObj = getCache('semiGlobalObj')
  105. ? getCache('semiGlobalObj')
  106. : {
  107. light: defaultGlobal.light,
  108. dark: defaultGlobal.dark,
  109. };
  110. const writeStyle = ({ lightPaletteStr, darkPaletteStr, lightGlobalStr, darkGlobalStr }) => {
  111. let cssText = `
  112. body{
  113. ${lightPaletteStr}
  114. ${lightGlobalStr}
  115. }\n
  116. body[theme-mode='dark'] {
  117. ${darkPaletteStr}
  118. ${darkGlobalStr}
  119. }
  120. `;
  121. const styleSheet = document.querySelector('style[name="semi"]');
  122. if (!styleSheet) {
  123. const style = document.createElement('style');
  124. style.innerText = cssText;
  125. style.setAttribute('name', 'semi');
  126. document.head.appendChild(style);
  127. } else {
  128. styleSheet.innerText = cssText;
  129. }
  130. };
  131. const lightPaletteStr = _conventStr(paletteObj.light);
  132. const darkPaletteStr = _conventStr(paletteObj.dark);
  133. const lightGlobalStr = _conventStr(globalObj.light);
  134. const darkGlobalStr = _conventStr(globalObj.dark);
  135. writeStyle({ lightPaletteStr, darkPaletteStr, lightGlobalStr, darkGlobalStr });
  136. };
  137. export default function HTML(props) {
  138. if (process.env.NODE_ENV === 'production') {
  139. for (const component of props.headComponents) {
  140. if (component.type === 'style') {
  141. const index = props.headComponents.indexOf(component);
  142. const link = <link rel="stylesheet" href={component.props['data-href']} />;
  143. props.headComponents.splice(index, 1, link);
  144. }
  145. }
  146. }
  147. return (
  148. <html {...props.htmlAttributes}>
  149. <head>
  150. <meta charSet="utf-8" />
  151. <meta httpEquiv="x-ua-compatible" content="ie=edge" />
  152. <meta name="viewport" content="width=1440, initial-scale=0, shrink-to-fit=no" />
  153. <script src="https://lf1-cdn-tos.bytescm.com/goofy/semi_convenience/semi-analyze.js" />
  154. {
  155. THEME_SWITCHER_URL?<script src={THEME_SWITCHER_URL} defer={true}/>:<script src="https://unpkg.byted-static.com/latest/ies/semi-theme-switcher-opensource/dist/semi-theme-switcher.js" defer={true}/>
  156. }
  157. {
  158. SEMI_SEARCH_URL?<script src={SEMI_SEARCH_URL} defer={true}/>:<script src={"https://unpkg.byted-static.com/latest/ies/semi-search-opensource/dist/semi-search.js"} defer={true}/>
  159. }
  160. <link rel="icon" href="https://lf9-static.semi.design/obj/semi-tos/images/favicon.ico" />
  161. <script dangerouslySetInnerHTML={{ __html: `(${darkmodeProcesser.toString()})()` }} />
  162. {props.headComponents}
  163. <script dangerouslySetInnerHTML={{ __html: `(${semiThemeProcesser.toString()})()` }} />
  164. </head>
  165. <body {...props.bodyAttributes}>
  166. {props.preBodyComponents}
  167. <div key={'body'} id="___gatsby" dangerouslySetInnerHTML={{ __html: props.body }} />
  168. {props.postBodyComponents}
  169. </body>
  170. </html>
  171. );
  172. }
  173. HTML.propTypes = {
  174. htmlAttributes: PropTypes.object,
  175. headComponents: PropTypes.array,
  176. bodyAttributes: PropTypes.object,
  177. preBodyComponents: PropTypes.array,
  178. body: PropTypes.string,
  179. postBodyComponents: PropTypes.array,
  180. };