Skip to content

Commit 0d0f6b9

Browse files
committed
Add Remarkable standalone blocks configuration to enable customization
1 parent 0a8059c commit 0d0f6b9

File tree

3 files changed

+145
-3
lines changed

3 files changed

+145
-3
lines changed

README.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,9 @@ Since entities can also contain additional custom information - in this case, th
128128

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

131-
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` -
131+
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).
132132

133+
Example to convert a custom Remarkable token `mention_open` to a Draft entity:
133134
```javascript
134135
var rawDraftJSObject = markdownToDraft(markdownString, {
135136
remarkablePlugins: [remarkableMentionPlugin],
@@ -146,10 +147,39 @@ var rawDraftJSObject = markdownToDraft(markdownString, {
146147
}
147148
};
148149
}
149-
}
150+
},
151+
});
152+
```
153+
Example to convert a custom Remarkable token `atomic` to a Draft block:
154+
```javascript
155+
var rawDraftJSObject = markdownToDraft(markdownString, {
156+
remarkablePlugins: [remarkableMentionPlugin],
157+
blockTypes: {
158+
atomic_block: function (item) {
159+
return {
160+
type: "atomic",
161+
text: item.content,
162+
entityRanges: [],
163+
inlineStyleRanges: [],
164+
}
165+
}
166+
},
150167
});
151168
```
152169

170+
If your custom Remarkable plugin returns a standalone token, you can specify it
171+
in the configuration:
172+
```javascript
173+
var rawDraftJSObject = markdownToDraft(markdownString, {
174+
remarkablePlugins: [remarkableMentionPlugin],
175+
blockTypes: {
176+
atomic_block: function (item) {
177+
...
178+
}
179+
},
180+
remarkableStandaloneBlocks: ['atomic_block']
181+
});
182+
```
153183

154184
## Additional options
155185

src/markdown-to-draft.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ const DefaultBlockStyles = {
101101
code: 'CODE'
102102
};
103103

104+
// Remarkable blocks that stands alone.
105+
const DefaultRemarkableStandaloneBlocks = [
106+
'hr',
107+
'fence'
108+
]
109+
104110
// Key generator for entityMap items
105111
var idCounter = -1;
106112
function generateUniqueKey() {
@@ -229,6 +235,7 @@ function markdownToDraft(string, options = {}) {
229235
const BlockTypes = Object.assign({}, DefaultBlockTypes, options.blockTypes || {});
230236
const BlockEntities = Object.assign({}, DefaultBlockEntities, options.blockEntities || {});
231237
const BlockStyles = Object.assign({}, DefaultBlockStyles, options.blockStyles || {});
238+
const RemarkableStandaloneBlocks = DefaultRemarkableStandaloneBlocks.concat(options.remarkableStandaloneBlocks)
232239

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

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

test/markdown-to-draft.spec.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,111 @@ describe('markdownToDraft', function () {
457457
expect(conversionResult.blocks[0].data.lang).toEqual('js');
458458
});
459459

460+
it('can handle custom standalone block data', function () {
461+
var delimiter = '---';
462+
var blockRule = function (state, startLine, endLine, silent) {
463+
var marker,
464+
len,
465+
nextLine,
466+
mem,
467+
content,
468+
haveEndMarker = false,
469+
pos = state.bMarks[startLine] + state.tShift[startLine],
470+
max = state.eMarks[startLine]
471+
472+
// Check if the current line is the first line
473+
if (startLine !== 0) { return false }
474+
475+
// Check if the line contains at least 3 characters
476+
if (pos + 3 > max) { return false }
477+
478+
// Check if the first character is a '-'
479+
marker = state.src.charCodeAt(pos)
480+
481+
if (marker !== 0x2D) { return false }
482+
483+
// Check marker length
484+
mem = pos
485+
pos = state.skipChars(pos, marker)
486+
len = pos - mem
487+
488+
if (len < 3) { return false }
489+
490+
// Since start is found, we can report success here in validation mode
491+
if (silent) { return true }
492+
493+
nextLine = startLine
494+
495+
for (;;) {
496+
nextLine++
497+
498+
// Break if the current line is the last line
499+
if (nextLine >= endLine) {
500+
break
501+
}
502+
503+
pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
504+
max = state.eMarks[nextLine]
505+
506+
// Skip to next line if the current line does not start with the
507+
// marker
508+
if (state.src.charCodeAt(pos) !== marker) { continue }
509+
510+
// Skip to next line if the closing dash is indented with more than
511+
// 4 spaces
512+
if (state.tShift[nextLine] - state.blkIndent >= 4) { continue }
513+
514+
pos = state.skipChars(pos, marker)
515+
516+
// Skip to next line if the number of closing dashed is not the same
517+
// as the opening ones.
518+
if (pos - mem < len) { continue }
519+
520+
// Skip to next line if there are more characters after possible
521+
// closing dashes
522+
pos = state.skipSpaces(pos)
523+
524+
if (pos < max) { continue }
525+
526+
// Mark block end
527+
haveEndMarker = true
528+
529+
break
530+
}
531+
532+
state.line = nextLine + (haveEndMarker ? 1 : 0)
533+
content = state.getLines(startLine + 1, nextLine, state.blkIndent, false).trim()
534+
535+
state.tokens.push({
536+
type: 'yaml_frontmatter',
537+
content: content,
538+
level: state.level,
539+
lines: [ startLine, state.line ]
540+
})
541+
542+
return true
543+
}
544+
var frontmatterYamlWrapper = function (remarkable) {
545+
remarkable.block.ruler.before('hr', 'yaml_frontmatter', blockRule)
546+
}
547+
var markdown = '---\nsomeMetadata: content\n---';
548+
var conversionResult = markdownToDraft(markdown, {
549+
remarkablePlugins: [frontmatterYamlWrapper],
550+
blockTypes: {
551+
yaml_frontmatter: function (item) {
552+
return {
553+
type: 'atomic',
554+
content: item.content
555+
}
556+
}
557+
},
558+
remarkableStandaloneBlocks: ['yaml_frontmatter']
559+
});
560+
561+
expect(conversionResult.blocks[0].type).toEqual('atomic');
562+
expect(conversionResult.blocks[0].content).toEqual('someMetadata: content');
563+
});
564+
460565
it('can handle simple nested styles', function () {
461566
var markdown = '__*hello* world__';
462567
var conversionResult = markdownToDraft(markdown);

0 commit comments

Comments
 (0)