Style slide container
karlhorky opened this issue · comments
I came across a need to style the slide container for all slides in all slide decks (making the default justifyContent: 'center'
instead of justifyContent: 'flex-start'
.
Looking at the readme, the Usage section mentions I can "customize my style via" without mentioning what I can do:
Workaround
Currently I'm using patch-package
to alter node_modules/@nkzw/remdx/index.js
the justifyContent: "flex-start"
to justifyContent: "center"
, as so:
...swipeHandler,
children: /* @__PURE__ */ jsx3(
"div",
{
style: {
display: "flex",
flex: 1,
flexDirection: "column",
justifyContent: "center",
padding
},
children: /* @__PURE__ */ jsx3(Suspense, { children })
}
)
But this seems to be a bit of a large hammer for the job - is there an easier way?
It seemed like a good default. The way I center content in slides is by using a <Center>
component like here: https://github.com/nkzw-tech/turn-based-ai-talk/blob/main/slides.re.mdx#L94-L110. As said previously, it would be great to have a standard library in a separate package for this sort of stuff.
However, your use-case is valid. Here are some possibilities beyond using the <Center>
component option:
render
andslidesToComponent
are just basic wrappers to simplify rendering a slide deck. You can build your own version ofslidesToComponent.tsx
to compose your deck, and you could simply wrap all slide components with a<Center />
component in your version ofslidesToComponent.tsx
.- We could remove the nested divs in
Slide.tsx
and collapse both into one, after which CSS styles via exporting aTheme
will allow you to customizejustifyContent
directly. - We could add a prop to
Slide.tsx
to customize the inner div's styles and then either you can use a customslidesToComponent.tsx
or we can add a way to export the default style via a config option in each slide deck.
There are probably other solutions, but those are some that I can think of.
Nice, it sounds like there are a few ways forward here then.
As a user of ReMDX, I would expect I could customize:
-
Global styles / theme for all slide decks created by an instance of ReMDX (useful in the case of multiple slide decks)
Maybe this could be an argument to the
render()
function:
remdx/packages/create-remdx/template/index.html
Lines 1 to 22 in 2849d4b
-
Deck styles / theme for all slides in a single slide deck - to alter the global styling applied to a single slide deck
Potential option: Exporting
Theme
via front matter + collapsing nested divs as you mentioned above in your 2nd bullet point -
Slide styles / theme for a single slide - to alter the styles
Here components like
<Center />
are already maybe enough... (maybe doesn't need separate front matter option or similar for each slide?)
For my immediate use case (making all slide decks uniform), I am primarily interested in number 1. above. However, I can see the use cases of 2. and 3. as well.
What are your thoughts in going these directions?
In fa5802d I added the ability to wrap slides with a custom container via:
export { Container } from './Container';
Container.tsx can look like this:
import { ReactNode } from 'react';
export const Container = ({ children }: { children: ReactNode }) => (
<div style={{ background: 'red' }}>{children}</div>
);
Every slide in the current deck will use this container. The <Slide>
component now takes a component as a container
prop with children
and style
props in case you have a custom renderer.
Released in 0.11.0.
Great, thanks!
I'm just trying to figure out how to use this version, to try to achieve "1. Global styles / theme for all slide decks created by an instance of ReMDX":
The
<Slide>
component now takes a component as acontainer
prop withchildren
andstyle
props in case you have a custom renderer.
I copied out the code from remdx/src/render.tsx
and remdx/src/slidesToComponent.tsx
to try to create my own render.tsx
file, kind of like this below:
import { MDXProvider } from '@mdx-js/react';
import Slide from '@nkzw/remdx/index';
// ... more copied imports, some of them which fail ...
export default async function slidesToComponent(module: Promise<ReMDXModule>) {
const {
Components,
Container,
Themes,
Transitions,
default: slides,
} = await module;
return (
<MDXProvider components={{ ...DefaultComponents, ...Components }}>
<Deck
slides={slides.map(({ Component, data }, index) => (
<Slide
container={Container}
id={index}
image={data?.image}
key={index}
style={Themes?.[data?.theme] || Themes?.default}
transition={
Transitions?.[data?.transition] ||
DefaultTransitions[data?.transition] ||
undefined
}
>
<Component />
</Slide>
))}
/>
</MDXProvider>
);
}
const roots = new WeakMap<HTMLElement, Root>();
export default async function render(
element: HTMLElement | null,
module: Promise<ReMDXModule>,
) {
if (!element) {
throw new Error(`remdx: The provided DOM node could not be found.`);
}
if (!roots.has(element)) {
roots.set(element, createRoot(element));
}
roots.get(element)?.render(await slidesToComponent(module));
}
Is this what you mean?
Ideally I would just want to be able to use the provided render()
from @nkzw/remdx
and pass in a Container
parameter / option.
But if this is the recommended approach, to copy+paste the render()
and slidesToComponent()
functions from ReMDX, I can also do this. I would want to avoid copy+pasting too much, eg. I still want to rely on the other functions and components from ReMDX.
If this is the recommended approach, I guess it would be good to:
- add types for
index.js
, eg. atindex.d.ts
: https://unpkg.com/browse/@nkzw/remdx@0.11.0/index.d.ts - export all non-exported identifiers, for example
Components_default
, used here:
I tried to fix the errors and missing exports
index.html
<!doctype html>
<html>
<!-- ... more <head> things here ... -->
<body>
<div id="app"></div>
<script type="module">
import '@nkzw/remdx/style.css';
import { render } from './render.tsx';
render(
document.getElementById('app'),
import('./slides.re.mdx'),
);
</script>
</body>
</html>
render.tsx
import { MDXProvider } from '@mdx-js/react';
// @ts-expect-error
import { Deck, Slide } from '@nkzw/remdx/index.js';
import { ReMDXModule, SlideTransition } from '@nkzw/remdx/types.jsx';
import { createRoot, Root } from 'react-dom/client';
const defaultTransition = {
enter: {
opacity: 1,
transform: 'translateX(0%)',
},
from: {
opacity: 0,
transform: 'translateX(100%)',
},
leave: {
opacity: 1,
transform: 'translateX(-100%)',
},
};
const defaultTransitions: Record<string, SlideTransition> = {
default: defaultTransition,
leaveOnly: {
enter: {
transform: 'translateX(0%)',
},
from: {},
leave: {
transform: 'translateX(-100%)',
},
},
none: {
enter: {},
from: {},
leave: {},
},
opacity: {
enter: {
opacity: 1,
},
from: {
opacity: 0,
},
leave: {
opacity: 1,
},
},
transformRight: {
enter: {
transform: 'translateX(0%)',
},
from: {
transform: 'translateX(100%)',
},
leave: {
transform: 'translateX(-100%)',
},
},
};
type ContainerProps = {
children: React.ReactNode;
};
function Container(props: ContainerProps) {
return (
<div className="p-[5vh 8vw] flex flex-1 flex-col justify-center">
{props.children}
</div>
);
}
function Image({
src: source,
...props
}: React.DetailedHTMLProps<
React.ImgHTMLAttributes<HTMLImageElement>,
HTMLImageElement
>) {
if (!source) {
return null;
}
const [src, query] = source.split('?');
return (
<img
loading="lazy"
src={src}
style={Object.fromEntries(new URLSearchParams(query))}
{...props}
/>
);
}
async function slidesToComponent(module: Promise<ReMDXModule>) {
const {
/* eslint-disable @typescript-eslint/naming-convention --
React Components can be PascalCase */
Components,
Themes,
Transitions,
/* eslint-enable @typescript-eslint/naming-convention --
React Components can be PascalCase */
default: slides,
} = await module;
return (
<MDXProvider components={{ img: Image, ...Components }}>
<Deck
slides={slides.map(({ Component, data }, index) => (
<Slide
// eslint-disable-next-line react/no-array-index-key -- Slide order will not change
key={`slide-${index}`}
container={Container}
id={index}
image={data.image}
style={(data.theme && Themes?.[data.theme]) || Themes?.default}
transition={
(data.transition &&
(Transitions?.[data.transition] ||
defaultTransitions[data.transition])) ||
undefined
}
>
<Component />
</Slide>
))}
/>
</MDXProvider>
);
}
const roots = new WeakMap<HTMLElement, Root>();
export async function render(
element: HTMLElement | null,
module: Promise<ReMDXModule>,
) {
if (!element) {
throw new Error('remdx: The provided DOM node could not be found.');
}
if (!roots.has(element)) {
roots.set(element, createRoot(element));
}
roots.get(element)?.render(await slidesToComponent(module));
}
This no longer causes a build error, but I have runtime errors in the browser console when trying this approach:
slides.re.mdx:246 Uncaught Error: Expected component `Center` to be defined: you likely forgot to import, pass, or provide it.
It’s referenced in your code at `12:1-14:10`
at _missingMdxReference (slides.re.mdx:246:9)
at _createMdxContent (slides.re.mdx:188:16)
at MDXContent (slides.re.mdx:243:14)
at renderWithHooks (chunk-UVTSGMSA.js?v=d96c63a4:12171:26)
at mountIndeterminateComponent (chunk-UVTSGMSA.js?v=d96c63a4:14921:21)
at beginWork (chunk-UVTSGMSA.js?v=d96c63a4:15902:22)
at HTMLUnknownElement.callCallback2 (chunk-UVTSGMSA.js?v=d96c63a4:3674:22)
at Object.invokeGuardedCallbackDev (chunk-UVTSGMSA.js?v=d96c63a4:3699:24)
at invokeGuardedCallback (chunk-UVTSGMSA.js?v=d96c63a4:3733:39)
at beginWork$1 (chunk-UVTSGMSA.js?v=d96c63a4:19761:15)
_missingMdxReference @ slides.re.mdx:246
_createMdxContent @ slides.re.mdx:188
MDXContent @ slides.re.mdx:243
renderWithHooks @ chunk-UVTSGMSA.js?v=d96c63a4:12171
mountIndeterminateComponent @ chunk-UVTSGMSA.js?v=d96c63a4:14921
beginWork @ chunk-UVTSGMSA.js?v=d96c63a4:15902
callCallback2 @ chunk-UVTSGMSA.js?v=d96c63a4:3674
invokeGuardedCallbackDev @ chunk-UVTSGMSA.js?v=d96c63a4:3699
invokeGuardedCallback @ chunk-UVTSGMSA.js?v=d96c63a4:3733
beginWork$1 @ chunk-UVTSGMSA.js?v=d96c63a4:19761
performUnitOfWork @ chunk-UVTSGMSA.js?v=d96c63a4:19194
workLoopSync @ chunk-UVTSGMSA.js?v=d96c63a4:19133
renderRootSync @ chunk-UVTSGMSA.js?v=d96c63a4:19112
performConcurrentWorkOnRoot @ chunk-UVTSGMSA.js?v=d96c63a4:18674
workLoop @ chunk-UVTSGMSA.js?v=d96c63a4:197
flushWork @ chunk-UVTSGMSA.js?v=d96c63a4:176
performWorkUntilDeadline @ chunk-UVTSGMSA.js?v=d96c63a4:384
Show 14 more frames
Show less
slides.re.mdx:336 Uncaught Error: Expected component `NotRecommended` to be defined: you likely forgot to import, pass, or provide it.
It’s referenced in your code at `16:1-16:19`
at _missingMdxReference (slides.re.mdx:336:9)
at _createMdxContent (slides.re.mdx:264:24)
at MDXContent (slides.re.mdx:333:14)
at renderWithHooks (chunk-UVTSGMSA.js?v=d96c63a4:12171:26)
at mountIndeterminateComponent (chunk-UVTSGMSA.js?v=d96c63a4:14921:21)
at beginWork (chunk-UVTSGMSA.js?v=d96c63a4:15902:22)
at HTMLUnknownElement.callCallback2 (chunk-UVTSGMSA.js?v=d96c63a4:3674:22)
at Object.invokeGuardedCallbackDev (chunk-UVTSGMSA.js?v=d96c63a4:3699:24)
at invokeGuardedCallback (chunk-UVTSGMSA.js?v=d96c63a4:3733:39)
at beginWork$1 (chunk-UVTSGMSA.js?v=d96c63a4:19761:15)
_missingMdxReference @ slides.re.mdx:336
_createMdxContent @ slides.re.mdx:264
MDXContent @ slides.re.mdx:333
renderWithHooks @ chunk-UVTSGMSA.js?v=d96c63a4:12171
mountIndeterminateComponent @ chunk-UVTSGMSA.js?v=d96c63a4:14921
beginWork @ chunk-UVTSGMSA.js?v=d96c63a4:15902
callCallback2 @ chunk-UVTSGMSA.js?v=d96c63a4:3674
invokeGuardedCallbackDev @ chunk-UVTSGMSA.js?v=d96c63a4:3699
invokeGuardedCallback @ chunk-UVTSGMSA.js?v=d96c63a4:3733
beginWork$1 @ chunk-UVTSGMSA.js?v=d96c63a4:19761
performUnitOfWork @ chunk-UVTSGMSA.js?v=d96c63a4:19194
workLoopSync @ chunk-UVTSGMSA.js?v=d96c63a4:19133
renderRootSync @ chunk-UVTSGMSA.js?v=d96c63a4:19112
performConcurrentWorkOnRoot @ chunk-UVTSGMSA.js?v=d96c63a4:18674
workLoop @ chunk-UVTSGMSA.js?v=d96c63a4:197
flushWork @ chunk-UVTSGMSA.js?v=d96c63a4:176
performWorkUntilDeadline @ chunk-UVTSGMSA.js?v=d96c63a4:384
Show 14 more frames
Show less
slides.re.mdx:389 Uncaught Error: Expected component `Content` to be defined: you likely forgot to import, pass, or provide it.
It’s referenced in your code at `10:1-10:51`
at _missingMdxReference (slides.re.mdx:389:9)
at _createMdxContent (slides.re.mdx:351:17)
at MDXContent (slides.re.mdx:386:14)
at renderWithHooks (chunk-UVTSGMSA.js?v=d96c63a4:12171:26)
at mountIndeterminateComponent (chunk-UVTSGMSA.js?v=d96c63a4:14921:21)
at beginWork (chunk-UVTSGMSA.js?v=d96c63a4:15902:22)
at HTMLUnknownElement.callCallback2 (chunk-UVTSGMSA.js?v=d96c63a4:3674:22)
at Object.invokeGuardedCallbackDev (chunk-UVTSGMSA.js?v=d96c63a4:3699:24)
at invokeGuardedCallback (chunk-UVTSGMSA.js?v=d96c63a4:3733:39)
at beginWork$1 (chunk-UVTSGMSA.js?v=d96c63a4:19761:15)
It seems like it's having troubles resolving the components in the await(module).Components
- although if I console.log()
them, I can see the object... 🤔
Components {Center: ƒ, Columns: ƒ, Content: ƒ, HeadsUp: ƒ, NewTerm: ƒ, …}
Anyway, maybe I'm going in the wrong direction here anyway, in case this is not really the recommended approach for "1. Global styles / theme for all slide decks created by an instance of ReMDX".
imho the best solution is to have one module that you export from at the top of each slide deck despite leading to one duplicated line across all slide decks.
export { Components, Container, Them } from 'my-remdx-setup';
If you don't love that, then your solution should work too. It's hard to make out what the issue is from the pasted code, if you put a full repo together that shows the issue, I'm happy to help debug it.
your solution should work too. It's hard to make out what the issue is from the pasted code, if you put a full repo together that shows the issue, I'm happy to help debug it.
Alright, I put together a reproduction repo for the custom render()
problems I described above at https://github.com/karlhorky/repro-remdx-custom-render .
I used the latest versions of everything (also re-copy+pasted from new ReMDX packages), and made minimal changes to the create-remdx
starter files:
- Added
render.tsx
with the changes described in my comment above - Changed the import path in
index.html
- Added
<Center># Welcome to ReMDX</Center>
inslides.re.mdx
Error message:
slides.re.mdx:42 Uncaught Error: Expected component `Center` to be defined: you likely forgot to import, pass, or provide it.
It’s referenced in your code at `6:1-6:36`
at _missingMdxReference (slides.re.mdx:42:9)
at _createMdxContent (slides.re.mdx:16:16)
at MDXContent (slides.re.mdx:39:14)
at renderWithHooks (chunk-EOWBSJ4E.js?v=59567093:11566:26)
at mountIndeterminateComponent (chunk-EOWBSJ4E.js?v=59567093:14944:21)
at beginWork (chunk-EOWBSJ4E.js?v=59567093:15932:22)
at HTMLUnknownElement.callCallback2 (chunk-EOWBSJ4E.js?v=59567093:3672:22)
at Object.invokeGuardedCallbackDev (chunk-EOWBSJ4E.js?v=59567093:3697:24)
at invokeGuardedCallback (chunk-EOWBSJ4E.js?v=59567093:3731:39)
at beginWork$1 (chunk-EOWBSJ4E.js?v=59567093:19791:15)
_missingMdxReference @ slides.re.mdx:42
_createMdxContent @ slides.re.mdx:16
MDXContent @ slides.re.mdx:39
renderWithHooks @ chunk-EOWBSJ4E.js?v=59567093:11566
mountIndeterminateComponent @ chunk-EOWBSJ4E.js?v=59567093:14944
beginWork @ chunk-EOWBSJ4E.js?v=59567093:15932
callCallback2 @ chunk-EOWBSJ4E.js?v=59567093:3672
invokeGuardedCallbackDev @ chunk-EOWBSJ4E.js?v=59567093:3697
invokeGuardedCallback @ chunk-EOWBSJ4E.js?v=59567093:3731
beginWork$1 @ chunk-EOWBSJ4E.js?v=59567093:19791
performUnitOfWork @ chunk-EOWBSJ4E.js?v=59567093:19224
workLoopSync @ chunk-EOWBSJ4E.js?v=59567093:19163
renderRootSync @ chunk-EOWBSJ4E.js?v=59567093:19142
performConcurrentWorkOnRoot @ chunk-EOWBSJ4E.js?v=59567093:18704
workLoop @ chunk-EOWBSJ4E.js?v=59567093:195
flushWork @ chunk-EOWBSJ4E.js?v=59567093:174
performWorkUntilDeadline @ chunk-EOWBSJ4E.js?v=59567093:382
Show 14 more frames
Show less
slides.re.mdx:42 Uncaught Error: Expected component `Center` to be defined: you likely forgot to import, pass, or provide it.
It’s referenced in your code at `6:1-6:36`
at _missingMdxReference (slides.re.mdx:42:9)
at _createMdxContent (slides.re.mdx:16:16)
at MDXContent (slides.re.mdx:39:14)
at renderWithHooks (chunk-EOWBSJ4E.js?v=59567093:11566:26)
at mountIndeterminateComponent (chunk-EOWBSJ4E.js?v=59567093:14944:21)
at beginWork (chunk-EOWBSJ4E.js?v=59567093:15932:22)
at HTMLUnknownElement.callCallback2 (chunk-EOWBSJ4E.js?v=59567093:3672:22)
at Object.invokeGuardedCallbackDev (chunk-EOWBSJ4E.js?v=59567093:3697:24)
at invokeGuardedCallback (chunk-EOWBSJ4E.js?v=59567093:3731:39)
at beginWork$1 (chunk-EOWBSJ4E.js?v=59567093:19791:15)
_missingMdxReference @ slides.re.mdx:42
_createMdxContent @ slides.re.mdx:16
MDXContent @ slides.re.mdx:39
renderWithHooks @ chunk-EOWBSJ4E.js?v=59567093:11566
mountIndeterminateComponent @ chunk-EOWBSJ4E.js?v=59567093:14944
beginWork @ chunk-EOWBSJ4E.js?v=59567093:15932
callCallback2 @ chunk-EOWBSJ4E.js?v=59567093:3672
invokeGuardedCallbackDev @ chunk-EOWBSJ4E.js?v=59567093:3697
invokeGuardedCallback @ chunk-EOWBSJ4E.js?v=59567093:3731
beginWork$1 @ chunk-EOWBSJ4E.js?v=59567093:19791
performUnitOfWork @ chunk-EOWBSJ4E.js?v=59567093:19224
workLoopSync @ chunk-EOWBSJ4E.js?v=59567093:19163
renderRootSync @ chunk-EOWBSJ4E.js?v=59567093:19142
recoverFromConcurrentError @ chunk-EOWBSJ4E.js?v=59567093:18762
performConcurrentWorkOnRoot @ chunk-EOWBSJ4E.js?v=59567093:18710
workLoop @ chunk-EOWBSJ4E.js?v=59567093:195
flushWork @ chunk-EOWBSJ4E.js?v=59567093:174
performWorkUntilDeadline @ chunk-EOWBSJ4E.js?v=59567093:382
Show 15 more frames
Show less
chunk-EOWBSJ4E.js?v=59567093:14050 The above error occurred in the <MDXContent> component:
at MDXContent (http://localhost:5173/slides.re.mdx?t=1717160128331:27:8)
at MDXContentWrapper (http://localhost:5173/slides.re.mdx?t=1717160128331:44:14)
at Suspense
at div
at Container (http://localhost:5173/node_modules/.vite/deps/chunk-KTCQYNXE.js?v=59567093:4977:39)
at div
at div
at http://localhost:5173/node_modules/.vite/deps/chunk-KTCQYNXE.js?v=59567093:2550:51
at Slide (http://localhost:5173/node_modules/.vite/deps/chunk-KTCQYNXE.js?v=59567093:4975:3)
at div
at div
at Deck (http://localhost:5173/node_modules/.vite/deps/chunk-KTCQYNXE.js?v=59567093:1238:3)
at MDXProvider (http://localhost:5173/node_modules/.vite/deps/@mdx-js_react.js?v=59567093:24:18)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
logCapturedError @ chunk-EOWBSJ4E.js?v=59567093:14050
update.callback @ chunk-EOWBSJ4E.js?v=59567093:14070
callCallback @ chunk-EOWBSJ4E.js?v=59567093:11266
commitUpdateQueue @ chunk-EOWBSJ4E.js?v=59567093:11283
commitLayoutEffectOnFiber @ chunk-EOWBSJ4E.js?v=59567093:17113
commitLayoutMountEffects_complete @ chunk-EOWBSJ4E.js?v=59567093:18006
commitLayoutEffects_begin @ chunk-EOWBSJ4E.js?v=59567093:17995
commitLayoutEffects @ chunk-EOWBSJ4E.js?v=59567093:17946
commitRootImpl @ chunk-EOWBSJ4E.js?v=59567093:19379
commitRoot @ chunk-EOWBSJ4E.js?v=59567093:19303
finishConcurrentRender @ chunk-EOWBSJ4E.js?v=59567093:18786
performConcurrentWorkOnRoot @ chunk-EOWBSJ4E.js?v=59567093:18744
workLoop @ chunk-EOWBSJ4E.js?v=59567093:195
flushWork @ chunk-EOWBSJ4E.js?v=59567093:174
performWorkUntilDeadline @ chunk-EOWBSJ4E.js?v=59567093:382
Show 15 more frames
Show less
chunk-EOWBSJ4E.js?v=59567093:19439 Uncaught Error: Expected component `Center` to be defined: you likely forgot to import, pass, or provide it.
It’s referenced in your code at `6:1-6:36`
at _missingMdxReference (slides.re.mdx:42:9)
at _createMdxContent (slides.re.mdx:16:16)
at MDXContent (slides.re.mdx:39:14)
at renderWithHooks (chunk-EOWBSJ4E.js?v=59567093:11566:26)
at mountIndeterminateComponent (chunk-EOWBSJ4E.js?v=59567093:14944:21)
at beginWork (chunk-EOWBSJ4E.js?v=59567093:15932:22)
at beginWork$1 (chunk-EOWBSJ4E.js?v=59567093:19779:22)
at performUnitOfWork (chunk-EOWBSJ4E.js?v=59567093:19224:20)
at workLoopSync (chunk-EOWBSJ4E.js?v=59567093:19163:13)
at renderRootSync (chunk-EOWBSJ4E.js?v=59567093:19142:15)
your solution should work too
Since you alluded above to this being the recommended approach, maybe I should also do another PR to make this pattern easier to implement with less copy/pasting?
I wrote some first recommendations at the bottom of this comment above:
If this is the recommended approach, I guess it would be good to:
- add types for
index.js
, eg. atindex.d.ts
: unpkg.com/browse/@nkzw/remdx@0.11.0/index.d.ts- export all non-exported identifiers, for example
Components_default
, used here:
The issue is that remdx bundles MDXProvider
, so you are bringing in a different version of MDX's context. This means that slides cannot find the right context to receive the list of components from. This can be fixed by changing ReMDX to export MDXProvider
as well, and using that instead of the @mdx-js/react
version. See cpojer/repro-remdx-custom-render@60ee3cb for the fixed version of your repo.
The default remdx setup only exports the image component. I don't think we need to export that right now tbh, people can copy-paste that one since it's only a few lines of code. I'm ok with exporting other things that aren't there yet.
Ok nice, thanks!
Then I'll do a PR to export some things to make it simpler to make a custom render()
Custom slides <Container>
for multiple decks (with custom render()
)
Now that #21 has been merged and published as @nkzw/remdx@0.14.0
, it's possible to create a custom <Container>
for multiple slide decks like this:
Repository:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ReMDX</title>
<meta name="viewport" content="width=device-width,
user-scalable=no, initial-scale=1, viewport-fit=cover" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style"
content="default" />
</head>
<body>
<div id="app"></div>
<script type="module">
import '@nkzw/remdx/style.css';
import { render } from './render';
render(document.getElementById('app'), import('./slides.re.mdx'));
</script>
</body>
</html>
render.tsx
import { Deck, MDXProvider, Slide, Transitions } from '@nkzw/remdx/index.js';
import { ReMDXModule } from '@nkzw/remdx/types.jsx';
import { createRoot, Root } from 'react-dom/client';
// Copy the Image component from ReMDX, because it is not exported
function Image({
src: source,
...props
}: React.DetailedHTMLProps<
React.ImgHTMLAttributes<HTMLImageElement>,
HTMLImageElement
>) {
if (!source) {
return null;
}
const [src, query] = source.split('?');
return (
<img
loading="lazy"
src={src}
style={Object.fromEntries(new URLSearchParams(query))}
{...props}
/>
);
}
async function slidesToComponent(module: Promise<ReMDXModule>) {
const {
Components,
Container,
Themes,
Transitions: slidesTransitions,
default: slides,
} = await module;
return (
<MDXProvider
components={{
// Copy the default components (...DefaultComponents)
// from ReMDX, because they are not exported
img: Image,
...Components,
}}
>
<Deck
slides={slides.map(({ Component, data }, index) => (
<Slide
container={Container}
id={index}
image={data?.image}
key={index}
style={Themes?.[data?.theme] || Themes?.default}
transition={
slidesTransitions?.[data?.transition] ||
Transitions[data?.transition] ||
undefined
}
>
<Component />
</Slide>
))}
/>
</MDXProvider>
);
}
const roots = new WeakMap<HTMLElement, Root>();
export async function render(
element: HTMLElement | null,
module: Promise<ReMDXModule>,
) {
if (!element) {
throw new Error(`remdx: The provided DOM node could not be found.`);
}
if (!roots.has(element)) {
roots.set(element, createRoot(element));
}
roots.get(element)?.render(await slidesToComponent(module));
}
Oh, thinking about this more, I also came up with an alternative - import a custom Container
in the index.html
file and pass this along with the slide decks module:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ReMDX</title>
<meta name="viewport" content="width=device-width,
user-scalable=no, initial-scale=1, viewport-fit=cover" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style"
content="default" />
</head>
<body>
<div id="app"></div>
<script type="module">
import '@nkzw/remdx/style.css';
- import { render } from './render';
+ import { render } from '@nkzw/remdx';
+ import Container from './Container';
- render(document.getElementById('app'), import('./slides.re.mdx'));
+ render(
+ document.getElementById('app'),
+ (async () => {
+ const slides = await import('./slides.re.mdx');
+ return {
+ Container,
+ ...slides,
+ };
+ })(),
+ );
</script>
</body>
</html>
Commit: karlhorky/repro-remdx-custom-render@b09b074
Do you see any downsides with this approach? Seems a lot more minimal than the custom render()
, if you don't need full control, but only want to override the Container
... 🤔
I didn't think of that, seems good!