mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-07 17:44:35 +08:00
docs(site): Network connection is not smooth notification (#53930)
This commit is contained in:
parent
8944de51e6
commit
37dd4449a6
@ -1,176 +0,0 @@
|
||||
(function createMirrorModal() {
|
||||
if (
|
||||
(navigator.languages.includes('zh') || navigator.languages.includes('zh-CN')) &&
|
||||
/-cn\/?$/.test(window.location.pathname) &&
|
||||
!['ant-design.gitee.io', 'ant-design.antgroup.com'].includes(window.location.hostname) &&
|
||||
!window.location.host.includes('surge') &&
|
||||
window.location.hostname !== 'localhost'
|
||||
) {
|
||||
const ANTD_DOT_NOT_SHOW_MIRROR_MODAL = 'ANT_DESIGN_DO_NOT_OPEN_MIRROR_MODAL';
|
||||
|
||||
const lastShowTime = window.localStorage.getItem(ANTD_DOT_NOT_SHOW_MIRROR_MODAL);
|
||||
if (
|
||||
lastShowTime &&
|
||||
lastShowTime !== 'true' &&
|
||||
Date.now() - new Date(lastShowTime).getTime() < 7 * 24 * 60 * 60 * 1000
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = `
|
||||
@keyframes mirror-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mirror-zoom-in {
|
||||
from {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.mirror-modal-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
z-index: 9999;
|
||||
animation: mirror-fade-in 0.3s forwards;
|
||||
}
|
||||
|
||||
.mirror-modal-dialog {
|
||||
position: fixed;
|
||||
top: 120px;
|
||||
inset-inline-start: 0;
|
||||
inset-inline-end: 0;
|
||||
margin: 0 auto;
|
||||
width: 420px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #eee;
|
||||
background: #fff;
|
||||
padding: 20px 24px;
|
||||
box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
||||
animation: mirror-zoom-in 0.3s forwards;
|
||||
box-sizing: border-box;
|
||||
max-width: 100vw;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.mirror-modal-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
align-self: flex-start;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mirror-modal-content {
|
||||
font-size: 14px;
|
||||
align-self: flex-start;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.mirror-modal-btns {
|
||||
align-self: flex-end;
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mirror-modal-btn {
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
height: 32px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
padding: 4px 16px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.mirror-modal-confirm-btn {
|
||||
background: #1677ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.mirror-modal-confirm-btn:hover {
|
||||
background: #4096ff;
|
||||
}
|
||||
|
||||
.mirror-modal-confirm-btn:active {
|
||||
background: #0958d9;
|
||||
}
|
||||
|
||||
.mirror-modal-cancel-btn {
|
||||
border: 1px solid #eee;
|
||||
color: #000;
|
||||
margin-inline-end: 8px;
|
||||
}
|
||||
|
||||
.mirror-modal-cancel-btn:hover {
|
||||
border-color: #4096ff;
|
||||
color: #4096ff
|
||||
}
|
||||
|
||||
.mirror-modal-cancel-btn:active {
|
||||
border-color: #0958d9;
|
||||
color: #0958d9;
|
||||
}
|
||||
`;
|
||||
document.head.append(style);
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'mirror-modal-mask';
|
||||
|
||||
const dialog = document.createElement('div');
|
||||
dialog.className = 'mirror-modal-dialog';
|
||||
modal.append(dialog);
|
||||
|
||||
const title = document.createElement('div');
|
||||
title.className = 'mirror-modal-title';
|
||||
title.textContent = '提示';
|
||||
dialog.append(title);
|
||||
|
||||
const content = document.createElement('div');
|
||||
content.className = 'mirror-modal-content';
|
||||
content.textContent = '🚀 国内用户推荐访问国内镜像以获得极速体验~';
|
||||
dialog.append(content);
|
||||
|
||||
const btnWrapper = document.createElement('div');
|
||||
btnWrapper.className = 'mirror-modal-btns';
|
||||
dialog.append(btnWrapper);
|
||||
|
||||
const cancelBtn = document.createElement('a');
|
||||
cancelBtn.className = 'mirror-modal-cancel-btn mirror-modal-btn';
|
||||
cancelBtn.textContent = '7 天内不再显示';
|
||||
btnWrapper.append(cancelBtn);
|
||||
cancelBtn.addEventListener('click', () => {
|
||||
window.localStorage.setItem(ANTD_DOT_NOT_SHOW_MIRROR_MODAL, new Date().toISOString());
|
||||
document.body.removeChild(modal);
|
||||
document.head.removeChild(style);
|
||||
document.body.style.overflow = '';
|
||||
});
|
||||
|
||||
const confirmBtn = document.createElement('a');
|
||||
confirmBtn.className = 'mirror-modal-confirm-btn mirror-modal-btn';
|
||||
confirmBtn.href = window.location.href.replace(window.location.host, 'ant-design.antgroup.com');
|
||||
confirmBtn.textContent = '🚀 立刻前往';
|
||||
btnWrapper.append(confirmBtn);
|
||||
|
||||
document.body.append(modal);
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
})();
|
230
.dumi/scripts/mirror-notify.js
Normal file
230
.dumi/scripts/mirror-notify.js
Normal file
@ -0,0 +1,230 @@
|
||||
(function createMirrorModal() {
|
||||
const SIGN = Symbol.for('antd.mirror-notify');
|
||||
const always = window.localStorage.getItem('DEBUG') === 'antd';
|
||||
const officialChinaMirror = 'https://ant-design.antgroup.com';
|
||||
|
||||
const enabledCondition = [
|
||||
// Check if the browser language is Chinese
|
||||
navigator.languages.includes('zh') || navigator.languages.includes('zh-CN'),
|
||||
// Check if the URL path ends with -cn
|
||||
/-cn\/?$/.test(window.location.pathname),
|
||||
// chinese mirror URL
|
||||
!['ant-design.gitee.io', new URL(officialChinaMirror).hostname].includes(
|
||||
window.location.hostname,
|
||||
),
|
||||
// PR review URL
|
||||
!window.location.host.includes('surge'),
|
||||
// development mode
|
||||
!['127.0.0.1', 'localhost'].includes(window.location.hostname),
|
||||
];
|
||||
|
||||
const isEnabled = always || enabledCondition.every(Boolean);
|
||||
|
||||
if (!isEnabled) return;
|
||||
|
||||
const prefixCls = 'antd-mirror-notify';
|
||||
const primaryColor = '#1677ff';
|
||||
|
||||
function insertCss() {
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = `
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
transform: translate3d(100%, 0, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.${prefixCls} {
|
||||
position: fixed;
|
||||
inset-inline-end: 12px;
|
||||
inset-block-start: 12px;
|
||||
z-index: 9999;
|
||||
width: 360px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
animation: slideInRight 0.3s ease-in-out;
|
||||
}
|
||||
.${prefixCls}-content {
|
||||
padding: 16px;
|
||||
}
|
||||
.${prefixCls}-content a {
|
||||
color: ${primaryColor};
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
.${prefixCls}-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-block-end: 8px;
|
||||
}
|
||||
.${prefixCls}-message {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
line-height: 1.57;
|
||||
}
|
||||
.${prefixCls}-footer {
|
||||
display: none;
|
||||
margin-block-start: 16px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.${prefixCls}-progress {
|
||||
position: relative;
|
||||
inset-inline-end: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.${prefixCls}-progress::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: var(--progress, 0%);
|
||||
background-color: ${primaryColor};
|
||||
transition: width 0.05s linear; /* Adjusted for smoother animation matching refreshRate */
|
||||
}
|
||||
.${prefixCls}-close {
|
||||
all: unset;
|
||||
position: absolute;
|
||||
inset-inline-end: 2px;
|
||||
inset-block-start: 2px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.${prefixCls}-close:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.${prefixCls}-action {
|
||||
all: unset;
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
background-color: ${primaryColor};
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
document.head.append(style);
|
||||
}
|
||||
|
||||
function createNotification() {
|
||||
insertCss();
|
||||
|
||||
const notify = document.createElement('div');
|
||||
notify.className = `${prefixCls} slideInRight`;
|
||||
notify.innerHTML = `
|
||||
<div class="${prefixCls}-content">
|
||||
<div class="${prefixCls}-title">🇨🇳 访问不畅?试试国内镜像</div>
|
||||
<div class="${prefixCls}-message">
|
||||
国内镜像站点可以帮助您更快地访问文档和资源。<br>
|
||||
请尝试访问 <a href="${officialChinaMirror}">国内镜像站点</a>,以获得更好的体验。
|
||||
</div>
|
||||
<div class="${prefixCls}-footer">
|
||||
<button class="${prefixCls}-action">🚀 立即前往</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="${prefixCls}-close">X</button>
|
||||
<div class="${prefixCls}-progress" style="--progress: 100%;"></div>
|
||||
`;
|
||||
document.body.appendChild(notify);
|
||||
|
||||
notify.querySelector(`.${prefixCls}-close`).addEventListener('click', () => {
|
||||
removeNotify();
|
||||
});
|
||||
|
||||
notify.querySelector(`.${prefixCls}-action`).addEventListener('click', () => {
|
||||
window.location.href = officialChinaMirror;
|
||||
removeNotify();
|
||||
});
|
||||
|
||||
const refreshRate = 50; // ms
|
||||
const duration = 10; // s
|
||||
const step = 100 / ((duration * 1000) / refreshRate);
|
||||
let progressInterval = -1;
|
||||
|
||||
function removeNotify() {
|
||||
clearInterval(progressInterval);
|
||||
notify.remove();
|
||||
}
|
||||
|
||||
const progressEl = notify.querySelector(`.${prefixCls}-progress`);
|
||||
let currentProgressValue = 100;
|
||||
|
||||
const progress = {
|
||||
get value() {
|
||||
return currentProgressValue;
|
||||
},
|
||||
set value(val) {
|
||||
currentProgressValue = Math.max(0, Math.min(100, val));
|
||||
progressEl.style.setProperty('--progress', `${currentProgressValue}%`);
|
||||
},
|
||||
};
|
||||
|
||||
function startProgressTimer() {
|
||||
if (progressInterval !== -1) {
|
||||
clearInterval(progressInterval);
|
||||
}
|
||||
progressInterval = setInterval(() => {
|
||||
if (progress.value <= 0) {
|
||||
removeNotify();
|
||||
} else {
|
||||
progress.value -= step;
|
||||
}
|
||||
}, refreshRate);
|
||||
}
|
||||
|
||||
startProgressTimer();
|
||||
|
||||
notify.addEventListener('mouseenter', () => {
|
||||
clearInterval(progressInterval);
|
||||
});
|
||||
|
||||
notify.addEventListener('mouseleave', () => {
|
||||
startProgressTimer();
|
||||
});
|
||||
}
|
||||
|
||||
// 断定网络不畅阈值(秒)
|
||||
const delayDuration = 3;
|
||||
|
||||
const reactTimeoutId = setTimeout(() => {
|
||||
if (typeof window[SIGN]?.YES === 'undefined') {
|
||||
console.error(
|
||||
`antd.mirror-notify: 页面加载超过 ${delayDuration} 秒,可能是网络不畅。\n请尝试访问国内镜像站点。%c${officialChinaMirror}`,
|
||||
`color: ${primaryColor}; font-weight: bold;`,
|
||||
);
|
||||
createNotification();
|
||||
}
|
||||
}, delayDuration * 1000);
|
||||
|
||||
// 交给 React effect 清理
|
||||
window[SIGN] = function stopMirrorNotify() {
|
||||
window[SIGN].YES = Date.now();
|
||||
clearTimeout(reactTimeoutId);
|
||||
};
|
||||
})();
|
@ -140,6 +140,12 @@ const GlobalLayout: React.FC = () => {
|
||||
// Handle isMobile
|
||||
updateMobileMode();
|
||||
|
||||
// 配合 dumi 的 mirror-notify 脚本使用
|
||||
const retrieveMirrorNotification = (window as any)[Symbol.for('antd.mirror-notify')];
|
||||
if (typeof retrieveMirrorNotification === 'function') {
|
||||
retrieveMirrorNotification();
|
||||
}
|
||||
|
||||
window.addEventListener('resize', updateMobileMode);
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateMobileMode);
|
||||
|
@ -190,7 +190,7 @@ export default defineConfig({
|
||||
{
|
||||
async: true,
|
||||
content: fs
|
||||
.readFileSync(path.join(__dirname, '.dumi', 'scripts', 'mirror-modal.js'))
|
||||
.readFileSync(path.join(__dirname, '.dumi', 'scripts', 'mirror-notify.js'))
|
||||
.toString(),
|
||||
},
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user