Skip to content

Commit 9248c38

Browse files
committed
Use React in a new Markdown pane
1 parent 3b65e9a commit 9248c38

11 files changed

Lines changed: 653 additions & 29 deletions

File tree

.babelrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"presets": [
33
"@babel/preset-env",
4+
"@babel/preset-react",
45
"@babel/preset-typescript"
56
],
67
}

.eslintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"project": "./tsconfig.json"
1515
},
1616
"plugins": [
17-
"@typescript-eslint"
17+
"@typescript-eslint",
18+
"react"
1819
],
1920
"rules": {
2021
"no-unused-vars": ["warn", {

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ if (typeof window !== 'undefined') {
4747

4848
let register = panes.register
4949

50+
register(require('./markdown/index.tsx').Pane)
5051
register(require('issue-pane'))
5152
register(require('contacts-pane'))
5253

markdown/index.tsx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import * as React from 'react'
2+
import * as ReactDOM from 'react-dom'
3+
import { PaneDefinition, NewPaneOptions } from '../types'
4+
import $rdf from 'rdflib'
5+
import solidUi from 'solid-ui'
6+
import { saveMarkdown, loadMarkdown } from './service'
7+
import { View } from './view'
8+
9+
const { icons, store } = solidUi
10+
11+
export const Pane: PaneDefinition = {
12+
icon: `${icons.iconBase}noun_79217.svg`,
13+
name: 'MarkdownPane',
14+
label: (subject) => subject.uri.endsWith('.md') ? 'Handle markdown file' : null,
15+
mintNew: function (options) {
16+
const newInstance = createFileName(options)
17+
return saveMarkdown(store, newInstance.uri, '# This is your markdown file\n\nHere be stuff!')
18+
.then(() => ({
19+
...options,
20+
newInstance
21+
}))
22+
.catch((err: any) => {
23+
console.error('Error creating new instance of markdown file', err)
24+
return options
25+
})
26+
},
27+
render: (subject) => {
28+
const container = document.createElement('div')
29+
30+
loadMarkdown(store, subject.uri).then((markdown) => {
31+
const view = (
32+
<View
33+
markdown={markdown}
34+
onSave={(newMarkdown) => saveMarkdown(store, subject.uri, newMarkdown)}
35+
/>
36+
)
37+
ReactDOM.render(view, container)
38+
})
39+
return container
40+
}
41+
}
42+
43+
function createFileName (options: NewPaneOptions): $rdf.NamedNode {
44+
let uri = options.newBase
45+
if (uri.endsWith('/')) {
46+
uri = uri.slice(0, -1) + '.md'
47+
}
48+
return $rdf.sym(uri)
49+
}

markdown/service.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { IndexedFormula } from 'rdflib'
2+
3+
export function loadMarkdown (store: IndexedFormula, uri: string): Promise<string> {
4+
return (store as any).fetcher.webOperation('GET', uri)
5+
.then((response: any) => response.responseText)
6+
}
7+
8+
export function saveMarkdown (store: IndexedFormula, uri: string, data: string): Promise<any> {
9+
return (store as any).fetcher.webOperation('PUT', uri, {
10+
data,
11+
contentType: 'text/markdown; charset=UTF-8'
12+
})
13+
}

markdown/view.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import * as React from 'react'
2+
import Markdown from 'react-markdown'
3+
4+
interface Props {
5+
markdown: string;
6+
onSave: (newMarkdown: string) => Promise<void>;
7+
}
8+
9+
export const View: React.FC<Props> = (props) => {
10+
const [phase, setPhase] = React.useState<'loading' | 'rendering' | 'editing'>('rendering')
11+
const [rawText, setRawText] = React.useState(props.markdown)
12+
13+
function storeMarkdown () {
14+
setPhase('loading')
15+
props.onSave(rawText).then(() => {
16+
setPhase('rendering')
17+
})
18+
}
19+
20+
if (phase === 'loading') {
21+
return <section aria-busy={true}>Loading&hellip;</section>
22+
}
23+
24+
if (phase === 'editing') {
25+
return (
26+
<section>
27+
<form onSubmit={(e) => { e.preventDefault(); storeMarkdown() }}>
28+
<textarea
29+
onChange={(e) => { setRawText(e.target.value) }}
30+
defaultValue={rawText}/>
31+
<button type="submit">RENDER</button>,
32+
</form>
33+
</section>
34+
)
35+
}
36+
37+
return (
38+
<section>
39+
<form onSubmit={(event) => { event.preventDefault(); setPhase('editing') }}>
40+
<Markdown source={rawText}/>
41+
<button type="submit">EDIT</button>
42+
</form>
43+
</section>
44+
)
45+
}

0 commit comments

Comments
 (0)