Skip to content

Commit 3f02144

Browse files
committed
Update components for better SSR support
1 parent 4317c6f commit 3f02144

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+117
-39
lines changed

src/components/AppFooter.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Link from './Link';
44
import Logo, { type LogoProps } from './Logo';
55
import type { LinkType, ThemeToken } from '../types';
66

7-
export interface AppFooterProps {
7+
export type AppFooterProps = {
88
bordered?: boolean;
99
className?: string;
1010
copyright?: React.ReactNode;
@@ -18,7 +18,7 @@ export interface AppFooterProps {
1818
sticky?: boolean;
1919
theme?: ThemeToken;
2020
transparent?: boolean;
21-
}
21+
} & Omit<React.ComponentPropsWithRef<'footer'>, 'children'>;
2222

2323
export default function AppFooter({
2424
bordered = false,
@@ -39,6 +39,7 @@ export default function AppFooter({
3939
sticky = false,
4040
theme,
4141
transparent = false,
42+
...rest
4243
}: AppFooterProps) {
4344
return (
4445
<footer
@@ -52,6 +53,7 @@ export default function AppFooter({
5253
className,
5354
)}
5455
data-theme={theme}
56+
{...rest}
5557
>
5658
<div className="container">
5759
<div className="row align-center my-lg">

src/components/AppHeader.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export type AppHeaderProps = {
1616
sticky?: boolean;
1717
theme?: ThemeToken;
1818
transparent?: boolean;
19-
} & React.ComponentPropsWithRef<'header'>;
19+
} & Omit<React.ComponentPropsWithRef<'header'>, 'children'>;
2020

2121
export default function AppHeader({
2222
bordered = false,
@@ -33,7 +33,6 @@ export default function AppHeader({
3333
}: AppHeaderProps) {
3434
return (
3535
<header
36-
{...rest}
3736
className={classNames(
3837
'app-header',
3938
{
@@ -44,6 +43,7 @@ export default function AppHeader({
4443
className,
4544
)}
4645
data-theme={theme}
46+
{...rest}
4747
>
4848
<Container fluid={fluid}>
4949
<Row className="flex-nowrap">

src/components/AppStoreBadge.tsx

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import classNames from 'classnames';
2-
import React, { useLayoutEffect, useState } from 'react';
2+
import React from 'react';
33
import type { PolymorphicProps } from '../types';
44

55
export type BaseAppStoreBadgeProps = {
@@ -29,19 +29,7 @@ export default function AppStoreBadge<T extends React.ElementType = 'a'>({
2929
...rest
3030
}: AppStoreBadgeProps<T>) {
3131
const Element = as ?? 'a';
32-
33-
let shortCode = (locale = locale.toLowerCase());
34-
const expeptionLocale = ['zh-cn', 'zh-tw'];
35-
if (expeptionLocale.indexOf(locale) === -1) {
36-
shortCode = locale.split(/[_-]/)[0];
37-
}
38-
39-
const [image, setImage] = useState(getImage(locale, shortCode, variant));
40-
41-
useLayoutEffect(() => {
42-
setImage(getImage(locale, shortCode, variant));
43-
}, [locale, shortCode, variant]);
44-
32+
const image = getImage(platform, locale, variant);
4533
return (
4634
<Element
4735
{...rest}
@@ -55,29 +43,42 @@ export default function AppStoreBadge<T extends React.ElementType = 'a'>({
5543
}}
5644
>
5745
<img
58-
alt={alt || image[platform].alt}
59-
src={image[platform].src}
46+
alt={alt || image?.alt}
47+
src={image?.src}
6048
style={{
6149
width: '100%',
6250
height: '100%',
6351
}}
64-
onError={() => {
65-
setImage(getImage('en-us', shortCode, variant));
66-
}}
6752
/>
6853
</Element>
6954
);
7055
}
7156

72-
function getImage(locale: string, code = locale, variant = 'black') {
73-
return {
74-
ios: {
75-
src: `https://toolbox.marketingtools.apple.com/api/v2/badges/download-on-the-app-store/${variant}/${locale}`,
76-
alt: 'Download on the App Store',
77-
},
78-
android: {
79-
src: `https://raw.github.com/yjb94/google-play-badge-svg/master/img/${code}_get.svg?sanitize=true`,
80-
alt: 'Get it on Google Play',
81-
},
82-
};
57+
function getImage(
58+
platform: 'ios' | 'android',
59+
locale: string,
60+
variant = 'black',
61+
) {
62+
switch (platform) {
63+
case 'ios':
64+
return {
65+
src: `https://toolbox.marketingtools.apple.com/api/v2/badges/download-on-the-app-store/${variant}/${locale}`,
66+
alt: 'Download on the App Store',
67+
};
68+
case 'android':
69+
const shortCode = getShortCode(locale);
70+
return {
71+
src: `https://raw.github.com/yjb94/google-play-badge-svg/master/img/${shortCode}_get.svg?sanitize=true`,
72+
alt: 'Get it on Google Play',
73+
};
74+
}
75+
}
76+
77+
function getShortCode(locale: string) {
78+
let shortCode = locale.toLowerCase();
79+
const expeptionLocale = ['zh-cn', 'zh-tw'];
80+
if (expeptionLocale.indexOf(locale) === -1) {
81+
shortCode = locale.split(/[_-]/)[0];
82+
}
83+
return shortCode;
8384
}

src/components/ClickableDiv.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React from 'react';
35
import { keyboardEventHandler } from '../utils';

src/components/CodeBlock.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React, { useEffect, useRef } from 'react';
35
import { highlightElement } from '../utils/syntax';

src/components/ContactForm.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React from 'react';
35

46
export type ContactFormProps = {
57
className?: string;
68
onSubmit?: React.FormEventHandler<HTMLFormElement>;
7-
} & React.ComponentPropsWithRef<'form'>;
9+
} & Omit<React.ComponentPropsWithRef<'form'>, 'children'>;
810

911
export default React.forwardRef(function ContactForm(
1012
{ className, onSubmit = () => {}, ...rest }: ContactFormProps,

src/components/CookieConsent.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useCookies } from 'react-cookie';
24
import classNames from 'classnames';
35
import React from 'react';

src/components/EmbeddedVideo.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React, { useRef } from 'react';
35
import LoadingSpinner from './LoadingSpinner';

src/components/Icon.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export default function Icon<T extends React.ElementType = 'i'>({
3737
variant = 'rounded',
3838
...rest
3939
}: IconProps<T>) {
40+
icon = icon ?? (children as string);
4041
const Element = as ?? 'i';
4142
const isTokenSize =
4243
typeof size === 'string' &&
@@ -52,8 +53,8 @@ export default function Icon<T extends React.ElementType = 'i'>({
5253
<Element
5354
{...rest}
5455
className={classNames(
56+
'icon',
5557
{
56-
icon: true,
5758
[`icon--${shape}`]: shape,
5859
[`icon--${size}`]: isTokenSize,
5960
[`text-${color}`]: isTokenColor,

src/components/ImageGallery.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React, { useCallback, useState } from 'react';
35
import Lightbox, { type LightboxProps } from './Lightbox';
@@ -15,7 +17,7 @@ export interface ImageGalleryProps {
1517
columns?: number;
1618
disableLightbox?: boolean;
1719
fullWidthFirstItem?: boolean;
18-
images?: Image[];
20+
images: Image[];
1921
lightboxProps?: LightboxProps;
2022
maxWidth?: number;
2123
minWidth?: number;

src/components/Input.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React, { useRef, useState } from 'react';
35
import Icon, { type IconProps } from './Icon';
@@ -12,7 +14,7 @@ export type InputProps = {
1214
onBlur?: React.FocusEventHandler<HTMLInputElement>;
1315
onFocus?: React.FocusEventHandler<HTMLInputElement>;
1416
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
15-
} & Omit<React.ComponentPropsWithoutRef<'input'>, 'size'>;
17+
} & Omit<React.ComponentPropsWithoutRef<'input'>, 'size' | 'children'>;
1618

1719
export default function Input({
1820
className,

src/components/Lightbox.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React, { useCallback } from 'react';
35
import Button from './Button';

src/components/NavBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export type NavBarProps = {
1313
onLinkClick?: (link: LinkTypeWithIcon) => void;
1414
spacing?: PaddingToken;
1515
variant?: 'button' | 'link';
16-
} & React.ComponentProps<'nav'>;
16+
} & Omit<React.ComponentProps<'nav'>, 'children'>;
1717

1818
export default function NavBar({
1919
className,

src/components/NavMenu.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React from 'react';
35
import Button from './Button';

src/components/Overlay.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React from 'react';
35
import ClickableDiv from './ClickableDiv';

src/components/Pagination.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React, { useMemo } from 'react';
35
import Button from './Button';

src/components/ProgressiveImage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React, { useRef } from 'react';
35
import LoadingSpinner from './LoadingSpinner';

src/components/ReactPortal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import React, { useEffect, useState } from 'react';
24
import { createPortal } from 'react-dom';
35

src/components/SearchInput.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React, { useCallback } from 'react';
35
import Input, { type InputProps } from './Input';

src/components/Thumbnail.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React, { useEffect, useMemo, useState } from 'react';
35
import ProgressiveImage, { type ProgressiveImageProps } from './ProgressiveImage'; // prettier-ignore

src/hoc/withScrollToTop.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import React, { useEffect } from 'react';
24
import { getDisplayName, scrollToTop } from '../utils';
35

src/hoc/withSyntaxHighlighting.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import React, { useEffect } from 'react';
24
import { getDisplayName, highlightAll } from '../utils';
35

src/hoc/withThemedFavicon.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import React from 'react';
24
import { useThemedFavicon } from '../hooks';
35
import { getDisplayName } from '../utils';

src/hoc/withTransition.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import classNames from 'classnames';
24
import React from 'react';
35
import { useMounted } from '../hooks';

src/hooks/useBodyOverflow.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useEffect } from 'react';
24
import { getScrollbarWidth } from '../utils';
35

src/hooks/useBreakpoint.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useMediaQuery } from './useMediaQuery';
24
import { BreakpointMinWidth, BreakpointMaxWidth } from '../enums';
35
import type { BreakpointToken } from '../types';

src/hooks/useElementSize.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useEffect, useLayoutEffect, useState } from 'react';
24
import type { Size } from '../types';
35

src/hooks/useKeyboardEvent.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useEffect } from 'react';
24
import { nativeKeyboardEventHandler } from '../utils';
35

src/hooks/useLoaded.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useEffect, useState } from 'react';
24
import { bindEvent, unbindEvent } from '../utils';
35

src/hooks/useLocalStorage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useEffect, useState } from 'react';
24

35
export function useLocalStorage<T>(

src/hooks/useMediaQuery.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useEffect, useState } from 'react';
24
import { bindEvent, unbindEvent } from '../utils';
35

src/hooks/useMounted.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useEffect, useState } from 'react';
24

35
export function useMounted() {

src/hooks/usePagination.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useEffect, useState } from 'react';
24

35
export type PaginationState<T> = {

src/hooks/useSearch.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useCallback, useEffect, useState } from 'react';
24

35
export type SearchState<T> = {

src/hooks/useSmoothDamp.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useEffect, useRef, useState } from 'react';
24
import { smoothdamp } from '../utils';
35

src/hooks/useTheme.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { createContext, useCallback } from 'react';
24
import { useLocalStorage } from './useLocalStorage';
35
import type { BaseThemeToken } from '../types';

src/hooks/useThemedFavicon.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useState } from 'react';
24
import { useMediaQuery } from './useMediaQuery';
35

src/utils/events.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
export function bindEvent(
24
element: EventTarget,
35
eventName: string,

0 commit comments

Comments
 (0)