-
Notifications
You must be signed in to change notification settings - Fork 67
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
Handle context menu format bold and italic #556
base: main
Are you sure you want to change the base?
Conversation
Hi @bernhardoj, thanks for submitting this PR. I'm okay with the changes. However, this PR only implements Format menu on web. I believe the same feature is missing on native iOS. Are there any plans to implement this missing feature also in native iOS? @Skalakid and @BartoszGrajdek Could you please review and test this PR? Thanks in advance. |
I don't see the Format option on iOS native. I think it's only available on the web. ios.2.mp4 |
@bernhardoj You're right, if there's no menu on native iOS then we don't need to add one right now. In that case I think we can proceed with web-only implementation. |
src/MarkdownTextInput.web.tsx
Outdated
default: | ||
markdown = ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this state is unreachable so we could just throw an error here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it better if I add underline as the default value?
switch (formatCommand) {
case 'formatBold':
formatType = 'bold';
break;
case 'formatItalic':
formatType = 'italic';
break;
default:
formatType = 'underline';
break;
}
Hello @bernhardoj, thank you for your PR! Yesterday, we merged support for adding custom parsers to the Live Markdown Input - PR. It's a pretty big change, and it extends the use cases for our library. Because of that, I think we should slightly update your solution in this PR. Now you've implemented adding static syntaxes in the formatSelection={(selectedText: string, style: 'bold' | 'italic' | 'underline') => style === 'bold' ? `*${selectedText}*` : style === 'italic' ? `_${selectedText}_` : selectedText} What do you think about it? |
That sounds good, I'll update it tomorrow! |
@Skalakid I need help with defining the type for react-native-live-markdown/src/MarkdownTextInput.tsx Lines 54 to 57 in 9ad0acd
react-native-live-markdown/src/MarkdownTextInput.web.tsx Lines 30 to 36 in 9ad0acd
|
@bernhardoj Just FYI, I was able to add "Format" button in context menu on native iOS with the following code snippet: // inside UITextView
UIMenuItem *formatItem = [[UIMenuItem alloc] initWithTitle:@"Format" action:@selector(command:textView:)];
UIMenuController.sharedMenuController.menuItems = @[formatItem]; - (void)command:(nonnull UICommand *)command textView:(nonnull UITextView *)textView
{
// Do something
} |
Should I add it here? react-native-live-markdown/apple/RCTUITextView+Markdown.mm Lines 29 to 40 in 5695408
Also, I get this error when trying to run the iOS example app.
|
@bernhardoj I think we can implement native iOS in a separate PR. Once we merge #520 it should be a bit simpler to implement that. I think we'll need to send a RN event from the native side to the JS side when bold or italic button in context menu is pressed so the function can modify the React state of the component in order to modify the current value by adding/removing some characters when toggling bold/italic. |
@bernhardoj Also, I'm curious what's the expected behavior when |
@bernhardoj I think, yes. You can add it to both files. Thanks to that there won't be any TS errors in the app |
c732cd4
to
292f1d8
Compare
@Skalakid updated.
I want to see how WhatsApp handles this case, but WA doesn't handle the format command on the web at all. |
@Skalakid hi, how's the review going? |
Hi, I reviewed the PR, and the code looks good to me, however, together with @tomekzaw, we noticed that this issue is more complex than we thought. The current implementation allows to modify the selected text, however, there are many other cases connected to removing style from the text, that should be handled. Here is a list of them:
I think we should reconsider the current solution and create simple to use API for |
On the other hand, I checked now, and WhatsApp doesn't support removing the styles in the format option. ScreenRecording_12-17-2024.14-13-21_1.MP4So I think we can merge it as it is and enhance the |
src/MarkdownTextInput.web.tsx
Outdated
default: | ||
formatType = 'underline'; | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why underline
is set as default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually asked about this here. Since we only handle the bold, italic, and underline command, I default it to underline. Should I use the "throw" approach?
src/MarkdownTextInput.web.tsx
Outdated
let formatType: FormatType; | ||
switch (formatCommand) { | ||
case 'formatBold': | ||
formatType = 'bold'; | ||
break; | ||
case 'formatItalic': | ||
formatType = 'italic'; | ||
break; | ||
default: | ||
formatType = 'underline'; | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move this switch elsewhere, for example to blockUtils.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/MarkdownTextInput.web.tsx
Outdated
break; | ||
} | ||
|
||
const beforeSelectedText = parsedText.slice(0, contentSelection.current.start); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const beforeSelectedText = parsedText.slice(0, contentSelection.current.start); | |
const prefix = parsedText.slice(0, contentSelection.current.start); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/MarkdownTextInput.web.tsx
Outdated
const selectedText = parsedText.slice(contentSelection.current.start, contentSelection.current.end); | ||
const formattedText = formatSelection?.(selectedText, formatType) ?? selectedText; | ||
const formattedTextDiffLength = formattedText.length - selectedText.length; | ||
const afterSelectedText = parsedText.slice(contentSelection.current.end); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const afterSelectedText = parsedText.slice(contentSelection.current.end); | |
const suffix = parsedText.slice(contentSelection.current.end); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/MarkdownTextInput.web.tsx
Outdated
const beforeSelectedText = parsedText.slice(0, contentSelection.current.start); | ||
const selectedText = parsedText.slice(contentSelection.current.start, contentSelection.current.end); | ||
const formattedText = formatSelection?.(selectedText, formatType) ?? selectedText; | ||
const formattedTextDiffLength = formattedText.length - selectedText.length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const formattedTextDiffLength = formattedText.length - selectedText.length; | |
const diffLength = formattedText.length - selectedText.length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
||
const beforeSelectedText = parsedText.slice(0, contentSelection.current.start); | ||
const selectedText = parsedText.slice(contentSelection.current.start, contentSelection.current.end); | ||
const formattedText = formatSelection?.(selectedText, formatType) ?? selectedText; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can check if the returned text actually changed after running formatSelection
, to avoid unnecessary text parsing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/MarkdownTextInput.web.tsx
Outdated
if (!contentSelection.current) { | ||
return { | ||
text: '', | ||
cursorPosition: 0, | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about early returning also when contentSelection length between start and end is less than 1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Details
Handle the context menu Format command for Bold and Italic.
Related Issues
Expensify/App#53145
Manual Tests
ios.mweb.mp4
web.mp4
Linked PRs