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

Add base custom layout in Markdown Support #1289

Merged
merged 15 commits into from
Jul 21, 2023
Merged
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
node-version: [16.x, 18.x]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
Expand Down
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
strict-peer-dependencies=false
prefer-workspace-packages=true
auto-install-peers=false

# Docusaurus has some phantom dependencies, so specifically hoist those.
public-hoist-pattern[]=@docusaurus/theme-classic
13 changes: 10 additions & 3 deletions examples/md/slides.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
# Spectacle Presentation (MD) 👋
---

# Spectacle Presentation (MD) 👋
These slides are bare Markdown with nothing special.

- `one`
- "two"
- 'three'

---
--- { "layout" : "columns" }

::section

# Write your Spectacle Presentations in Markdown

And use layout primitives to define columns!

::section

## And seamlessly use React Components

**How sweet is that**
**(super sweet)**

---
--- { "layout" : "center" }

![datboi](https://media.giphy.com/media/xohHbwcnOhqbS/giphy.gif)

Expand Down
1 change: 1 addition & 0 deletions examples/one-page/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"broadcast-channel": "https://esm.sh/v121/broadcast-channel@^[email protected]",
"history": "https://esm.sh/v121/history@^[email protected]",
"kbar": "https://esm.sh/v121/[email protected][email protected]",
"lodash.clonedeep": "https://esm.sh/v121/lodash.clonedeep@^[email protected]",
"mdast-builder": "https://esm.sh/v121/mdast-builder@^[email protected]",
"mdast-zone": "https://esm.sh/v121/mdast-zone@^[email protected]",
"merge-anything": "https://esm.sh/v121/merge-anything@^[email protected]",
Expand Down
2 changes: 2 additions & 0 deletions packages/spectacle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"broadcast-channel": "^4.17.0",
"history": "^5.3.0",
"kbar": "0.1.0-beta.40",
"lodash.clonedeep": "^4.5.0",
"mdast-builder": "^1.1.1",
"mdast-zone": "^4.0.0",
"merge-anything": "^3.0.3",
Expand All @@ -44,6 +45,7 @@
"react-dom": ">=18.0.0"
},
"devDependencies": {
"@types/lodash.clonedeep": "^4.5.7",
"@types/mousetrap": "^1.6.8",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.2.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { PropsWithChildren } from 'react';
import { Box, FlexBox } from '../layout-primitives';

export const Columns = ({ children }: PropsWithChildren) => (
<FlexBox flexDirection="row" alignItems="start" flex={1}>
{children}
</FlexBox>
);

export const Center = ({ children }: PropsWithChildren) => (
<FlexBox justifyContent="center" alignItems="center" height="100vh">
<Box>{children}</Box>
</FlexBox>
);

export const hasLayoutConfig =
(layoutKey: string) => (config?: Record<string, string>) =>
config && 'layout' in config && config.layout === layoutKey;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Slide from '../slide/slide';
import React from 'react';
import { Markdown } from './markdown';
import { MarkdownSlideProps } from './markdown-types';
import { Center, Columns, hasLayoutConfig } from './markdown-layout-containers';

export const MarkdownSlide = ({
children,
componentMap,
animateListItems = false,
componentProps = {},
slideConfig,
template: propTemplate,
...rest
}: MarkdownSlideProps) => {
let template = propTemplate;

if (hasLayoutConfig('columns')(slideConfig)) template = { default: Columns };
if (hasLayoutConfig('center')(slideConfig)) template = { default: Center };

return (
<Slide {...rest}>
<Markdown
{...{
componentMap,
template,
animateListItems,
componentProps,
children,
slideConfig
}}
/>
</Slide>
);
};
25 changes: 25 additions & 0 deletions packages/spectacle/src/components/markdown/markdown-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { MarkdownComponentMap } from '../../utils/mdx-component-mapper';
import { ElementType } from 'react';

export type MdComponentProps = { [key: string]: any };

export type CommonMarkdownProps = {
animateListItems?: boolean;
componentProps?: MdComponentProps;
children: string;
};

export type MarkdownSlideProps = CommonMarkdownProps &
MapAndTemplate & { slideConfig?: Record<string, string> };

export type MarkdownSlideSetProps = CommonMarkdownProps & {
slideProps?: Partial<MarkdownSlideProps>[];
};

export type MapAndTemplate = {
componentMap?: MarkdownComponentMap;
template?: {
default: ElementType;
getPropsForAST?: Function;
};
};
3 changes: 2 additions & 1 deletion packages/spectacle/src/components/markdown/markdown.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ReactElement } from 'react';
import { Markdown, MarkdownSlide, MarkdownSlideSet } from './markdown';
import { Markdown, MarkdownSlideSet } from './markdown';
import Deck from '../deck';
import { Heading } from '../typography';
import Slide from '../slide/slide';
import { render } from '@testing-library/react';
import { MarkdownSlide } from './markdown-slide-renderer';

