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

Ordered and unordered lists #381

Open
robinmitra opened this issue Jun 10, 2024 · 4 comments
Open

Ordered and unordered lists #381

robinmitra opened this issue Jun 10, 2024 · 4 comments
Labels
question Further information is requested

Comments

@robinmitra
Copy link

Thank you for your work on this library!

Do you have any plans for implementing ordered and unordered lists?

@tomekzaw tomekzaw added the question Further information is requested label Jun 11, 2024
@tomekzaw
Copy link
Collaborator

tomekzaw commented Jun 11, 2024

Hello and thanks for opening this issue.

Do you have any plans for implementing ordered and unordered lists?

Currently, we don't support ordered and unordered lists. This is mostly because currently the library uses a hard-coded Markdown parser called ExpensiMark and it does not have this feature.

However, in the future, library users will be able to pass custom Markdown parser and use custom styles for formatting so theoretically this should be possible.

If you need this feature now, I think it should be pretty straightforward to extend this library in terms of Markdown parser as well as on the native side. If you need any guidance, I could provide you exact steps how to do that.

I will keep you updated on that.

@robinmitra
Copy link
Author

Thanks for coming back to me, yes I think I'd like some guidance on extending this library for supporting these feature. Appreciate the help 🙌🏼

@tomekzaw
Copy link
Collaborator

Sure! First you'll need to make changes in the JS parser here: https://github.com/Expensify/react-native-live-markdown/blob/main/parser/index.ts There's a function called parseExpensiMarkToRanges that accepts a string with the message and returns an array of styled ranges. Don't forget to bundle the code with yarn build and test it with yarn test.

Once that's complete, you'll need to implement the actual styling logic on the native side.

For iOS, you'll need to edit this file:

if ([type isEqualToString:@"syntax"]) {
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.syntaxColor range:range];
} else if ([type isEqualToString:@"strikethrough"]) {
[attributedString addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
} else if ([type isEqualToString:@"code"]) {
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.codeColor range:range];
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.codeBackgroundColor range:range];
} else if ([type isEqualToString:@"mention-here"]) {
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionHereColor range:range];
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionHereBackgroundColor range:range];
} else if ([type isEqualToString:@"mention-user"]) {
// TODO: change mention color when it mentions current user
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionUserColor range:range];
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionUserBackgroundColor range:range];
} else if ([type isEqualToString:@"mention-report"]) {
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionReportColor range:range];
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionReportBackgroundColor range:range];
} else if ([type isEqualToString:@"link"]) {
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.linkColor range:range];
} else if ([type isEqualToString:@"blockquote"]) {
CGFloat indent = (_markdownStyle.blockquoteMarginLeft + _markdownStyle.blockquoteBorderWidth + _markdownStyle.blockquotePaddingLeft) * depth;
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.firstLineHeadIndent = indent;
paragraphStyle.headIndent = indent;
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
[_blockquoteRangesAndLevels addObject:@{
@"range": [NSValue valueWithRange:range],
@"depth": @(depth)
}];
} else if ([type isEqualToString:@"pre"]) {
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.preColor range:range];
NSRange rangeForBackground = [inputString characterAtIndex:range.location] == '\n' ? NSMakeRange(range.location + 1, range.length - 1) : range;
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.preBackgroundColor range:rangeForBackground];
// TODO: pass background color and ranges to layout manager
} else if ([type isEqualToString:@"h1"]) {
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
NSRange rangeWithHashAndSpace = NSMakeRange(range.location - 2, range.length + 2); // we also need to include prepending "# "
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:rangeWithHashAndSpace];
}

For Android, you'll need to make changes here:

switch (type) {
case "bold":
setSpan(ssb, new MarkdownBoldSpan(), start, end);
break;
case "italic":
setSpan(ssb, new MarkdownItalicSpan(), start, end);
break;
case "strikethrough":
setSpan(ssb, new MarkdownStrikethroughSpan(), start, end);
break;
case "emoji":
setSpan(ssb, new MarkdownEmojiSpan(mMarkdownStyle.getEmojiFontSize()), start, end);
break;
case "mention-here":
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getMentionHereColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(mMarkdownStyle.getMentionHereBackgroundColor()), start, end);
break;
case "mention-user":
// TODO: change mention color when it mentions current user
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getMentionUserColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(mMarkdownStyle.getMentionUserBackgroundColor()), start, end);
break;
case "mention-report":
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getMentionReportColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(mMarkdownStyle.getMentionReportBackgroundColor()), start, end);
break;
case "syntax":
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getSyntaxColor()), start, end);
break;
case "link":
setSpan(ssb, new MarkdownUnderlineSpan(), start, end);
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getLinkColor()), start, end);
break;
case "code":
setSpan(ssb, new MarkdownFontFamilySpan(mMarkdownStyle.getCodeFontFamily(), mAssetManager), start, end);
setSpan(ssb, new MarkdownFontSizeSpan(mMarkdownStyle.getCodeFontSize()), start, end);
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getCodeColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(mMarkdownStyle.getCodeBackgroundColor()), start, end);
break;
case "pre":
setSpan(ssb, new MarkdownFontFamilySpan(mMarkdownStyle.getPreFontFamily(), mAssetManager), start, end);
setSpan(ssb, new MarkdownFontSizeSpan(mMarkdownStyle.getPreFontSize()), start, end);
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getPreColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(mMarkdownStyle.getPreBackgroundColor()), start, end);
break;
case "h1":
setSpan(ssb, new MarkdownBoldSpan(), start, end);
CustomLineHeightSpan[] spans = ssb.getSpans(0, ssb.length(), CustomLineHeightSpan.class);
if (spans.length >= 1) {
int lineHeight = spans[0].getLineHeight();
setSpan(ssb, new MarkdownLineHeightSpan(lineHeight * 1.5f), start, end);
}
// NOTE: size span must be set after line height span to avoid height jumps
setSpan(ssb, new MarkdownFontSizeSpan(mMarkdownStyle.getH1FontSize()), start, end);
break;
case "blockquote":
MarkdownBlockquoteSpan span = new MarkdownBlockquoteSpan(
mMarkdownStyle.getBlockquoteBorderColor(),
mMarkdownStyle.getBlockquoteBorderWidth(),
mMarkdownStyle.getBlockquoteMarginLeft(),
mMarkdownStyle.getBlockquotePaddingLeft(),
depth);
setSpan(ssb, span, start, end);
break;

FYI We also offer consulting services, so if you need some more help with extending this library, we'd be more than happy to do so, just drop us a line at projects@swmansion.com.

@robinmitra
Copy link
Author

Thank you! I'll give it a try this weekend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants