Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[draft-js-export-markdown] Add option for custom entity render function #234

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
67 changes: 49 additions & 18 deletions packages/draft-js-export-markdown/src/stateToMarkdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import {
INLINE_STYLE,
} from 'draft-js-utils';

import type {ContentState, ContentBlock} from 'draft-js';
import type {ContentState, ContentBlock, EntityInstance} from 'draft-js';

type RenderEntityFn = (content: string, entity: EntityInstance) => string;

type Options = {
gfm?: ?boolean;
renderEntity?: RenderEntityFn;
};

const {BOLD, CODE, ITALIC, STRIKETHROUGH, UNDERLINE} = INLINE_STYLE;
Expand Down Expand Up @@ -198,13 +201,16 @@ class MarkupGenerator {
let {contentState} = this;
let blockType = block.getType();
let text = block.getText();

if (text === '') {
// Prevent element collapse if completely empty.
// TODO: Replace with constant.
return '\u200B';
}

let charMetaList = block.getCharacterList();
let entityPieces = getEntityRanges(text, charMetaList);

return entityPieces
.map(([entityKey, stylePieces]) => {
let content = stylePieces
Expand All @@ -224,10 +230,10 @@ class MarkupGenerator {
const leftPad = contentLength - content.length;
content = content.trimRight();
const rightPad = contentLength - content.length - leftPad;
if (!content.length) {
// Return the original text if there is nothing left after trimming.
return text;
}
if (!content.length) {
// Return the original text if there is nothing left after trimming.
return text;
}

// NOTE: We attempt some basic character escaping here, although
// I don't know if escape sequences are really valid in markdown,
Expand All @@ -254,25 +260,50 @@ if (!content.length) {
return content.padEnd(content.length + rightPad);
})
.join('');

let entity = entityKey ? contentState.getEntity(entityKey) : null;
if (entity != null && entity.getType() === ENTITY_TYPE.LINK) {
let data = entity.getData();
let url = data.href || data.url || '';
let title = data.title ? ` "${escapeTitle(data.title)}"` : '';
return `[${content}](${encodeURL(url)}${title})`;
} else if (entity != null && entity.getType() === ENTITY_TYPE.IMAGE) {
let data = entity.getData();
let src = data.src || '';
let alt = data.alt ? `${escapeTitle(data.alt)}` : '';
return `![${alt}](${encodeURL(src)})`;
} else if (entity != null && entity.getType() === ENTITY_TYPE.EMBED) {
return entity.getData().url || content;
} else {

if (entity === null) {
return content;
}

return this.renderContentEntity(content, entity);
})
.join('');
}

renderContentEntity(content: string, entity: EntityInstance): string {
let {renderEntity} = this.options;

// Custom entity renderer must be a function & return string
// Otherwise, default to built-in renderer below
if (typeof renderEntity === 'function') {
const rendered = renderEntity(content, entity);

if (typeof rendered === 'string') {
return rendered;
}
}

const data = entity.getData();
switch (entity.getType()) {
case ENTITY_TYPE.LINK:
let url = data.href || data.url || '';
let title = data.title ? ` "${escapeTitle(data.title)}"` : '';
return `[${content}](${encodeURL(url)}${title})`;

case ENTITY_TYPE.IMAGE:
let src = data.src || '';
let alt = data.alt ? `${escapeTitle(data.alt)}` : '';
return `![${alt}](${encodeURL(src)})`;

case ENTITY_TYPE.EMBED:
return data.url || content;

default:
return content;
}
}
}

function canHaveDepth(blockType: any): boolean {
Expand Down