Skip to content

Commit

Permalink
Merge branch 'main' into @Skalakid/inline-images-feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Skalakid committed Aug 30, 2024
2 parents ee19f1f + 0d7552b commit c6c70c1
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 24 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,4 @@ lib/

# react-native-live-markdown
android/src/main/assets/react-native-live-markdown-parser.js
.build_complete
8 changes: 7 additions & 1 deletion WebExample/__tests__/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ const setupInput = async (page: Page, action?: 'clear' | 'reset') => {
};

const getCursorPosition = async (elementHandle: Locator) => {
const inputSelectionHandle = await elementHandle.evaluateHandle((div: HTMLInputElement) => ({start: div.selectionStart, end: div.selectionEnd}));
const inputSelectionHandle = await elementHandle.evaluateHandle(
(
div: HTMLInputElement & {
selection: {start: number; end: number};
},
) => div.selection,
);
const selection = await inputSelectionHandle.jsonValue();
return selection;
};
Expand Down
7 changes: 5 additions & 2 deletions 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.117",
"version": "0.1.121",
"description": "Drop-in replacement for React Native's TextInput component with Markdown formatting.",
"main": "lib/commonjs/index",
"module": "lib/module/index",
Expand Down Expand Up @@ -38,6 +38,7 @@
"lint:WebExample": "eslint WebExample --ext .js,.ts,.tsx",
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
"prepare": "bob build && mkdir -p lib/parser && cp parser/react-native-live-markdown-parser.js lib/parser/react-native-live-markdown-parser.js",
"build:watch": "nodemon --watch src --ext .ts,.tsx,.css --exec \"rm -f .build_complete && yarn prepare && yarn pack && touch .build_complete\"",
"release": "release-it"
},
"keywords": [
Expand Down Expand Up @@ -87,6 +88,7 @@
"eslint-plugin-tsdoc": "^0.2.17",
"jest": "^29.6.3",
"jest-environment-jsdom": "^29.7.0",
"nodemon": "^3.1.3",
"prettier": "2.8.8",
"react": "18.2.0",
"react-native": "0.73.4",
Expand All @@ -97,7 +99,8 @@
"typescript": "^5.3.3"
},
"resolutions": {
"@types/react": "17.0.21"
"@types/react": "17.0.21",
"link@^2.1.1": "patch:link@npm%3A2.1.1#./.yarn/patches/link-npm-2.1.1-1c9fea076e.patch"
},
"peerDependencies": {
"react": "*",
Expand Down
29 changes: 12 additions & 17 deletions src/MarkdownTextInput.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ let focusTimeout: NodeJS.Timeout | null = null;
type MarkdownTextInputElement = HTMLDivElement &
HTMLInputElement & {
tree: TreeNode;
selection: Selection;
};

type HTMLMarkdownElement = HTMLElement & {
Expand Down Expand Up @@ -233,14 +234,15 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(
);

const updateRefSelectionVariables = useCallback((newSelection: Selection) => {
if (!divRef.current) {
return;
}
const {start, end} = newSelection;
const markdownHTMLInput = divRef.current as HTMLInputElement;
markdownHTMLInput.selectionStart = start;
markdownHTMLInput.selectionEnd = end;
divRef.current.selection = {start, end};
}, []);

const updateSelection = useCallback(
(e: SyntheticEvent<HTMLDivElement> | null = null, predefinedSelection: Selection | null = null) => {
(e: SyntheticEvent<HTMLDivElement>, predefinedSelection: Selection | null = null) => {
if (!divRef.current) {
return;
}
Expand All @@ -250,9 +252,7 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(
updateRefSelectionVariables(newSelection);
contentSelection.current = newSelection;

if (e) {
handleSelectionChange(e);
}
handleSelectionChange(e);
}
},
[handleSelectionChange, updateRefSelectionVariables],
Expand Down Expand Up @@ -384,9 +384,8 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(
(e.nativeEvent as MarkdownNativeEvent).inputType = 'pasteText';

handleOnChangeText(e);
updateSelection(e);
},
[handleOnChangeText, updateSelection],
[handleOnChangeText],
);

const handleKeyPress = useCallback(
Expand Down Expand Up @@ -423,8 +422,6 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(
onKeyPress(event);
}

updateSelection(event as unknown as SyntheticEvent<HTMLDivElement, Event>);

if (
e.key === 'Enter' &&
// Do not call submit if composition is occuring.
Expand All @@ -445,7 +442,7 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(
}
}
},
[multiline, blurOnSubmit, setEventProps, onKeyPress, updateSelection, handleOnChangeText, onSubmitEditing, insertText],
[multiline, blurOnSubmit, setEventProps, onKeyPress, handleOnChangeText, onSubmitEditing, insertText],
);

const handleFocus: FocusEventHandler<HTMLDivElement> = useCallback(
Expand All @@ -461,7 +458,6 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(
const valueLength = value ? value.length : divRef.current.value.length;
setCursorPosition(divRef.current, valueLength, null);
}
updateSelection(event, contentSelection.current);
}

if (onFocus) {
Expand All @@ -487,7 +483,7 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(
}
}
},
[clearTextOnFocus, onFocus, selectTextOnFocus, setEventProps, updateSelection, value],
[clearTextOnFocus, onFocus, selectTextOnFocus, setEventProps, value],
);

