README.md

# NotionRenderer

**This project's author has no affiliation with Notion**

Want to render notion API blocks into HTML? This library is for you.

## Installation

This package can be installed by adding `notion_renderer` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:notion_renderer, "~> 0.1.0"}
  ]
end
```

## TODO

- [ ] Better testing strategy and exhaustive testing
- [ ] Hex documentation (currently documentation is in this file)

## Usage

Retrieving blocks via the API is not something this library handles. You need to do that and then provide blocks
to `NotionRenderer.block_to_html/1,2`. In order to render child content, you also need to provide the `_children`
attribute which is a list of blocks. Basically, you can recursively hit the API to build up the blocks list.

Blocks are expected to be a string-based map. The blocks are taken as-is from the API, with the
added support of `_children`.

```elixir
# Retrieving blocks is up to you
blocks = retrieve_blocks_from_api()

# Then just pass them in!
html = NotionRenderer.block_to_html(blocks)

# or pass options
rewriter = fn href -> "modified #{href}" end
html = NotionRenderer.block_to_html(blocks, [link_rewriter: rewriter])
```

## Options

You can provide `link_rewriter` option to rewrite all link hrefs. This option is useful to rewrite page links
into your platform's patterns.

All of the HTML renderers are replaceable via the `config` property. It's undocumented at this time.

## Dimensions

Notion's API does not provide any dimensional information. So if a video or image has been resized, then there
is no access to that in the API. So, it will be max width. It's up to you to style via CSS.

## Not supported block types

Have an idea on how to best support these types? Make an issue to discuss:

* child_page
* child_database
* bookmark
* table_of_contents
* column
* column_list
* link_preview
* template
* link_to_page
* unsupported

Some types are supported but have caveats:

* bulleted_list_item (is implemented as a ul, does not follow the same display system as Notion)
* embed (is implemented as a figure containing the source)
* file (is implemented as a figure containing the source)
* pdf (is implemented as a figure containing the source)
* video (is implemented with a few common video providers embed code, but might be missing some. See `Html.VideoEmbed` for the list)
* toggle (is implemented, but is just a <ul>)
* equation (is implemented, but you must use KaTeX to format it in JS)
* synced_block (is implemented, but only if you provide the `_children` property)

## CSS

Rather than inlining styles, this code tries to use CSS classes whenever reasonable. You can overwrite these classes
as needed, but the following stylesheet is a great start.

```
<style>
  .notion-callout {
    border-radius: 3px;
    padding: 1rem;

    white-space:pre-wrap;
    display:flex;
    gap: 10px;
    background-color: rgba(241, 241, 239, 1);
  }

  .notion-callout-content {
    flex-grow: 1;
  }

  .notion-video {
    margin: 0;
  }

  .notion-embed,
  .notion-file,
  .notion-pdf {
    margin: 1.25em 0;
    page-break-inside: avoid;
  }

  .notion-embed .notion-source,
  .notion-file .notion-source,
  .notion-pdf .notion-source {
    border: 1px solid #ddd;
    border-radius: 3px;
    padding: 1.5em;
    word-break: break-all;
  }

  .notion-image {
    border: none;
    margin: 1.5em 0;
    padding: 0;
    border-radius: 0;
    text-align: center;
  }

  .notion-caption {
    opacity: 0.5;
    font-size: 85%;
    margin-top: 0.5em;
  }

  .notion-video-embed-sizer {
    position: relative;
    padding-bottom: calc(9/16 * 100%);
    height: 0;
  }

  .notion-video-embed-sizer iframe,
  .notion-video-embed-sizer video {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    border: 0;
    margin: 0;
  }
</style>
```