Skip to content

Commit

Permalink
Add base custom layout in Markdown Support (#1289)
Browse files Browse the repository at this point in the history
  • Loading branch information
carloskelly13 committed Jul 21, 2023
1 parent 1add746 commit 6153f1a
Show file tree
Hide file tree
Showing 18 changed files with 1,234 additions and 2,523 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
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
@@ -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
@@ -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
Expand Up @@ -17,6 +17,7 @@
"broadcast-channel": "https://esm.sh/v121/broadcast-channel@^4.17.0?deps=react@18.2.0",
"history": "https://esm.sh/v121/history@^5.3.0?deps=react@18.2.0",
"kbar": "https://esm.sh/v121/kbar@0.1.0-beta.40?deps=react@18.2.0",
"lodash.clonedeep": "https://esm.sh/v121/lodash.clonedeep@^4.5.0?deps=react@18.2.0",
"mdast-builder": "https://esm.sh/v121/mdast-builder@^1.1.1?deps=react@18.2.0",
"mdast-zone": "https://esm.sh/v121/mdast-zone@^4.0.0?deps=react@18.2.0",
"merge-anything": "https://esm.sh/v121/merge-anything@^3.0.3?deps=react@18.2.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/spectacle/package.json
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
@@ -0,0 +1,18 @@
import React, { PropsWithChildren } from 'react';
import { 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="100%">
{children}
</FlexBox>
);

export const hasLayoutConfig =
(layoutKey: string) => (config?: Record<string, string>) =>
config && 'layout' in config && config.layout === layoutKey;
@@ -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
@@ -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
@@ -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
84 changes: 28 additions & 56 deletions packages/spectacle/src/components/markdown/markdown.tsx
@@ -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));
const Container = styled('div')(compose(position, layout), { height: '100%' });

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
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

1 comment on commit 6153f1a

@vercel
Copy link

@vercel vercel bot commented on 6153f1a Jul 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.