Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(theme-classic): split theme footer into smaller components + swizzle config #6894

Merged
merged 8 commits into from
Mar 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
);
}
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