const mountInsideDeck = (tree: ReactElement) => {
return render(<Deck>{tree}</Deck>);
Expand Down
82 changes: 27 additions & 55 deletions packages/spectacle/src/components/markdown/markdown.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable react/display-name */
import Slide from '../slide/slide';
import { DeckContext } from '../deck/deck';
import presenterNotesPlugin from '../../utils/remark-rehype-presenter-notes';
import CodePane, { CodePaneProps } from '../code-pane';
Expand All @@ -12,9 +11,7 @@ import remarkRaw from 'rehype-raw';
import rehype2react from 'rehype-react';
import { isValidElementType } from 'react-is';
import { root as mdRoot } from 'mdast-builder';
import mdxComponentMap, {
MarkdownComponentMap
} from '../../utils/mdx-component-mapper';
import mdxComponentMap from '../../utils/mdx-component-mapper';
import indentNormalizer from '../../utils/indent-normalizer';
import Notes from '../notes';
import { ListItem } from '../../index';
Expand All @@ -29,27 +26,25 @@ import React, {
createElement,
Children
} from 'react';

type MdComponentProps = { [key: string]: any };

type CommonMarkdownProps = {
animateListItems?: boolean;
componentProps?: MdComponentProps;
children: string;
};

type MapAndTemplate = {
componentMap?: MarkdownComponentMap;
template?: {
default: ElementType;
getPropsForAST?: Function;
};
};
import { separateSectionsFromJson } from '../../utils/separate-sections-from-json';
import {
CommonMarkdownProps,
MapAndTemplate,
MarkdownSlideSetProps
} from './markdown-types';
import { MarkdownSlide } from './markdown-slide-renderer';
import {
directiveParserPlugin,
directivesHandlerPlugin
} from '../../utils/remark-rehype-directive';

type MarkdownProps = CommonMarkdownProps & MapAndTemplate;
const Container = styled('div')(compose(position, layout));

export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>(
export const Markdown = forwardRef<
HTMLDivElement,
MarkdownProps & { slideConfig?: Record<string, string> }
>(
(
{
componentMap: userProvidedComponentMap = mdxComponentMap,
Expand All @@ -59,6 +54,7 @@ export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>(
children: rawMarkdownText,
animateListItems = false,
componentProps,
slideConfig,
...props
},
ref
Expand All @@ -78,6 +74,8 @@ export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>(
.use(presenterNotesPlugin, (...notes) => {
extractedNotes.children.push(...notes);
})
.use(directiveParserPlugin)
.use(directivesHandlerPlugin)
.runSync(ast);

// Pass the AST into the provided template function, which returns an object
Expand Down Expand Up @@ -136,6 +134,7 @@ export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>(
return child;
})
: props.children;

return (
<Component {...props} {...(componentProps || {})}>
{children}
Expand Down Expand Up @@ -197,11 +196,12 @@ export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>(
]);

const { children, ...restProps } = templateProps;

return (
<Container ref={ref} {...props}>
<TemplateComponent {...restProps}>
{children}
{slideConfig?.layout === 'columns'
? children.props.children
: children}
{noteElements}
</TemplateComponent>
</Container>
Expand All @@ -217,42 +217,13 @@ const AppearingListItem = (
</Appear>
);

type MarkdownSlideProps = CommonMarkdownProps & MapAndTemplate;

export const MarkdownSlide = ({
children,
componentMap,
template,
animateListItems = false,
componentProps = {},
...rest
}: MarkdownSlideProps) => {
return (
<Slide {...rest}>
<Markdown
{...{
componentMap,
template,
animateListItems,
componentProps,
children
}}
/>
</Slide>
);
};

type MarkdownSlideSetProps = CommonMarkdownProps & {
slideProps?: Partial<MarkdownSlideProps>[];
};

export const MarkdownSlideSet = ({
children: rawMarkdownText,
slideProps = [],
...allSlideProps
}: MarkdownSlideSetProps) => {
const dedentedMarkdownText = indentNormalizer(rawMarkdownText);
const mdSlides = dedentedMarkdownText.split(/\n\s*---\n/);
const mdSlides = separateSectionsFromJson(dedentedMarkdownText);
return (
<>
{mdSlides.map((md, ix) => {
Expand All @@ -261,9 +232,10 @@ export const MarkdownSlideSet = ({
if (slideProps[ix]) {
Object.assign(props, slideProps[ix]);
}
const { jsonObject = {}, content } = md;
return (
<MarkdownSlide key={ix} {...props}>
{md}
<MarkdownSlide key={ix} slideConfig={jsonObject} {...props}>
{content}
</MarkdownSlide>
);
})}
Expand Down
2 changes: 1 addition & 1 deletion packages/spectacle/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ export { DefaultTemplate } from './components/default-template';
export {
Markdown,
MarkdownSlideSet,
MarkdownSlide,
MarkdownPreHelper
} from './components/markdown/markdown';
export { MarkdownSlide } from './components/markdown/markdown-slide-renderer';
export { default as SpectacleLogo } from './components/logo';
export { default as mdxComponentMap } from './utils/mdx-component-mapper';
export type { MarkdownComponentMap } from './utils/mdx-component-mapper';
Expand Down
Loading
Loading