Skip to content

Commit

Permalink
Merge branch 'main' into @tomekzaw/worklets
Browse files Browse the repository at this point in the history
  • Loading branch information
tomekzaw committed Oct 4, 2024
2 parents 0fd1dc2 + 86c8497 commit 3a5f413
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 17 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@expensify/react-native-live-markdown",
"version": "0.1.162",
"version": "0.1.164",
"description": "Drop-in replacement for React Native's TextInput component with Markdown formatting.",
"main": "lib/commonjs/index",
"module": "lib/module/index",
Expand Down
4 changes: 2 additions & 2 deletions src/MarkdownTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {MarkdownStyle} from './MarkdownTextInputDecoratorViewNativeComponen
import NativeLiveMarkdownModule from './NativeLiveMarkdownModule';
import {mergeMarkdownStyleWithDefault} from './styleUtils';
import type {PartialMarkdownStyle} from './styleUtils';
import type {MarkdownRange} from './commonTypes';
import type {InlineImagesInputProps, MarkdownRange} from './commonTypes';

declare global {
// eslint-disable-next-line no-var
Expand Down Expand Up @@ -51,7 +51,7 @@ function unregisterParser(parserId: number) {
global.jsi_unregisterMarkdownWorklet(parserId);
}

interface MarkdownTextInputProps extends TextInputProps {
interface MarkdownTextInputProps extends TextInputProps, InlineImagesInputProps {
markdownStyle?: PartialMarkdownStyle;
parser: (value: string) => MarkdownRange[];
}
Expand Down
12 changes: 9 additions & 3 deletions src/MarkdownTextInput.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ import type {MarkdownStyle} from './MarkdownTextInputDecoratorViewNativeComponen
import {getElementHeight, getPlaceholderValue, isEventComposing, normalizeValue, parseInnerHTMLToText} from './web/utils/inputUtils';
import {parseToReactDOMStyle, processMarkdownStyle} from './web/utils/webStyleUtils';
import {forceRefreshAllImages} from './web/inputElements/inlineImage';
import type {InlineImagesInputProps} from './commonTypes';

Check failure on line 27 in src/MarkdownTextInput.web.tsx

View workflow job for this annotation

GitHub Actions / check

'/home/runner/work/react-native-live-markdown/react-native-live-markdown/src/commonTypes.ts' imported multiple times

const useClientEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;

interface MarkdownTextInputProps extends TextInputProps {
interface MarkdownTextInputProps extends TextInputProps, InlineImagesInputProps {
markdownStyle?: MarkdownStyle;
parser: (text: string) => MarkdownRange[];
onClick?: (e: MouseEvent<HTMLDivElement>) => void;
Expand Down Expand Up @@ -102,6 +103,8 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(
id,
inputMode,
onTouchStart,
addAuthTokenToImageURLCallback,
imagePreviewAuthRequiredURLs,
},
ref,
) => {
Expand Down Expand Up @@ -163,7 +166,10 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(
if (text === null) {
return {text: divRef.current.value, cursorPosition: null};
}
const parsedText = updateInputStructure(parserFunction, target, text, cursorPosition, multiline, customMarkdownStyles, false, shouldForceDOMUpdate, shouldScrollIntoView);
const parsedText = updateInputStructure(parserFunction, target, text, cursorPosition, multiline, customMarkdownStyles, false, shouldForceDOMUpdate, shouldScrollIntoView, {
addAuthTokenToImageURLCallback,
imagePreviewAuthRequiredURLs,
});
divRef.current.value = parsedText.text;

if (history.current && shouldAddToHistory) {
Expand All @@ -172,7 +178,7 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(

return parsedText;
},
[multiline],
[addAuthTokenToImageURLCallback, imagePreviewAuthRequiredURLs, multiline],
);

const processedMarkdownStyle = useMemo(() => {
Expand Down
7 changes: 6 additions & 1 deletion src/commonTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ interface MarkdownRange {
depth?: number;
}

export type {MarkdownType, MarkdownRange};
type InlineImagesInputProps = {
addAuthTokenToImageURLCallback?: (url: string) => string;
imagePreviewAuthRequiredURLs?: string[];
};

export type {MarkdownType, MarkdownRange, InlineImagesInputProps};
15 changes: 13 additions & 2 deletions src/web/inputElements/inlineImage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {HTMLMarkdownElement, MarkdownTextInputElement} from '../../MarkdownTextInput.web';
import type {MarkdownRange} from '../../commonTypes';
import type {InlineImagesInputProps, MarkdownRange} from '../../commonTypes';
import {parseStringWithUnitToNumber} from '../../styleUtils';
import type {PartialMarkdownStyle} from '../../styleUtils';
import type {TreeNode} from '../utils/treeUtils';
Expand Down Expand Up @@ -141,11 +141,22 @@ function updateImageTreeNode(targetNode: TreeNode, newElement: HTMLMarkdownEleme
}

/** The main function that adds inline image preview to the node */
function addInlineImagePreview(currentInput: MarkdownTextInputElement, targetNode: TreeNode, text: string, ranges: MarkdownRange[], markdownStyle: PartialMarkdownStyle) {
function addInlineImagePreview(
currentInput: MarkdownTextInputElement,
targetNode: TreeNode,
text: string,
ranges: MarkdownRange[],
markdownStyle: PartialMarkdownStyle,
inlineImagesProps: InlineImagesInputProps,
) {
const {addAuthTokenToImageURLCallback, imagePreviewAuthRequiredURLs} = inlineImagesProps;
const linkRange = ranges.find((r) => r.type === 'link');
let imageHref = '';
if (linkRange) {
imageHref = text.substring(linkRange.start, linkRange.start + linkRange.length);
if (addAuthTokenToImageURLCallback && imagePreviewAuthRequiredURLs && imagePreviewAuthRequiredURLs.find((url) => imageHref.startsWith(url))) {
imageHref = addAuthTokenToImageURLCallback(imageHref);
}
}

const imageMarginTop = parseStringWithUnitToNumber(`${markdownStyle.inlineImage?.marginTop}`);
Expand Down
5 changes: 3 additions & 2 deletions src/web/utils/blockUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {MarkdownTextInputElement} from '../../MarkdownTextInput.web';
import type {MarkdownRange} from '../../commonTypes';
import type {InlineImagesInputProps, MarkdownRange} from '../../commonTypes';
import type {PartialMarkdownStyle} from '../../styleUtils';
import {addInlineImagePreview} from '../inputElements/inlineImage';
import type {NodeType, TreeNode} from './treeUtils';
Expand Down Expand Up @@ -98,10 +98,11 @@ function extendBlockStructure(
ranges: MarkdownRange[],
text: string,
markdownStyle: PartialMarkdownStyle,
inlineImagesProps: InlineImagesInputProps,
) {
switch (currentRange.type) {
case 'inline-image':
return addInlineImagePreview(currentInput, targetNode, text, ranges, markdownStyle);
return addInlineImagePreview(currentInput, targetNode, text, ranges, markdownStyle, inlineImagesProps);
default:
break;
}
Expand Down
6 changes: 4 additions & 2 deletions src/web/utils/cursorUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ function setCursorPosition(target: MarkdownTextInputElement, startIndex: number,
if (startTreeNode.type === 'br') {
range.setStartBefore(startTreeNode.element);
} else {
range.setStart(startTreeNode.element.childNodes[0] as ChildNode, start - startTreeNode.start);
const startElement = startTreeNode.element;
range.setStart((startElement.childNodes[0] || startElement) as ChildNode, start - startTreeNode.start);
}

if (endTreeNode.type === 'br') {
range.setEndBefore(endTreeNode.element);
} else {
range.setEnd(endTreeNode.element.childNodes[0] as ChildNode, (end || start) - endTreeNode.start);
const endElement = endTreeNode.element;
range.setEnd((endElement.childNodes[0] || endElement) as ChildNode, (end || start) - endTreeNode.start);
}

if (!end) {
Expand Down
8 changes: 5 additions & 3 deletions src/web/utils/parserUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {NodeType, TreeNode} from './treeUtils';
import type {PartialMarkdownStyle} from '../../styleUtils';
import {getCurrentCursorPosition, moveCursorToEnd, setCursorPosition} from './cursorUtils';
import {addStyleToBlock, extendBlockStructure, getFirstBlockMarkdownRange, isBlockMarkdownType} from './blockUtils';
import type {MarkdownRange} from '../../commonTypes';
import type {InlineImagesInputProps, MarkdownRange} from '../../commonTypes';
import {getAnimationCurrentTimes, updateAnimationsTime} from './animationUtils';

type Paragraph = {
Expand Down Expand Up @@ -151,6 +151,7 @@ function parseRangesToHTMLNodes(
markdownStyle: PartialMarkdownStyle = {},
disableInlineStyles = false,
currentInput: MarkdownTextInputElement | null = null,
inlineImagesProps: InlineImagesInputProps = {},
) {
const rootElement: HTMLMarkdownElement = document.createElement('span') as HTMLMarkdownElement;
const textLength = text.length;
Expand Down Expand Up @@ -224,7 +225,7 @@ function parseRangesToHTMLNodes(
const spanNode = appendNode(span, currentParentNode, range.type, range.length);

if (isMultiline && !disableInlineStyles && currentInput) {
currentParentNode = extendBlockStructure(currentInput, currentParentNode, range, lineMarkdownRanges, text, markdownStyle);
currentParentNode = extendBlockStructure(currentInput, currentParentNode, range, lineMarkdownRanges, text, markdownStyle, inlineImagesProps);
}

if (lineMarkdownRanges.length > 0 && nextRangeStartIndex < endOfCurrentRange && range.type !== 'syntax') {
Expand Down Expand Up @@ -280,6 +281,7 @@ function updateInputStructure(
alwaysMoveCursorToTheEnd = false,
shouldForceDOMUpdate = false,
shouldScrollIntoView = false,
inlineImagesProps: InlineImagesInputProps = {},
) {
const targetElement = target;

Expand All @@ -298,7 +300,7 @@ function updateInputStructure(

// We don't want to parse text with single '\n', because contentEditable represents it as invisible <br />
if (text) {
const {dom, tree} = parseRangesToHTMLNodes(text, markdownRanges, isMultiline, markdownStyle, false, targetElement);
const {dom, tree} = parseRangesToHTMLNodes(text, markdownRanges, isMultiline, markdownStyle, false, targetElement, inlineImagesProps);

if (shouldForceDOMUpdate || targetElement.innerHTML !== dom.innerHTML) {
const animationTimes = getAnimationCurrentTimes(targetElement);
Expand Down
6 changes: 5 additions & 1 deletion src/web/utils/treeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,13 @@ function findHTMLElementInTree(treeRoot: TreeNode, element: HTMLElement): TreeNo

function getTreeNodeByIndex(treeRoot: TreeNode, index: number): TreeNode | null {
let el: TreeNode | null = treeRoot;

let i = 0;
let newLineGenerated = false;

if (treeRoot.length === 0) {
return treeRoot;
}

while (el && el.childNodes.length > 0 && i < el.childNodes.length) {
const child = el.childNodes[i] as TreeNode;

Expand Down

0 comments on commit 3a5f413

Please sign in to comment.