2017-11-30 21:38:48 +08:00
import React from 'react' ;
2016-05-27 11:48:08 +08:00
import ReactDOM from 'react-dom' ;
2020-04-24 18:48:05 +08:00
import classNames from 'classnames' ;
2020-04-25 18:43:54 +08:00
import { IntlProvider } from 'react-intl' ;
2019-12-25 19:04:29 +08:00
import { presetPalettes , presetDarkPalettes } from '@ant-design/colors' ;
2020-01-02 17:54:11 +08:00
import themeSwitcher from 'theme-switcher' ;
2019-12-25 10:10:28 +08:00
import { setTwoToneColor } from '@ant-design/icons' ;
2019-11-09 17:42:41 +08:00
import { Helmet , HelmetProvider } from 'react-helmet-async' ;
2018-05-18 11:34:26 +08:00
import 'moment/locale/zh-cn' ;
2019-07-24 10:34:55 +08:00
import { ConfigProvider } from 'antd' ;
2020-09-14 10:48:08 +08:00
import { browserHistory } from 'bisheng/router' ;
2020-10-26 11:57:37 +08:00
import zhCN from 'antd/lib/locale/zh_CN' ;
2016-05-27 11:48:08 +08:00
import Header from './Header' ;
2020-02-27 10:53:30 +08:00
import SiteContext from './SiteContext' ;
2016-09-27 10:06:34 +08:00
import enLocale from '../../en-US' ;
import cnLocale from '../../zh-CN' ;
2016-10-18 11:18:25 +08:00
import * as utils from '../utils' ;
2022-03-21 16:48:25 +08:00
import defaultSeedToken from '../../../../components/_util/theme/themes/default' ;
2022-02-18 14:17:32 +08:00
import DynamicTheme from './DynamicTheme' ;
2016-05-27 11:48:08 +08:00
2018-09-26 11:13:19 +08:00
if ( typeof window !== 'undefined' && navigator . serviceWorker ) {
2018-12-07 16:17:45 +08:00
navigator . serviceWorker . getRegistrations ( ) . then ( registrations => {
2018-09-26 11:13:19 +08:00
registrations . forEach ( registration => registration . unregister ( ) ) ;
} ) ;
}
2016-11-21 14:39:15 +08:00
if ( typeof window !== 'undefined' ) {
2020-02-27 17:11:29 +08:00
// Redirect to `ant.design` if is not next version anymore
2020-02-29 00:03:19 +08:00
if ( location . hostname === 'next.ant.design' ) {
2020-02-27 17:11:29 +08:00
location . href = location . href . replace ( 'next.ant.design' , 'ant.design' ) ;
}
2019-08-06 15:02:05 +08:00
// eslint-disable-next-line global-require
2016-11-21 14:39:15 +08:00
require ( '../../static/style' ) ;
// Expose to iframe
window . react = React ;
window [ 'react-dom' ] = ReactDOM ;
2019-08-06 15:09:07 +08:00
// eslint-disable-next-line global-require
2016-11-21 14:39:15 +08:00
window . antd = require ( 'antd' ) ;
2019-08-13 14:07:17 +08:00
// eslint-disable-next-line global-require
window [ '@ant-design/icons' ] = require ( '@ant-design/icons' ) ;
2019-02-27 15:31:32 +08:00
// Error log statistic
2019-03-06 12:23:41 +08:00
window . addEventListener ( 'error' , function onError ( e ) {
2019-02-27 15:31:32 +08:00
// Ignore ResizeObserver error
if ( e . message === 'ResizeObserver loop limit exceeded' ) {
e . stopPropagation ( ) ;
e . stopImmediatePropagation ( ) ;
}
} ) ;
2016-11-21 14:39:15 +08:00
}
2016-05-27 11:48:08 +08:00
2020-02-27 10:53:30 +08:00
const RESPONSIVE _MOBILE = 768 ;
2019-12-23 17:41:55 +08:00
2019-12-24 11:14:13 +08:00
// for dark.css timestamp to remove cache
const timestamp = new Date ( ) . getTime ( ) ;
2020-01-02 17:54:11 +08:00
const themeMap = {
dark : ` /dark.css? ${ timestamp } ` ,
2020-03-29 10:39:46 +08:00
compact : ` /compact.css? ${ timestamp } ` ,
2020-01-02 17:54:11 +08:00
} ;
const themeConfig = {
themeMap ,
} ;
const { switcher } = themeSwitcher ( themeConfig ) ;
2019-12-24 11:14:13 +08:00
2016-06-03 15:26:25 +08:00
export default class Layout extends React . Component {
2020-05-29 18:05:58 +08:00
static contextType = SiteContext ;
2018-01-04 20:00:38 +08:00
2020-09-14 10:48:08 +08:00
isBeforeComponent = false ;
2022-03-15 17:05:36 +08:00
syncIframeThemeId = null ;
2020-09-14 10:48:08 +08:00
2016-12-09 13:02:16 +08:00
constructor ( props ) {
super ( props ) ;
2017-10-09 13:23:20 +08:00
const { pathname } = props . location ;
2016-12-09 13:02:16 +08:00
const appLocale = utils . isZhCN ( pathname ) ? cnLocale : enLocale ;
2018-04-22 18:58:40 +08:00
2016-12-09 13:02:16 +08:00
this . state = {
appLocale ,
2020-09-14 10:48:08 +08:00
theme : 'default' ,
2019-12-18 18:45:05 +08:00
setTheme : this . setTheme ,
2020-01-02 19:10:16 +08:00
direction : 'ltr' ,
2020-01-02 17:54:11 +08:00
setIframeTheme : this . setIframeTheme ,
2022-03-21 16:48:25 +08:00
designToken : defaultSeedToken ,
2022-03-03 14:55:19 +08:00
hashedStyle : true ,
2016-12-09 13:02:16 +08:00
} ;
}
2016-11-09 19:55:14 +08:00
2016-06-03 15:26:25 +08:00
componentDidMount ( ) {
2020-05-29 18:05:58 +08:00
const { location , router } = this . props ;
2020-09-14 10:48:08 +08:00
router . listen ( ( { pathname , search } ) => {
const { theme } = this . props . location . query ;
2018-05-21 23:27:26 +08:00
if ( typeof window . ga !== 'undefined' ) {
2020-09-14 10:48:08 +08:00
window . ga ( 'send' , 'pageview' , pathname + search ) ;
2018-05-21 23:27:26 +08:00
}
// eslint-disable-next-line
if ( typeof window . _hmt !== 'undefined' ) {
// eslint-disable-next-line
2020-09-14 10:48:08 +08:00
window . _hmt . push ( [ '_trackPageview' , pathname + search ] ) ;
2018-05-21 23:27:26 +08:00
}
2019-12-23 17:58:59 +08:00
const componentPage = /^\/?components/ . test ( pathname ) ;
2020-09-14 10:48:08 +08:00
2019-12-23 17:58:59 +08:00
// only component page can use `dark` theme
if ( ! componentPage ) {
2020-09-14 10:48:08 +08:00
this . isBeforeComponent = false ;
2019-12-23 17:58:59 +08:00
this . setTheme ( 'default' , false ) ;
2020-09-14 10:48:08 +08:00
} else if ( theme && ! this . isBeforeComponent ) {
this . isBeforeComponent = true ;
this . setTheme ( theme , false ) ;
2019-12-23 17:58:59 +08:00
}
2018-05-21 23:27:26 +08:00
} ) ;
2020-09-14 10:48:08 +08:00
if ( location . query . theme && /^\/?components/ . test ( location . pathname ) ) {
this . isBeforeComponent = true ;
this . setTheme ( location . query . theme , false ) ;
} else {
this . isBeforeComponent = false ;
this . setTheme ( 'default' , false ) ;
}
if ( location . query . direction ) {
this . setState ( {
direction : location . query . direction ,
} ) ;
} else {
this . setState ( {
direction : 'ltr' ,
} ) ;
}
2019-12-23 17:41:55 +08:00
2016-12-09 14:24:38 +08:00
const nprogressHiddenStyle = document . getElementById ( 'nprogress-style' ) ;
if ( nprogressHiddenStyle ) {
2016-07-26 17:40:08 +08:00
this . timer = setTimeout ( ( ) => {
2016-12-09 14:24:38 +08:00
nprogressHiddenStyle . parentNode . removeChild ( nprogressHiddenStyle ) ;
} , 0 ) ;
2016-06-23 21:10:02 +08:00
}
2018-01-04 20:00:38 +08:00
2020-02-27 10:53:30 +08:00
this . updateMobileMode ( ) ;
window . addEventListener ( 'resize' , this . updateMobileMode ) ;
2022-03-15 17:05:36 +08:00
// Sync iframe theme with current theme
this . syncIframeThemeId = setInterval ( ( ) => {
const { designToken , hashedStyle } = this . state ;
const content = JSON . stringify ( {
action : 'sync.theme' ,
designToken ,
hashed : hashedStyle ,
} ) ;
document . querySelectorAll ( 'iframe.iframe-demo' ) . forEach ( iframe => {
iframe . contentWindow . postMessage ( content ) ;
} ) ;
} , 1000 ) ;
2016-06-23 21:10:02 +08:00
}
componentWillUnmount ( ) {
clearTimeout ( this . timer ) ;
2022-03-15 17:05:36 +08:00
clearInterval ( this . syncIframeThemeId ) ;
2020-02-27 10:53:30 +08:00
window . removeEventListener ( 'resize' , this . updateMobileMode ) ;
2016-06-03 15:26:25 +08:00
}
2020-02-27 10:53:30 +08:00
updateMobileMode = ( ) => {
const { isMobile } = this . state ;
const newIsMobile = window . innerWidth < RESPONSIVE _MOBILE ;
if ( isMobile !== newIsMobile ) {
this . setState ( {
isMobile : newIsMobile ,
} ) ;
}
} ;
2020-01-02 17:54:11 +08:00
setIframeTheme = ( iframeNode , theme ) => {
iframeNode . contentWindow . postMessage (
JSON . stringify ( {
action : 'change.theme' ,
data : {
themeConfig ,
theme ,
} ,
} ) ,
) ;
} ;
2019-12-23 17:58:59 +08:00
setTheme = ( theme , persist = true ) => {
2019-12-23 16:02:09 +08:00
if ( typeof window === 'undefined' ) {
return ;
}
2020-01-02 17:54:11 +08:00
switcher ( {
theme ,
useStorage : persist ,
} ) ;
const iframeNodes = document . querySelectorAll ( '.iframe-demo' ) ;
// loop element node
[ ] . forEach . call ( iframeNodes , iframeNode => {
this . setIframeTheme ( iframeNode , theme ) ;
} ) ;
2019-12-18 18:45:05 +08:00
this . setState ( {
theme ,
} ) ;
2019-12-25 10:10:28 +08:00
const iconTwoToneThemeMap = {
2019-12-25 19:04:29 +08:00
dark : [ presetDarkPalettes . blue . primary , '#111d2c' ] ,
2019-12-25 10:10:28 +08:00
default : presetPalettes . blue . primary ,
} ;
setTwoToneColor ( iconTwoToneThemeMap [ theme ] || iconTwoToneThemeMap . default ) ;
2019-12-18 18:45:05 +08:00
} ;
2020-05-31 11:48:34 +08:00
changeDirection = direction => {
2020-01-02 19:10:16 +08:00
this . setState ( {
direction ,
} ) ;
2020-09-14 10:48:08 +08:00
const { pathname , hash , query } = this . props . location ;
if ( direction === 'ltr' ) {
delete query . direction ;
} else {
query . direction = 'rtl' ;
}
browserHistory . push ( {
pathname : ` / ${ pathname } ` ,
query ,
hash ,
} ) ;
2020-05-31 11:48:34 +08:00
} ;
2020-01-02 19:10:16 +08:00
2016-06-03 15:26:25 +08:00
render ( ) {
2019-11-11 14:30:11 +08:00
const { children , helmetContext = { } , ... restProps } = this . props ;
2022-03-03 14:55:19 +08:00
const {
appLocale ,
direction ,
isMobile ,
theme ,
setTheme ,
setIframeTheme ,
designToken ,
hashedStyle ,
} = this . state ;
2019-09-16 11:23:26 +08:00
const title =
appLocale . locale === 'zh-CN'
? 'Ant Design - 一套企业级 UI 设计语言和 React 组件库'
2020-04-26 00:09:23 +08:00
: "Ant Design - The world's second most popular React UI framework" ;
2019-09-16 11:23:26 +08:00
const description =
appLocale . locale === 'zh-CN'
? '基于 Ant Design 设计体系的 React UI 组件库,用于研发企业级中后台产品。'
: 'An enterprise-class UI design language and React UI library with a set of high-quality React components, one of best React UI library for enterprises' ;
2016-06-03 15:26:25 +08:00
return (
2020-05-29 18:05:58 +08:00
< SiteContext.Provider value = { { isMobile , direction , theme , setTheme , setIframeTheme } } >
2020-02-27 10:53:30 +08:00
< HelmetProvider context = { helmetContext } >
< Helmet encodeSpecialCharacters = { false } >
2020-04-24 18:48:05 +08:00
< html
lang = { appLocale . locale === 'zh-CN' ? 'zh' : 'en' }
data - direction = { direction }
className = { classNames ( {
[ ` rtl ` ] : direction === 'rtl' ,
} ) }
/ >
2020-02-27 10:53:30 +08:00
< title > { title } < / title >
< link
rel = "apple-touch-icon-precomposed"
sizes = "144x144"
href = "https://gw.alipayobjects.com/zos/antfincdn/UmVnt3t4T0/antd.png"
/ >
< meta name = "description" content = { description } / >
< meta property = "og:title" content = { title } / >
< meta property = "og:type" content = "website" / >
< meta
property = "og:image"
content = "https://gw.alipayobjects.com/zos/rmsportal/rlpTLlbMzTNYuZGGCVYM.png"
/ >
< / Helmet >
< IntlProvider
locale = { appLocale . locale }
messages = { appLocale . messages }
defaultLocale = "en-US"
>
< ConfigProvider
locale = { appLocale . locale === 'zh-CN' ? zhCN : null }
direction = { direction }
2022-02-18 14:17:32 +08:00
theme = { {
token : designToken ,
2022-03-03 14:55:19 +08:00
hashed : hashedStyle ,
2022-02-18 14:17:32 +08:00
} }
2020-02-27 10:53:30 +08:00
>
2020-04-24 18:48:05 +08:00
< Header { ...restProps } changeDirection = { this . changeDirection } / >
{ children }
2022-02-18 14:17:32 +08:00
< DynamicTheme
2022-03-17 14:06:21 +08:00
componentName = { this . props . params ? . children ? . replace ( '-cn' , '' ) }
2022-03-03 14:55:19 +08:00
defaultToken = { {
... designToken ,
hashed : hashedStyle ,
} }
2022-02-18 14:17:32 +08:00
onChangeTheme = { newToken => {
console . log ( 'Change Theme:' , newToken ) ;
2022-03-03 14:55:19 +08:00
const { hashed , ... restToken } = newToken ;
2022-02-18 14:17:32 +08:00
this . setState ( {
2022-03-03 14:55:19 +08:00
designToken : restToken ,
hashedStyle : hashed ,
2022-02-18 14:17:32 +08:00
} ) ;
} }
/ >
2020-02-27 10:53:30 +08:00
< / ConfigProvider >
< / IntlProvider >
< / HelmetProvider >
< / SiteContext.Provider >
2016-06-03 15:26:25 +08:00
) ;
}
}