Skip to content

Instantly share code, notes, and snippets.

@walemust
Created September 13, 2020 08:07
Show Gist options
  • Save walemust/a87534d75038d0b33c4f2d37c1470db9 to your computer and use it in GitHub Desktop.
Save walemust/a87534d75038d0b33c4f2d37c1470db9 to your computer and use it in GitHub Desktop.
Fork Me! FCC: Markdown Previewer
const projectName = "markdown-previewer";
localStorage.setItem('example_project', 'Markdown Previewer');
// ALLOWS LINE BREAKS WITH RETURN BUTTON
marked.setOptions({
breaks: true,
});
// INSERTS target="_blank" INTO HREF TAGS (required for codepen links)
const renderer = new marked.Renderer();
renderer.link = function (href, title, text) {
return `<a target="_blank" href="${href}">${text}` + '</a>';
}
class App extends React.Component{
constructor(props) {
super(props);
this.state = {
markdown: placeholder,
editorMaximized: false,
previewMaximized: false
}
this.handleChange = this.handleChange.bind(this);
this.handleEditorMaximize = this.handleEditorMaximize.bind(this);
this.handlePreviewMaximize = this.handlePreviewMaximize.bind(this);
}
handleChange(e) {
this.setState({
markdown: e.target.value
});
}
handleEditorMaximize() {
this.setState({
editorMaximized: !this.state.editorMaximized
});
}
handlePreviewMaximize() {
this.setState({
previewMaximized: !this.state.previewMaximized
});
}
render() {
const classes = this.state.editorMaximized ?
['editorWrap maximized',
'previewWrap hide',
'fa fa-compress'] :
this.state.previewMaximized ?
['editorWrap hide',
'previewWrap maximized',
'fa fa-compress'] :
['editorWrap',
'previewWrap',
'fa fa-arrows-alt'];
return (
<div>
<div className={classes[0]}>
<Toolbar
icon={classes[2]}
onClick={this.handleEditorMaximize}
text="Editor"/>
<Editor markdown={this.state.markdown}
onChange={this.handleChange} />
</div>
<div className="converter">
</div>
<div className={classes[1]}>
<Toolbar
icon={classes[2]}
onClick={this.handlePreviewMaximize}
text="Previewer"/>
<Preview markdown={this.state.markdown}/>
</div>
</div>
)
}
};
const Toolbar = (props) => {
return (
<div className="toolbar">
<i title="no-stack-dub-sack" className="fa fa-free-code-camp"/>
{props.text}
<i onClick={props.onClick} className={props.icon}></i>
</div>
)
}
const Editor = (props) => {
return (
<textarea id="editor"
value={props.markdown}
onChange={props.onChange}
type="text"/>
)
}
const Preview = (props) => {
return (
<div id='preview' dangerouslySetInnerHTML={{__html: marked(props.markdown, { renderer: renderer })}} />
)
}
const placeholder =
`# Welcome to my React Markdown Previewer!
## This is a sub-heading...
### And here's some other cool stuff:
Heres some code, \`<div></div>\`, between 2 backticks.
\`\`\`
// this is multi-line code:
function anotherExample(firstLine, lastLine) {
if (firstLine == '\`\`\`' && lastLine == '\`\`\`') {
return multiLineCode;
}
}
\`\`\`
You can also make text **bold**... whoa!
Or _italic_.
Or... wait for it... **_both!_**
And feel free to go crazy ~~crossing stuff out~~.
There's also [links](https://www.freecodecamp.com), and
> Block Quotes!
And if you want to get really crazy, even tables:
Wild Header | Crazy Header | Another Header?
------------ | ------------- | -------------
Your content can | be here, and it | can be here....
And here. | Okay. | I think we get it.
- And of course there are lists.
- Some are bulleted.
- With different indentation levels.
- That look like this.
1. And there are numbererd lists too.
1. Use just 1s if you want!
1. But the list goes on...
- Even if you use dashes or asterisks.
* And last but not least, let's not forget embedded images:
![React Logo w/ Text](https://goo.gl/Umyytc)
`
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.6/marked.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
<script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>
@import url('https://fonts.googleapis.com/css?family=Russo+One');
$darkAccent: #224B4B;
$lightAccent: #2CDA9D;
$backgroundBase: #619E9E;
$shadow: 1px 1px 10px 2px darken($backgroundBase, 20%);
$default-border: 1px solid darken($backgroundBase, 35%);
body {
background: lighten($backgroundBase, 12%);
margin: 10px 0;
}
#preview,
.title {
display: inline-block;
}
.colorScheme {
background-color: lighten($backgroundBase, 30%);
border: $default-border;
box-shadow: $shadow;
border-top: none;
}
.toolbar {
position: relative;
background-color: lighten($darkAccent, 25%);
padding: 4px 4px 3px 3px;
border: $default-border;
box-shadow: $shadow;
font-family: Russo One;
font-size: 15px;
i {
width: 25px;
color: black;
margin-left: 5px;
}
.fa-arrows-alt,
.fa-compress {
position: absolute;
right: -5px;
}
}
.fa-retweet,
.fa-compress,
.fa-arrows-alt {
&:hover {
color: lighten($lightAccent, 10%);
cursor: pointer;
}
}
.fa-free-code-camp {
margin-right: 3px;
}
.editorWrap {
width: 600px;
margin: 18px auto;
.toolbar {
width: 99%;
}
textarea {
@extend .colorScheme;
width: 99%;
min-height: 200px;
margin-bottom: -5px;
resize: vertical;
outline: none;
padding-left: 5px;
padding-top: 5px;
font-size: 12px;
}
}
.converter {
width: 100px;
text-align: center;
font-size: 35px;
margin: auto;
}
.previewWrap {
@extend .colorScheme;
width: 800px;
margin: 20px auto;
min-height: 200px;
overflow-wrap: break-word;
padding-right: 20px;
.toolbar {
left: -1px;
width: 100%;
padding-right: 17px;
}
#preview {
margin-left: 5px;
margin-top: -10px;
width: 100%;
}
}
@media screen and (max-width: 850px) {
.previewWrap {
width: 630px;
}
.editorWrap {
width: 550px;
}
}
.maximized {
width: 96%;
min-height: 100vh;
margin: auto;
textarea {
min-height: 95vh;
resize: none;
}
}
.hide {
display: none;
}
@media screen and (max-width: 650px) {
body {
margin: 5px 0;
}
.editorWrap {
width: 80vw;
margin: 0 auto;
}
.maximized {
width: 95%;
margin: auto;
}
.previewWrap {
width: 95vw;
#preview {
width: 100%;
img {
height: 200px;
}
}
}
}
// MARKDOWN STYLES
#preview {
blockquote {
border-left: 3px solid #224B4B;
color: #224B4B;
padding-left: 5px;
margin-left: 25px;
}
code {
background: white;
padding: 1px 4px 2px 4px;
font-size: 12px;
font-weight: bold;
}
pre {
background: white;
padding: 5px 0 5px 5px;
}
h1 {
border-bottom: 2px solid $darkAccent;
}
h2 {
border-bottom: 1px solid $darkAccent;
}
table {
border-collapse: collapse;
}
td,
th {
border: 2px solid $darkAccent;
padding-left: 5px;
padding-right: 5px;
}
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.0.2/mocha.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment