Skip to content

Commit

Permalink
refactor(theme-classic): split theme footer into smaller components +…
Browse files Browse the repository at this point in the history
… swizzle config (#6894)

Co-authored-by: Joshua Chen <[email protected]>
  • Loading branch information
slorber and Josh-Cena authored Mar 11, 2022
1 parent c9ee6e4 commit 1efc6c6
Show file tree
Hide file tree
Showing 14 changed files with 416 additions and 180 deletions.
53 changes: 52 additions & 1 deletion packages/docusaurus-theme-classic/src/getSwizzleConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,62 @@ export default function getSwizzleConfig(): SwizzleConfig {
},
Footer: {
actions: {
eject: 'unsafe', // TODO split footer into smaller parts
eject: 'safe',
wrap: 'safe',
},
description: "The footer component of you site's layout",
},
'Footer/Copyright': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description: 'The footer copyright',
},
'Footer/Layout': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description: 'The footer main layout component',
},
'Footer/LinkItem': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description: 'The footer link item component',
},
'Footer/Links': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description: 'The footer component rendering the footer links',
},
'Footer/Links/MultiColumn': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description:
'The footer component rendering the footer links with a multi-column layout',
},
'Footer/Links/Simple': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description:
'The footer component rendering the footer links with a simple layout (single row)',
},
'Footer/Logo': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description: 'The footer logo',
},
IconArrow: {
actions: {
eject: 'safe',
Expand Down
73 changes: 72 additions & 1 deletion packages/docusaurus-theme-classic/src/theme-classic.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ declare module '@theme/DocSidebar/Desktop/Content' {
readonly sidebar: readonly PropSidebarItem[];
}

export default function CollapseButton(props: Props): JSX.Element;
export default function Content(props: Props): JSX.Element;
}

declare module '@theme/DocSidebar/Desktop/CollapseButton' {
Expand Down Expand Up @@ -280,6 +280,77 @@ declare module '@theme/Footer' {
export default function Footer(): JSX.Element | null;
}

declare module '@theme/Footer/Logo' {
import type {FooterLogo} from '@docusaurus/theme-common';

export interface Props {
logo: FooterLogo;
}

export default function FooterLogo(props: Props): JSX.Element;
}

declare module '@theme/Footer/Copyright' {
export interface Props {
copyright: string;
}

export default function FooterCopyright(props: Props): JSX.Element;
}

declare module '@theme/Footer/LinkItem' {
import type {FooterLinkItem} from '@docusaurus/theme-common';

export interface Props {
item: FooterLinkItem;
}

export default function FooterLinkItem(props: Props): JSX.Element;
}

declare module '@theme/Footer/Layout' {
import type {ReactNode} from 'react';

export interface Props {
style: 'light' | 'dark';
links: ReactNode;
logo: ReactNode;
copyright: ReactNode;
}

export default function FooterLayout(props: Props): JSX.Element;
}

declare module '@theme/Footer/Links' {
import type {Footer} from '@docusaurus/theme-common';

export interface Props {
links: Footer['links'];
}

export default function FooterLinks(props: Props): JSX.Element;
}

declare module '@theme/Footer/Links/MultiColumn' {
import type {MultiColumnFooter} from '@docusaurus/theme-common';

export interface Props {
columns: MultiColumnFooter['links'];
}

export default function FooterLinksMultiColumn(props: Props): JSX.Element;
}

declare module '@theme/Footer/Links/Simple' {
import type {SimpleFooter} from '@docusaurus/theme-common';

export interface Props {
links: SimpleFooter['links'];
}

export default function FooterLinksSimple(props: Props): JSX.Element;
}

declare module '@theme/Heading' {
import type {ComponentProps} from 'react';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import type {Props} from '@theme/Footer/Copyright';

export default function FooterCopyright({copyright}: Props): JSX.Element {
return (
<div
className="footer__copyright"
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: copyright,
}}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import clsx from 'clsx';
import type {Props} from '@theme/Footer/Layout';

export default function FooterLayout({
style,
links,
logo,
copyright,
}: Props): JSX.Element {
return (
<footer
className={clsx('footer', {
'footer--dark': style === 'dark',
})}>
<div className="container container-fluid">
{links}
{(logo || copyright) && (
<div className="footer__bottom text--center">
{logo && <div className="margin-bottom--sm">{logo}</div>}
{copyright}
</div>
)}
</div>
</footer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';

import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import isInternalUrl from '@docusaurus/isInternalUrl';
import IconExternalLink from '@theme/IconExternalLink';
import type {Props} from '@theme/Footer/LinkItem';

export default function FooterLinkItem({item}: Props): JSX.Element {
const {to, href, label, prependBaseUrlToHref, ...props} = item;
const toUrl = useBaseUrl(to);
const normalizedHref = useBaseUrl(href, {forcePrependBaseUrl: true});

return (
<Link
className="footer__link-item"
{...(href
? {
href: prependBaseUrlToHref ? normalizedHref : href,
}
: {
to: toUrl,
})}
{...props}>
<span>
{label}
{href && !isInternalUrl(href) && <IconExternalLink />}
</span>
</Link>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import type {Props} from '@theme/Footer/Links/MultiColumn';
import LinkItem from '@theme/Footer/LinkItem';

type ColumnType = Props['columns'][number];
type ColumnItemType = ColumnType['items'][number];

function ColumnLinkItem({item}: {item: ColumnItemType}) {
return item.html ? (
<li
className="footer__item"
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: item.html,
}}
/>
) : (
<li key={item.href || item.to} className="footer__item">
<LinkItem item={item} />
</li>
);
}

function Column({column}: {column: ColumnType}) {
return (
<div className="col footer__col">
<div className="footer__title">{column.title}</div>
<ul className="footer__items">
{column.items.map((item, i) => (
<ColumnLinkItem key={i} item={item} />
))}
</ul>
</div>
);
}

export default function FooterLinksMultiColumn({columns}: Props): JSX.Element {
return (
<div className="row footer__links">
{columns.map((column, i) => (
<Column key={i} column={column} />
))}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import type {Props} from '@theme/Footer/Links/Simple';
import LinkItem from '@theme/Footer/LinkItem';

function Separator() {
return <span className="footer__link-separator">·</span>;
}

function SimpleLinkItem({item}: {item: Props['links'][number]}) {
return item.html ? (
<span
className="footer__link-item"
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: item.html,
}}
/>
) : (
<LinkItem item={item} />
);
}

export default function FooterLinksSimple({links}: Props): JSX.Element {
return (
<div className="footer__links text--center">
<div className="footer__links">
{links.map((item, i) => (
<React.Fragment key={i}>
<SimpleLinkItem item={item} />
{links.length !== i + 1 && <Separator />}
</React.Fragment>
))}
</div>
</div>
);
}
21 changes: 21 additions & 0 deletions packages/docusaurus-theme-classic/src/theme/Footer/Links/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';

import {isMultiColumnFooterLinks} from '@docusaurus/theme-common';
import type {Props} from '@theme/Footer/Links';
import FooterLinksMultiColumn from '@theme/Footer/Links/MultiColumn';
import FooterLinksSimple from '@theme/Footer/Links/Simple';

export default function FooterLinks({links}: Props): JSX.Element {
return isMultiColumnFooterLinks(links) ? (
<FooterLinksMultiColumn columns={links} />
) : (
<FooterLinksSimple links={links} />
);
}
Loading

0 comments on commit 1efc6c6

Please sign in to comment.