const handleBlur: FocusEventHandler<HTMLDivElement> = useCallback(
Expand All @@ -505,14 +501,13 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(

const handleClick = useCallback(
(e: MouseEvent<HTMLDivElement, globalThis.MouseEvent>) => {
updateSelection(e);
if (!onClick || !divRef.current) {
return;
}
(e.target as HTMLInputElement).value = divRef.current.value;
onClick(e);
},
[onClick, updateSelection],
[onClick],
);

const handleCopy: ClipboardEventHandler<HTMLDivElement> = useCallback((e) => {
Expand Down Expand Up @@ -662,7 +657,6 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(
onKeyDown={handleKeyPress}
onCompositionStart={startComposition}
onCompositionEnd={endComposition}
onKeyUp={updateSelection}
onInput={handleOnChangeText}
onClick={handleClick}
onFocus={handleFocus}
Expand All @@ -674,6 +668,7 @@ const MarkdownTextInput = React.forwardRef<TextInput, MarkdownTextInputProps>(
spellCheck={spellCheck}
dir={dir}
inputMode={inputMode}
onSelect={updateSelection}
/>
);
},
Expand Down
4 changes: 4 additions & 0 deletions src/web/utils/cursorUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ function setCursorPosition(target: MarkdownTextInputElement, startIndex: number,
selection.setBaseAndExtent(range.startContainer, range.startOffset, range.endContainer, range.endOffset);
}

// Update the focused elements zIndex to make sure they are on top and the cursor is beeing set correctly
startTreeNode.element.style.zIndex = '1';
endTreeNode.element.style.zIndex = '1';

scrollIntoView(startTreeNode);
}

Expand Down
2 changes: 1 addition & 1 deletion src/web/utils/inputUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function parseInnerHTMLToText(target: MarkdownTextInputElement, inputType: strin
text += node.textContent;
} else if (node.nodeName === 'BR') {
const parentNode = getTopParentNode(node);
if (parentNode && parentNode.parentElement?.contentEditable !== 'true') {
if (parentNode && parentNode.parentElement?.contentEditable !== 'true' && !!(node as HTMLElement).getAttribute('data-id')) {
// Parse br elements into newlines only if their parent is not a child of the MarkdownTextInputElement (a paragraph when writing or a div when pasting).
// It prevents adding extra newlines when entering text
text += '\n';
Expand Down
2 changes: 1 addition & 1 deletion src/web/utils/parserUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ function parseRangesToHTMLNodes(
currentInput: MarkdownTextInputElement | null = null,
) {
const rootElement: HTMLMarkdownElement = document.createElement('span') as HTMLMarkdownElement;
const textLength = text.replace(/\n/g, '\\n').length;
const textLength = text.length;
const rootNode: TreeNode = {
element: rootElement,
start: 0,
Expand Down
76 changes: 74 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2685,6 +2685,7 @@ __metadata:
eslint-plugin-tsdoc: ^0.2.17
jest: ^29.6.3
jest-environment-jsdom: ^29.7.0
nodemon: ^3.1.3
prettier: 2.8.8
react: 18.2.0
react-native: 0.73.4
Expand Down Expand Up @@ -6922,7 +6923,7 @@ __metadata:
languageName: node
linkType: hard

"chokidar@npm:^3.4.0, chokidar@npm:^3.5.3":
"chokidar@npm:^3.4.0, chokidar@npm:^3.5.2, chokidar@npm:^3.5.3":
version: 3.6.0
resolution: "chokidar@npm:3.6.0"
dependencies:
Expand Down Expand Up @@ -8139,6 +8140,18 @@ __metadata:
languageName: node
linkType: hard

"debug@npm:^4":
version: 4.3.5
resolution: "debug@npm:4.3.5"
dependencies:
ms: 2.1.2
peerDependenciesMeta:
supports-color:
optional: true
checksum: 7c002b51e256257f936dda09eb37167df952758c57badf6bf44bdc40b89a4bcb8e5a0a2e4c7b53f97c69e2970dd5272d33a757378a12c8f8e64ea7bf99e8e86e
languageName: node
linkType: hard

"decamelize-keys@npm:^1.1.0":
version: 1.1.1
resolution: "decamelize-keys@npm:1.1.1"
Expand Down Expand Up @@ -11758,6 +11771,13 @@ __metadata:
languageName: node
linkType: hard

"ignore-by-default@npm:^1.0.1":
version: 1.0.1
resolution: "ignore-by-default@npm:1.0.1"
checksum: 441509147b3615e0365e407a3c18e189f78c07af08564176c680be1fabc94b6c789cad1342ad887175d4ecd5225de86f73d376cec8e06b42fd9b429505ffcf8a
languageName: node
linkType: hard

"ignore@npm:^4.0.6":
version: 4.0.6
resolution: "ignore@npm:4.0.6"
Expand Down Expand Up @@ -15227,6 +15247,26 @@ __metadata:
languageName: node
linkType: hard

"nodemon@npm:^3.1.3":
version: 3.1.3
resolution: "nodemon@npm:3.1.3"
dependencies:
chokidar: ^3.5.2
debug: ^4
ignore-by-default: ^1.0.1
minimatch: ^3.1.2
pstree.remy: ^1.1.8
semver: ^7.5.3
simple-update-notifier: ^2.0.0
supports-color: ^5.5.0
touch: ^3.1.0
undefsafe: ^2.0.5
bin:
nodemon: bin/nodemon.js
checksum: ac2fa8865ab292b7ddf66731487acca4b4282b2728361e0de633c0c74cd705d6a0852b52f785c09469d959241d038ba824f50375622e687a2a318be747d9cd9d
languageName: node
linkType: hard

"nopt@npm:^7.0.0":
version: 7.2.0
resolution: "nopt@npm:7.2.0"
Expand Down Expand Up @@ -16796,6 +16836,13 @@ __metadata:
languageName: node
linkType: hard

"pstree.remy@npm:^1.1.8":
version: 1.1.8
resolution: "pstree.remy@npm:1.1.8"
checksum: 5cb53698d6bb34dfb278c8a26957964aecfff3e161af5fbf7cee00bbe9d8547c7aced4bd9cb193bce15fb56e9e4220fc02a5bf9c14345ffb13a36b858701ec2d
languageName: node
linkType: hard

"pump@npm:^3.0.0":
version: 3.0.0
resolution: "pump@npm:3.0.0"
Expand Down Expand Up @@ -18323,6 +18370,15 @@ __metadata:
languageName: node
linkType: hard

"simple-update-notifier@npm:^2.0.0":
version: 2.0.0
resolution: "simple-update-notifier@npm:2.0.0"
dependencies:
semver: ^7.5.3
checksum: 9ba00d38ce6a29682f64a46213834e4eb01634c2f52c813a9a7b8873ca49cdbb703696f3290f3b27dc067de6d9418b0b84bef22c3eb074acf352529b2d6c27fd
languageName: node
linkType: hard

"simply-deferred@git+https://github.com/Expensify/simply-deferred.git#77a08a95754660c7bd6e0b6979fdf84e8e831bf5":
version: 3.0.0
resolution: "simply-deferred@https://github.com/Expensify/simply-deferred.git#commit=77a08a95754660c7bd6e0b6979fdf84e8e831bf5"
Expand Down Expand Up @@ -18992,7 +19048,7 @@ __metadata:
languageName: node
linkType: hard

"supports-color@npm:^5.3.0":
"supports-color@npm:^5.3.0, supports-color@npm:^5.5.0":
version: 5.5.0
resolution: "supports-color@npm:5.5.0"
dependencies:
Expand Down Expand Up @@ -19350,6 +19406,15 @@ __metadata:
languageName: node
linkType: hard

"touch@npm:^3.1.0":
version: 3.1.1
resolution: "touch@npm:3.1.1"
bin:
nodetouch: bin/nodetouch.js
checksum: fb8c54207500eb760b6b9d77b9c5626cc027c9ad44431eed4268845f00f8c6bbfc95ce7e9da8e487f020aa921982a8bc5d8e909d0606e82686bd0a08a8e0539b
languageName: node
linkType: hard

"tough-cookie@npm:^4.1.2":
version: 4.1.3
resolution: "tough-cookie@npm:4.1.3"
Expand Down Expand Up @@ -19843,6 +19908,13 @@ __metadata:
languageName: node
linkType: hard

"undefsafe@npm:^2.0.5":
version: 2.0.5
resolution: "undefsafe@npm:2.0.5"
checksum: f42ab3b5770fedd4ada175fc1b2eb775b78f609156f7c389106aafd231bfc210813ee49f54483d7191d7b76e483bc7f537b5d92d19ded27156baf57592eb02cc
languageName: node
linkType: hard

"underscore@npm:^1.13.1":
version: 1.13.6
resolution: "underscore@npm:1.13.6"
Expand Down

0 comments on commit c6c70c1

Please sign in to comment.