Skip to content

Commit

Permalink
feat: move setupEffect to theme-default
Browse files Browse the repository at this point in the history
  • Loading branch information
sanyuan0704 committed Sep 30, 2022
1 parent e6959f4 commit 60e5281
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/runtime/client-entry.tsx
@@ -1,5 +1,5 @@
import { ComponentType } from 'react';
import { setupEffects } from './sideEffects';
import { setupEffects } from 'island/theme';

// Type shim for window.ISLANDS
declare global {
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/hooks.ts
@@ -1,6 +1,6 @@
import { createContext, useContext } from 'react';
import { PageData } from '../shared/types';
import { inBrowser } from './utils';
import { inBrowser } from '../shared/utils';

export const DataContext = createContext({
data: inBrowser() ? window?.ISLAND_PAGE_DATA : null,
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/sideEffects.ts
@@ -1,6 +1,6 @@
import { throttle } from 'lodash-es';
import { APPEARANCE_KEY } from '../shared/constants';
import { inBrowser } from './utils';
import { inBrowser } from '../shared/utils';

const DEFAULT_NAV_HEIGHT = 60;

Expand Down
2 changes: 0 additions & 2 deletions src/runtime/utils.ts
@@ -1,7 +1,5 @@
import { cleanUrl } from '../shared/utils';

export const inBrowser = () => typeof window !== 'undefined';

export const omit = (obj: Record<string, unknown>, keys: string[]) => {
const ret = { ...obj };
for (const key of keys) {
Expand Down
1 change: 1 addition & 0 deletions src/shared/types/type.d.ts
Expand Up @@ -13,6 +13,7 @@ declare module 'island/theme*' {

export const Layout: ComponentType<unknown>;
export const NotFoundLayout: ComponentType<unknown>;
export const setupEffects: () => void;
}

declare module 'island:site-data' {
Expand Down
2 changes: 2 additions & 0 deletions src/shared/utils/index.ts
Expand Up @@ -3,3 +3,5 @@ export const hashRE = /#.*$/s;

export const cleanUrl = (url: string): string =>
url.replace(hashRE, '').replace(queryRE, '');

export const inBrowser = () => typeof window !== 'undefined';
2 changes: 2 additions & 0 deletions src/theme-default/index.ts
Expand Up @@ -6,3 +6,5 @@ import { Layout } from './layout/Layout';

// Tree Shaking
export { Layout, NotFoundLayout };

export { setupEffects } from './logic';
1 change: 1 addition & 0 deletions src/theme-default/logic/index.ts
Expand Up @@ -31,3 +31,4 @@ export { usePrevNextPage } from './usePrevNextPage';
export { useEditLink } from './useEditLink';
export { useSidebarData } from './useSidebarData';
export { useLocaleSiteData } from './useLocaleSiteData';
export { setupEffects } from './sideEffects';
205 changes: 205 additions & 0 deletions src/theme-default/logic/sideEffects.ts
@@ -0,0 +1,205 @@
import { throttle } from 'lodash-es';
import { APPEARANCE_KEY } from '../../shared/constants';
import { inBrowser } from '../../shared/utils';

const DEFAULT_NAV_HEIGHT = 60;

function bindingAppearanceToggle() {
// Appearance click event
const appearanceEl = document.getElementById('appearance');

function getAppearanceToggle() {
if (typeof localStorage === 'undefined') {
return () => undefined;
}
const setClass = (dark: boolean): void => {
classList[dark ? 'add' : 'remove']('dark');
};
// Determine if the theme mode of the user's operating system is dark
const query = window.matchMedia('(prefers-color-scheme: dark)');

const classList = document.documentElement.classList;

let userPreference = localStorage.getItem(APPEARANCE_KEY) || 'auto';
// When user preference is auto,the island theme will change with the system user's operating system theme.
let isDark =
userPreference === 'auto' ? query.matches : userPreference === 'dark';

query.onchange = (e) => {
// If user preference is not auto, the island theme will not change with the system user's operating system theme.
if (userPreference === 'auto') {
setClass((isDark = e.matches));
}
};
const toggle = () => {
setClass((isDark = !isDark));
if (isDark) {
// When the user's operating system theme is light, and actively switch the theme to dark锛宼hat mean the user preference is dark.
userPreference = query.matches ? 'auto' : 'dark';
} else {
// When the user's operating system theme is dark, and actively switch the theme to light锛宼hat mean user Preference is light
userPreference = query.matches ? 'light' : 'auto';
}

localStorage.setItem(APPEARANCE_KEY, userPreference);
};

return toggle;
}
appearanceEl!.addEventListener('click', getAppearanceToggle());
}

// Control the scroll behavior of the browser when user clicks on a link
function bindingWindowScroll() {
// eslint-disable-next-line no-inner-declarations
function scrollTo(el: HTMLElement, hash: string, isSmooth = false) {
let target: HTMLElement | null = null;
try {
target = el.classList.contains('header-anchor')
? el
: document.getElementById(decodeURIComponent(hash.slice(1)));

target;
} catch (e) {
console.warn(e);
}

if (target) {
const targetPadding = parseInt(
window.getComputedStyle(target).paddingTop,
10
);
const targetTop =
window.scrollY +
target.getBoundingClientRect().top -
DEFAULT_NAV_HEIGHT +
targetPadding;
// Only scroll smoothly in page header anchor
window.scrollTo({
left: 0,
top: targetTop,
...(isSmooth ? { behavior: 'smooth' } : {})
});
}
}

window.addEventListener(
'click',
(e) => {
// Only handle a tag click
const link = (e.target as Element).closest('a');
if (link) {
const { origin, hash, target, pathname, search } = link;
const currentUrl = window.location;
// only intercept inbound links
if (hash && target !== '_blank' && origin === currentUrl.origin) {
// scroll between hash anchors in the same page
if (
pathname === currentUrl.pathname &&
search === currentUrl.search &&
hash &&
hash !== currentUrl.hash &&
link.classList.contains('header-anchor')
) {
e.preventDefault();
history.pushState(null, '', hash);
// use smooth scroll when clicking on header anchor links
scrollTo(link, hash, true);
// still emit the event so we can listen to it in themes
window.dispatchEvent(new Event('hashchange'));
}
}
}
},
{ capture: true }
);
window.addEventListener('hashchange', (e) => {
e.preventDefault();
});
}

// Binding the scroll event to the aside element
function bindingAsideScroll() {
function isBottom() {
return (
document.documentElement.scrollTop + window.innerHeight >=
document.documentElement.scrollHeight
);
}
const NAV_HEIGHT = 60;
const marker = document.getElementById('aside-marker');
const aside = document.getElementById('aside-container');
const links = document.querySelectorAll<HTMLAnchorElement>(
'.island-doc .header-anchor'
);
let prevActiveLink: null | HTMLAnchorElement = null;
const headers = Array.from(aside?.getElementsByTagName('a') || []).map(
(item) => item.hash
);
if (marker && !headers.length) {
marker.style.opacity = '0';
return;
}
// Util function to set dom ref after determining the active link
const activate = (links: NodeListOf<HTMLAnchorElement>, index: number) => {
if (prevActiveLink) {
prevActiveLink.classList.remove('aside-active');
}
if (links[index]) {
links[index].classList.add('aside-active');
const id = links[index].getAttribute('href');
const tocIndex = headers.findIndex((item) => item === id);
const currentLink = aside?.querySelector(`a[href="#${id?.slice(1)}"]`);
if (currentLink) {
prevActiveLink = currentLink as HTMLAnchorElement;
// Activate the a link element in aside
prevActiveLink.classList.add('aside-active');
// Activate the marker element
marker!.style.top = `${33 + tocIndex * 28}px`;
marker!.style.opacity = '1';
}
}
};
const setActiveLink = () => {
if (isBottom()) {
activate(links, links.length - 1);
} else {
// Compute current index
for (let i = 0; i < links.length; i++) {
const currentAnchor = links[i];
const nextAnchor = links[i + 1];
const scrollTop = window.scrollY;
const currentAnchorTop =
currentAnchor.parentElement!.offsetTop - NAV_HEIGHT;
if (i === 0 && scrollTop === 0) {
activate(links, 0);
}

if (!nextAnchor) {
activate(links, i);
break;
}

const nextAnchorTop = nextAnchor.parentElement!.offsetTop - NAV_HEIGHT;

if (scrollTop > currentAnchorTop && scrollTop < nextAnchorTop) {
activate(links, i);
break;
}
}
}
};
const throttledSetLink = throttle(setActiveLink, 100);
requestAnimationFrame(setActiveLink);

window.addEventListener('scroll', throttledSetLink);
}

export function setupEffects() {
if (!inBrowser()) {
return;
}
bindingAppearanceToggle();
bindingAsideScroll();
bindingWindowScroll();
}

0 comments on commit 60e5281

Please sign in to comment.