nkzw-tech / remdx

Beautiful Minimalist React & MDX Presentations
MIT License
302 stars 9 forks source link

Add exports for custom `render()` implementations #21

Closed karlhorky closed 2 months ago

karlhorky commented 2 months ago

Addresses https://github.com/nkzw-tech/remdx/issues/17#issuecomment-2143377885

Custom render() would require this reduced code following this change:

import { Deck, defaultTransition, MDXProvider, Slide, Transitions } from '@nkzw/remdx/index.js';
import { ReMDXModule, SlideTransition } from '@nkzw/remdx/types.jsx';
import { createRoot, Root } from 'react-dom/client';

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={{
        // ...DefaultComponents,
        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));
}

And then this usage in 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>

Still quite a bit of code to set a default Container component for all slide decks, but at least a bit better than the original code required for this

cpojer commented 2 months ago

Thanks! Released as 0.14.0.

karlhorky commented 2 months ago

Thanks!

I wrote an issue comment on how to create a custom render() implementation with @nkzw/remdx@0.14.0 in the issue: