Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,9 @@ Since entities can also contain additional custom information - in this case, th

What if you wanted to go the opposite direction? markdownToDraft uses [Remarkable](https://github.com/jonschlinkert/remarkable) for defining custom markdown types.

In this case, you need to write a [remarkable plugin](https://github.com/jonschlinkert/remarkable/blob/master/docs/plugins.md) first and pass it in to `markdownToDraft` -
In this case, you need to write a [remarkable plugin](https://github.com/jonschlinkert/remarkable/blob/master/docs/plugins.md) first and pass it in to `markdownToDraft`. You can find an example of Remarkable plugin in [this repository tests](https://github.com/Rosey/markdown-draft-js/blob/main/test/markdown-to-draft.spec.js#L369).

Example to convert a custom Remarkable token `mention_open` to a Draft entity:
```javascript
var rawDraftJSObject = markdownToDraft(markdownString, {
remarkablePlugins: [remarkableMentionPlugin],
Expand All @@ -146,10 +147,39 @@ var rawDraftJSObject = markdownToDraft(markdownString, {
}
};
}
}
},
});
```
Example to convert a custom Remarkable token `atomic` to a Draft block:
```javascript
var rawDraftJSObject = markdownToDraft(markdownString, {
remarkablePlugins: [remarkableMentionPlugin],
blockTypes: {
atomic_block: function (item) {
return {
type: "atomic",
text: item.content,
entityRanges: [],
inlineStyleRanges: [],
}
}
},
});
```

If your custom Remarkable plugin returns a standalone token, you can specify it
in the configuration:
```javascript
var rawDraftJSObject = markdownToDraft(markdownString, {
remarkablePlugins: [remarkableMentionPlugin],
blockTypes: {
atomic_block: function (item) {
...
}
},
remarkableStandaloneBlocks: ['atomic_block']
});
```

## Additional options

Expand Down
9 changes: 8 additions & 1 deletion src/markdown-to-draft.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ const DefaultBlockStyles = {
code: 'CODE'
};

// Remarkable blocks that stands alone.
const DefaultRemarkableStandaloneBlocks = [
'hr',
'fence'
]

// Key generator for entityMap items
var idCounter = -1;
function generateUniqueKey() {
Expand Down Expand Up @@ -229,6 +235,7 @@ function markdownToDraft(string, options = {}) {
const BlockTypes = Object.assign({}, DefaultBlockTypes, options.blockTypes || {});
const BlockEntities = Object.assign({}, DefaultBlockEntities, options.blockEntities || {});
const BlockStyles = Object.assign({}, DefaultBlockStyles, options.blockStyles || {});
const RemarkableStandaloneBlocks = DefaultRemarkableStandaloneBlocks.concat(options.remarkableStandaloneBlocks)

parsedData.forEach(function (item) {
// Because of how remarkable's data is formatted, we need to cache what kind of list we're currently dealing with
Expand All @@ -254,7 +261,7 @@ function markdownToDraft(string, options = {}) {

// The entity map is a master object separate from the block so just add any entities created for this block to the master object
Object.assign(entityMap, blockEntities);
} else if ((itemType.indexOf('_open') !== -1 || itemType === 'fence' || itemType === 'hr') && BlockTypes[itemType]) {
} else if ((itemType.indexOf('_open') !== -1 || RemarkableStandaloneBlocks.includes(itemType)) && BlockTypes[itemType]) {
var depth = 0;
var block;

Expand Down
105 changes: 105 additions & 0 deletions test/markdown-to-draft.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,111 @@ describe('markdownToDraft', function () {
expect(conversionResult.blocks[0].data.lang).toEqual('js');
});

it('can handle custom standalone block data', function () {
var delimiter = '---';
var blockRule = function (state, startLine, endLine, silent) {
var marker,
len,
nextLine,
mem,
content,
haveEndMarker = false,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine]

// Check if the current line is the first line
if (startLine !== 0) { return false }

// Check if the line contains at least 3 characters
if (pos + 3 > max) { return false }

// Check if the first character is a '-'
marker = state.src.charCodeAt(pos)

if (marker !== 0x2D) { return false }

// Check marker length
mem = pos
pos = state.skipChars(pos, marker)
len = pos - mem

if (len < 3) { return false }

// Since start is found, we can report success here in validation mode
if (silent) { return true }

nextLine = startLine

for (;;) {
nextLine++

// Break if the current line is the last line
if (nextLine >= endLine) {
break
}

pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
max = state.eMarks[nextLine]

// Skip to next line if the current line does not start with the
// marker
if (state.src.charCodeAt(pos) !== marker) { continue }

// Skip to next line if the closing dash is indented with more than
// 4 spaces
if (state.tShift[nextLine] - state.blkIndent >= 4) { continue }

pos = state.skipChars(pos, marker)

// Skip to next line if the number of closing dashed is not the same
// as the opening ones.
if (pos - mem < len) { continue }

// Skip to next line if there are more characters after possible
// closing dashes
pos = state.skipSpaces(pos)

if (pos < max) { continue }

// Mark block end
haveEndMarker = true

break
}

state.line = nextLine + (haveEndMarker ? 1 : 0)
content = state.getLines(startLine + 1, nextLine, state.blkIndent, false).trim()

state.tokens.push({
type: 'yaml_frontmatter',
content: content,
level: state.level,
lines: [ startLine, state.line ]
})

return true
}
var frontmatterYamlWrapper = function (remarkable) {
remarkable.block.ruler.before('hr', 'yaml_frontmatter', blockRule)
}
var markdown = '---\nsomeMetadata: content\n---';
var conversionResult = markdownToDraft(markdown, {
remarkablePlugins: [frontmatterYamlWrapper],
blockTypes: {
yaml_frontmatter: function (item) {
return {
type: 'atomic',
content: item.content
}
}
},
remarkableStandaloneBlocks: ['yaml_frontmatter']
});

expect(conversionResult.blocks[0].type).toEqual('atomic');
expect(conversionResult.blocks[0].content).toEqual('someMetadata: content');
});

it('can handle simple nested styles', function () {
var markdown = '__*hello* world__';
var conversionResult = markdownToDraft(markdown);
Expand Down