Skip to content

Commit

Permalink
[WIP] Allow importing full MDX documents
Browse files Browse the repository at this point in the history
  • Loading branch information
nerik committed Aug 7, 2023
1 parent bee1ea4 commit 2d85329
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 15 deletions.
25 changes: 18 additions & 7 deletions app/scripts/components/publication-tool/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import { atomWithStorage } from 'jotai/utils';
import { useParams } from 'react-router';
import { useAtomValue, useSetAtom } from 'jotai';
import { useCallback, useMemo } from 'react';
import { DataStory } from './types';
import { EditorDataStory } from './types';
import { toEditorDataStory, toMDXDocument } from './utils';

export const DataStoriesAtom = atomWithStorage<DataStory[]>('dataStories', [
export const DataStoriesAtom = atomWithStorage<EditorDataStory[]>('dataStories', [
{
frontmatter: {
id: 'example-data-story',
name: 'Example Data Story',
description: 'This is an example data story',
sources: [],
thematics: [],
pubDate: '2023-01-01'
pubDate: '2023-01-01',
taxonomy: [],
},
currentBlockId: '1',
blocks: [
Expand Down Expand Up @@ -59,8 +59,7 @@ export const DataStoriesAtom = atomWithStorage<DataStory[]>('dataStories', [
id: 'example-data-story-2',
name: 'Example Data Story 2',
description: 'This is an example data story',
sources: [],
thematics: [],
taxonomy: [],
pubDate: '2023-01-01'
},
blocks: [
Expand All @@ -78,6 +77,18 @@ export const DataStoriesAtom = atomWithStorage<DataStory[]>('dataStories', [
}
]);

export const useCreateEditorDataStoryFromMDXDocument = () => {
const setDataStories = useSetAtom(DataStoriesAtom);
return useCallback((mdxDocument: string) => {
const editorDataStory = toEditorDataStory(mdxDocument);
setDataStories((oldDataStories) => {
const newDataStories = [...oldDataStories, editorDataStory];
return newDataStories;
});
return editorDataStory;
}, [setDataStories]);
};

export const useCurrentDataStory = () => {
const { storyId } = useParams();
const dataStories = useAtomValue(DataStoriesAtom);
Expand Down
37 changes: 34 additions & 3 deletions app/scripts/components/publication-tool/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React, { useMemo } from 'react';
import { Route, Routes, useParams } from 'react-router';
import React, { useMemo, useState } from 'react';
import { Route, Routes, useNavigate, useParams } from 'react-router';
import { useAtomValue } from 'jotai';
import { Button, ButtonGroup } from '@devseed-ui/button';
import DataStoryEditor from './data-story';
import { DataStoriesAtom } from './atoms';
import {
DataStoriesAtom,
useCreateEditorDataStoryFromMDXDocument
} from './atoms';
import { LayoutProps } from '$components/common/layout-root';
import { resourceNotFound } from '$components/uhoh';
import PageHero from '$components/common/page-hero';
Expand Down Expand Up @@ -74,6 +77,17 @@ function DataStoryEditorLayout() {

function PublicationTool() {
const dataStories = useAtomValue(DataStoriesAtom);
const [newStory, setNewStory] = useState<string>('');
const createEditorDataStoryFromMDXDocument =
useCreateEditorDataStoryFromMDXDocument();
const navigate = useNavigate();

const onCreate = () => {
const { frontmatter } = createEditorDataStoryFromMDXDocument(newStory);
setNewStory('');
navigate(`/publication-tool/${frontmatter.id}`);
};

return (
<Routes>
<Route path=':storyId' element={<DataStoryEditorLayout />} />
Expand Down Expand Up @@ -114,6 +128,23 @@ function PublicationTool() {
))}
</tbody>
</table>
<hr />
<h2>Create new Story</h2>
<div>
<p>From existing MDX document:</p>
<textarea
rows={20}
onChange={(e) => setNewStory(e.currentTarget.value)}
placeholder='Paste full MDX document here (including frontmatter)'
/>
</div>
<Button
disabled={!newStory || newStory === ''}
onClick={onCreate}
variation='primary-fill'
>
Create
</Button>
</FoldProse>
</Fold>
</PageMainContent>
Expand Down
10 changes: 5 additions & 5 deletions app/scripts/components/publication-tool/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { DiscoveryData } from "veda";
import { StoryData } from "veda";

export interface DataStoryBlock {
export interface EditorStoryBlock {
id: string;
tag: 'Block' | 'ScrollyTellingBlock';
blockType?: 'wide' | 'full';
mdx: string;
}

export interface DataStory {
frontmatter: DiscoveryData;
blocks: DataStoryBlock[];
export interface EditorStory {
frontmatter: StoryData;
blocks: EditorStoryBlock[];
currentBlockId?: string;
}
52 changes: 52 additions & 0 deletions app/scripts/components/publication-tool/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { dump, load } from 'js-yaml';
import { StoryData } from 'veda';
import { EditorStory, EditorStoryBlock } from './types';

export function toMDXBlock(block: EditorStoryBlock): string {
const type = block.blockType ? ` type=${block.blockType}` : '';
return `<Block${type}>
${block.mdx}
</Block>`;
}

export function toMDXDocument(dataStory: EditorStory): string {
const frontmatter = dump(dataStory.frontmatter);
const doc = `---
${frontmatter}
---
${dataStory.blocks.map((block) => toMDXBlock(block)).join('\n\n')}
`;

return doc;
}

export function toEditorDataStory(mdxDocument: string): EditorStory {
const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)/;

const matches = mdxDocument.match(frontmatterRegex);

if (!matches) {
throw new Error('Frontmatter not found in the Markdown document.');
}

const rawFrontmatter = matches[1];
const rawContent = matches[2];

const frontmatter = load(rawFrontmatter) as StoryData;

const blockRegex = /<Block\s*(?:type=['"](.+?)['"])?\s*>([\s\S]*?)<\/Block>/g;
let blocks: EditorStoryBlock[] = [];
let match;
let id = 0;
while ((match = blockRegex.exec(rawContent)) !== null) {
const blockType = match[1];
const mdx = match[2].trim();
blocks = [...blocks, { tag: 'Block', blockType, mdx, id: `${id++}` }];
}

return {
frontmatter,
blocks
};
}

0 comments on commit 2d85329

Please sign in to comment.