Merge pull request #1506 from hackmdio/feature/csvpreview

This commit is contained in:
Max Wu 2020-07-09 17:37:45 +08:00 committed by GitHub
commit 4fe0f07749
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 96 additions and 0 deletions

6
package-lock.json generated
View File

@ -12024,6 +12024,12 @@
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"dev": true
},
"papaparse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.2.0.tgz",
"integrity": "sha512-ylq1wgUSnagU+MKQtNeVqrPhZuMYBvOSL00DHycFTCxownF95gpLAk1HiHdUW77N8yxRq1qHXLdlIPyBSG9NSA==",
"dev": true
},
"parallel-transform": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",

View File

@ -169,6 +169,7 @@
"mock-require": "~3.0.3",
"nyc": "~14.0.0",
"optimize-css-assets-webpack-plugin": "~5.0.0",
"papaparse": "^5.2.0",
"pdfobject": "~2.1.1",
"plantuml-encoder": "^1.2.5",
"power-assert": "~1.6.1",

View File

@ -208,6 +208,30 @@ When youre a carpenter making a beautiful chest of drawers, youre not goin
> > Even support the nest blockquotes!
> > [name=ChengHan Wu] [time=Sun, Jun 28, 2015 10:00 PM] [color=red]
### Render CSV as table
You can use write csv in the codeblock:
~~~md
```csvpreview {header="true"}
firstName,lastName,email,phoneNumber
John,Doe,john@doe.com,0123456789
Jane,Doe,jane@doe.com,9876543210
James,Bond,james.bond@mi6.co.uk,0612345678
```
~~~
which rendered to:
```csvpreview {header="true"}
firstName,lastName,email,phoneNumber
John,Doe,john@doe.com,0123456789
Jane,Doe,jane@doe.com,9876543210
James,Bond,james.bond@mi6.co.uk,0612345678
```
We use [Papa Parse](https://www.papaparse.com/) for parsing csv. The parsing option is given in braces: `{}`, and multiple options are seperated by a space. e.g. `{header="true" delimiter="."}`. Please read [their documentation](https://www.papaparse.com/docs#config) as reference.
## Externals
### YouTube

View File

@ -25,6 +25,7 @@ import {
} from './lib/markdown/utils'
import { renderFretBoard } from './lib/renderer/fretboard/fretboard'
import './lib/renderer/lightbox'
import { renderCSVPreview } from './lib/renderer/csvpreview'
import markdownit from 'markdown-it'
import markdownitContainer from 'markdown-it-container'
@ -1211,6 +1212,12 @@ md.renderer.rules.fence = (tokens, idx, options, env, self) => {
if (info) {
langName = info.split(/\s+/g)[0]
if (langName === 'csvpreview') {
const params = parseFenceCodeParams(info)
return renderCSVPreview(token.content, params)
}
if (/!$/.test(info)) token.attrJoin('class', 'wrap')
token.attrJoin('class', options.langPrefix + langName.replace(/=$|=\d+$|=\+$|!$|=!$/, ''))
token.attrJoin('class', 'hljs')

View File

@ -0,0 +1,44 @@
import Papa from 'papaparse'
import escapeHTML from 'lodash/escape'
const safeParse = d => {
try {
return JSON.parse(d)
} catch (err) {
return d
}
}
export function renderCSVPreview (csv, options = {}, attr = '') {
const opt = Object.keys(options).reduce((acc, key) => {
return Object.assign(acc, {
[key]: safeParse(options[key])
})
}, {})
const results = Papa.parse(csv.trim(), opt)
if (opt.header) {
const fields = results.meta.fields
return `<table ${attr}>
<thead>
<tr>
${fields.map(f => `<th>${escapeHTML(f)}</th>`).join('')}
</tr>
</thead>
<tbody>
${results.data.map(d => `<tr>
${fields.map(f => `<td>${escapeHTML(d[f])}</td>`).join('')}
</tr>`).join('')}
</tbody>
</table>`
} else {
return `<table ${attr}>
<tbody>
${results.data.map(d => `<tr>
${d.map(f => `<td>${escapeHTML(f)}</td>`).join('')}
</tr>`).join('')}
</tbody>
</table>`
}
}

View File

@ -7,6 +7,8 @@ import markdownitContainer from 'markdown-it-container'
import { md } from '../extra'
import modeType from './modeType'
import appState from './appState'
import { renderCSVPreview } from './renderer/csvpreview'
import { parseFenceCodeParams } from './markdown/utils'
function addPart (tokens, idx) {
if (tokens[idx].map && tokens[idx].level === 0) {
@ -71,6 +73,18 @@ md.renderer.rules.fence = (tokens, idx, options, env, self) => {
if (info) {
langName = info.split(/\s+/g)[0]
if (langName === 'csvpreview') {
const params = parseFenceCodeParams(info)
let attr = ''
if (tokens[idx].map && tokens[idx].level === 0) {
const startline = tokens[idx].map[0] + 1
const endline = tokens[idx].map[1]
attr = `class="part" data-startline="${startline}" data-endline="${endline}"`
}
return renderCSVPreview(token.content, params, attr)
}
if (/!$/.test(info)) token.attrJoin('class', 'wrap')
token.attrJoin('class', options.langPrefix + langName.replace(/=$|=\d+$|=\+$|!$|=!/, ''))
token.attrJoin('class', 'hljs')