Skip to content

Commit 6153f1a

Browse files
Add base custom layout in Markdown Support (#1289)
1 parent 1add746 commit 6153f1a

18 files changed

+1234
-2523
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
runs-on: ubuntu-latest
1919
strategy:
2020
matrix:
21-
node-version: [14.x, 16.x, 18.x]
21+
node-version: [16.x, 18.x]
2222
steps:
2323
- uses: actions/checkout@v2
2424
- uses: actions/setup-node@v2

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
strict-peer-dependencies=false
22
prefer-workspace-packages=true
3+
auto-install-peers=false
34

45
# Docusaurus has some phantom dependencies, so specifically hoist those.
56
public-hoist-pattern[]=@docusaurus/theme-classic

examples/md/slides.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
1-
# Spectacle Presentation (MD) 👋
1+
---
22

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

56
- `one`
67
- "two"
78
- 'three'
89

9-
---
10+
--- { "layout" : "columns" }
11+
12+
::section
1013

1114
# Write your Spectacle Presentations in Markdown
1215

16+
And use layout primitives to define columns!
17+
18+
::section
19+
1320
## And seamlessly use React Components
1421

1522
**How sweet is that**
1623
**(super sweet)**
1724

18-
---
25+
--- { "layout" : "center" }
1926

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

examples/one-page/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"broadcast-channel": "https://esm.sh/v121/broadcast-channel@^[email protected]",
1818
"history": "https://esm.sh/v121/history@^[email protected]",
1919
"kbar": "https://esm.sh/v121/[email protected][email protected]",
20+
"lodash.clonedeep": "https://esm.sh/v121/lodash.clonedeep@^[email protected]",
2021
"mdast-builder": "https://esm.sh/v121/mdast-builder@^[email protected]",
2122
"mdast-zone": "https://esm.sh/v121/mdast-zone@^[email protected]",
2223
"merge-anything": "https://esm.sh/v121/merge-anything@^[email protected]",

packages/spectacle/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"broadcast-channel": "^4.17.0",
2020
"history": "^5.3.0",
2121
"kbar": "0.1.0-beta.40",
22+
"lodash.clonedeep": "^4.5.0",
2223
"mdast-builder": "^1.1.1",
2324
"mdast-zone": "^4.0.0",
2425
"merge-anything": "^3.0.3",
@@ -44,6 +45,7 @@
4445
"react-dom": ">=18.0.0"
4546
},
4647
"devDependencies": {
48+
"@types/lodash.clonedeep": "^4.5.7",
4749
"@types/mousetrap": "^1.6.8",
4850
"@types/react": "^18.2.6",
4951
"@types/react-dom": "^18.2.4",
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React, { PropsWithChildren } from 'react';
2+
import { FlexBox } from '../layout-primitives';
3+
4+
export const Columns = ({ children }: PropsWithChildren) => (
5+
<FlexBox flexDirection="row" alignItems="start" flex={1}>
6+
{children}
7+
</FlexBox>
8+
);
9+
10+
export const Center = ({ children }: PropsWithChildren) => (
11+
<FlexBox justifyContent="center" alignItems="center" height="100%">
12+
{children}
13+
</FlexBox>
14+
);
15+
16+
export const hasLayoutConfig =
17+
(layoutKey: string) => (config?: Record<string, string>) =>
18+
config && 'layout' in config && config.layout === layoutKey;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import Slide from '../slide/slide';
2+
import React from 'react';
3+
import { Markdown } from './markdown';
4+
import { MarkdownSlideProps } from './markdown-types';
5+
import { Center, Columns, hasLayoutConfig } from './markdown-layout-containers';
6+
7+
export const MarkdownSlide = ({
8+
children,
9+
componentMap,
10+
animateListItems = false,
11+
componentProps = {},
12+
slideConfig,
13+
template: propTemplate,
14+
...rest
15+
}: MarkdownSlideProps) => {
16+
let template = propTemplate;
17+
18+
if (hasLayoutConfig('columns')(slideConfig)) template = { default: Columns };
19+
if (hasLayoutConfig('center')(slideConfig)) template = { default: Center };
20+
21+
return (
22+
<Slide {...rest}>
23+
<Markdown
24+
{...{
25+
componentMap,
26+
template,
27+
animateListItems,
28+
componentProps,
29+
children,
30+
slideConfig
31+
}}
32+
/>
33+
</Slide>
34+
);
35+
};
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { MarkdownComponentMap } from '../../utils/mdx-component-mapper';
2+
import { ElementType } from 'react';
3+
4+
export type MdComponentProps = { [key: string]: any };
5+
6+
export type CommonMarkdownProps = {
7+
animateListItems?: boolean;
8+
componentProps?: MdComponentProps;
9+
children: string;
10+
};
11+
12+
export type MarkdownSlideProps = CommonMarkdownProps &
13+
MapAndTemplate & { slideConfig?: Record<string, string> };
14+
15+
export type MarkdownSlideSetProps = CommonMarkdownProps & {
16+
slideProps?: Partial<MarkdownSlideProps>[];
17+
};
18+
19+
export type MapAndTemplate = {
20+
componentMap?: MarkdownComponentMap;
21+
template?: {
22+
default: ElementType;
23+
getPropsForAST?: Function;
24+
};
25+
};

packages/spectacle/src/components/markdown/markdown.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { ReactElement } from 'react';
2-
import { Markdown, MarkdownSlide, MarkdownSlideSet } from './markdown';
2+
import { Markdown, MarkdownSlideSet } from './markdown';
33
import Deck from '../deck';
44
import { Heading } from '../typography';
55
import Slide from '../slide/slide';
66
import { render } from '@testing-library/react';
7+
import { MarkdownSlide } from './markdown-slide-renderer';
78

89
const mountInsideDeck = (tree: ReactElement) => {
910
return render(<Deck>{tree}</Deck>);

packages/spectacle/src/components/markdown/markdown.tsx

Lines changed: 28 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
/* eslint-disable react/display-name */
2-
import Slide from '../slide/slide';
32
import { DeckContext } from '../deck/deck';
43
import presenterNotesPlugin from '../../utils/remark-rehype-presenter-notes';
54
import CodePane, { CodePaneProps } from '../code-pane';
@@ -12,9 +11,7 @@ import remarkRaw from 'rehype-raw';
1211
import rehype2react from 'rehype-react';
1312
import { isValidElementType } from 'react-is';
1413
import { root as mdRoot } from 'mdast-builder';
15-
import mdxComponentMap, {
16-
MarkdownComponentMap
17-
} from '../../utils/mdx-component-mapper';
14+
import mdxComponentMap from '../../utils/mdx-component-mapper';
1815
import indentNormalizer from '../../utils/indent-normalizer';
1916
import Notes from '../notes';
2017
import { ListItem } from '../../index';
@@ -29,27 +26,25 @@ import React, {
2926
createElement,
3027
Children
3128
} from 'react';
32-
33-
type MdComponentProps = { [key: string]: any };
34-
35-
type CommonMarkdownProps = {
36-
animateListItems?: boolean;
37-
componentProps?: MdComponentProps;
38-
children: string;
39-
};
40-
41-
type MapAndTemplate = {
42-
componentMap?: MarkdownComponentMap;
43-
template?: {
44-
default: ElementType;
45-
getPropsForAST?: Function;
46-
};
47-
};
29+
import { separateSectionsFromJson } from '../../utils/separate-sections-from-json';
30+
import {
31+
CommonMarkdownProps,
32+
MapAndTemplate,
33+
MarkdownSlideSetProps
34+
} from './markdown-types';
35+
import { MarkdownSlide } from './markdown-slide-renderer';
36+
import {
37+
directiveParserPlugin,
38+
directivesHandlerPlugin
39+
} from '../../utils/remark-rehype-directive';
4840

4941
type MarkdownProps = CommonMarkdownProps & MapAndTemplate;
50-
const Container = styled('div')(compose(position, layout));
42+
const Container = styled('div')(compose(position, layout), { height: '100%' });
5143

52-
export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>(
44+
export const Markdown = forwardRef<
45+
HTMLDivElement,
46+
MarkdownProps & { slideConfig?: Record<string, string> }
47+
>(
5348
(
5449
{
5550
componentMap: userProvidedComponentMap = mdxComponentMap,
@@ -59,6 +54,7 @@ export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>(
5954
children: rawMarkdownText,
6055
animateListItems = false,
6156
componentProps,
57+
slideConfig,
6258
...props
6359
},
6460
ref
@@ -78,6 +74,8 @@ export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>(
7874
.use(presenterNotesPlugin, (...notes) => {
7975
extractedNotes.children.push(...notes);
8076
})
77+
.use(directiveParserPlugin)
78+
.use(directivesHandlerPlugin)
8179
.runSync(ast);
8280

8381
// Pass the AST into the provided template function, which returns an object
@@ -136,6 +134,7 @@ export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>(
136134
return child;
137135
})
138136
: props.children;
137+
139138
return (
140139
<Component {...props} {...(componentProps || {})}>
141140
{children}
@@ -197,11 +196,12 @@ export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>(
197196
]);
198197

199198
const { children, ...restProps } = templateProps;
200-
201199
return (
202200
<Container ref={ref} {...props}>
203201
<TemplateComponent {...restProps}>
204-
{children}
202+
{slideConfig?.layout === 'columns'
203+
? children.props.children
204+
: children}
205205
{noteElements}
206206
</TemplateComponent>
207207
</Container>
@@ -217,42 +217,13 @@ const AppearingListItem = (
217217
</Appear>
218218
);
219219

220-
type MarkdownSlideProps = CommonMarkdownProps & MapAndTemplate;
221-
222-
export const MarkdownSlide = ({
223-
children,
224-
componentMap,
225-
template,
226-
animateListItems = false,
227-
componentProps = {},
228-
...rest
229-
}: MarkdownSlideProps) => {
230-
return (
231-
<Slide {...rest}>
232-
<Markdown
233-
{...{
234-
componentMap,
235-
template,
236-
animateListItems,
237-
componentProps,
238-
children
239-
}}
240-
/>
241-
</Slide>
242-
);
243-
};
244-
245-
type MarkdownSlideSetProps = CommonMarkdownProps & {
246-
slideProps?: Partial<MarkdownSlideProps>[];
247-
};
248-
249220
export const MarkdownSlideSet = ({
250221
children: rawMarkdownText,
251222
slideProps = [],
252223
...allSlideProps
253224
}: MarkdownSlideSetProps) => {
254225
const dedentedMarkdownText = indentNormalizer(rawMarkdownText);
255-
const mdSlides = dedentedMarkdownText.split(/\n\s*---\n/);
226+
const mdSlides = separateSectionsFromJson(dedentedMarkdownText);
256227
return (
257228
<>
258229
{mdSlides.map((md, ix) => {
@@ -261,9 +232,10 @@ export const MarkdownSlideSet = ({
261232
if (slideProps[ix]) {
262233
Object.assign(props, slideProps[ix]);
263234
}
235+
const { jsonObject = {}, content } = md;
264236
return (
265-
<MarkdownSlide key={ix} {...props}>
266-
{md}
237+
<MarkdownSlide key={ix} slideConfig={jsonObject} {...props}>
238+
{content}
267239
</MarkdownSlide>
268240
);
269241
})}

0 commit comments

Comments
 (0)