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

[No QA][TS migration] Migrate 'Clipboard' lib to TypeScript #28789

Merged
merged 18 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from 10 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
18 changes: 0 additions & 18 deletions src/libs/Clipboard/index.native.js

This file was deleted.

19 changes: 19 additions & 0 deletions src/libs/Clipboard/index.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Clipboard from '@react-native-clipboard/clipboard';
import {SetString, CanSetHtml, SetHtml} from './types';

/**
* Sets a string on the Clipboard object via @react-native-clipboard/clipboard
*/
const setString: SetString = (text) => {
Clipboard.setString(text);
};

// We don't want to set HTML on native platforms so noop them.
const canSetHtml: CanSetHtml = () => false;
const setHtml: SetHtml = () => {};

export default {
setString,
canSetHtml,
setHtml,
};
70 changes: 43 additions & 27 deletions src/libs/Clipboard/index.js → src/libs/Clipboard/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
import Clipboard from '@react-native-clipboard/clipboard';
import lodashGet from 'lodash/get';
import CONST from '../../CONST';
import * as Browser from '../Browser';
import {SetString, SetHtml, CanSetHtml} from './types';

const canSetHtml = () => lodashGet(navigator, 'clipboard.write');
type ComposerSelection = {
start: number;
end: number;
direction: 'forward' | 'backward' | 'none';
};

type AnchorSelection = {
anchorOffset: number;
focusOffset: number;
anchorNode: Node;
focusNode: Node;
};

type NullableObject<T> = {[K in keyof T]: T[K] | null};

type OriginalSelection = ComposerSelection | Partial<NullableObject<AnchorSelection>>;

const canSetHtml: CanSetHtml =
() =>
(...args: ClipboardItems) =>
navigator?.clipboard?.write([...args]);

/**
* Deprecated method to write the content as HTML to clipboard.
* @param {String} html HTML representation
* @param {String} text Plain text representation
*/
function setHTMLSync(html, text) {
function setHTMLSync(html: string, text: string) {
const node = document.createElement('span');
node.textContent = html;
node.style.all = 'unset';
Expand All @@ -21,16 +39,16 @@ function setHTMLSync(html, text) {
node.addEventListener('copy', (e) => {
e.stopPropagation();
e.preventDefault();
e.clipboardData.clearData();
e.clipboardData.setData('text/html', html);
e.clipboardData.setData('text/plain', text);
e.clipboardData?.clearData();
e.clipboardData?.setData('text/html', html);
e.clipboardData?.setData('text/plain', text);
});
document.body.appendChild(node);

const selection = window.getSelection();
const firstAnchorChild = selection.anchorNode && selection.anchorNode.firstChild;
const selection = window?.getSelection();
const firstAnchorChild = selection?.anchorNode?.firstChild;
const isComposer = firstAnchorChild instanceof HTMLTextAreaElement;
let originalSelection = null;
let originalSelection: OriginalSelection | null = null;
if (isComposer) {
originalSelection = {
start: firstAnchorChild.selectionStart,
Expand All @@ -39,17 +57,17 @@ function setHTMLSync(html, text) {
};
} else {
originalSelection = {
anchorNode: selection.anchorNode,
anchorOffset: selection.anchorOffset,
focusNode: selection.focusNode,
focusOffset: selection.focusOffset,
anchorNode: selection?.anchorNode,
anchorOffset: selection?.anchorOffset,
focusNode: selection?.focusNode,
focusOffset: selection?.focusOffset,
};
}

selection.removeAllRanges();
selection?.removeAllRanges();
const range = document.createRange();
range.selectNodeContents(node);
selection.addRange(range);
selection?.addRange(range);

teneeto marked this conversation as resolved.
Show resolved Hide resolved
try {
document.execCommand('copy');
Expand All @@ -58,23 +76,22 @@ function setHTMLSync(html, text) {
// See https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#the-copy-command for more details.
}

selection.removeAllRanges();
selection?.removeAllRanges();

if (isComposer) {
if (isComposer && 'start' in originalSelection) {
firstAnchorChild.setSelectionRange(originalSelection.start, originalSelection.end, originalSelection.direction);
} else {
selection.setBaseAndExtent(originalSelection.anchorNode, originalSelection.anchorOffset, originalSelection.focusNode, originalSelection.focusOffset);
const anchorSelection = originalSelection as AnchorSelection;
selection?.setBaseAndExtent(anchorSelection.anchorNode, anchorSelection.anchorOffset, anchorSelection.focusNode, anchorSelection.focusOffset);
}

document.body.removeChild(node);
}

/**
* Writes the content as HTML if the web client supports it.
* @param {String} html HTML representation
* @param {String} text Plain text representation
*/
const setHtml = (html, text) => {
const setHtml: SetHtml = (html: string, text: string) => {
if (!html || !text) {
return;
}
Expand All @@ -91,9 +108,10 @@ const setHtml = (html, text) => {
setHTMLSync(html, text);
} else {
navigator.clipboard.write([
// eslint-disable-next-line no-undef
new ClipboardItem({
// eslint-disable-next-line @typescript-eslint/naming-convention
'text/html': new Blob([html], {type: 'text/html'}),
// eslint-disable-next-line @typescript-eslint/naming-convention
'text/plain': new Blob([text], {type: 'text/plain'}),
teneeto marked this conversation as resolved.
Show resolved Hide resolved
}),
]);
Expand All @@ -102,10 +120,8 @@ const setHtml = (html, text) => {

/**
* Sets a string on the Clipboard object via react-native-web
*
* @param {String} text
*/
const setString = (text) => {
const setString: SetString = (text) => {
Clipboard.setString(text);
};

Expand Down
5 changes: 5 additions & 0 deletions src/libs/Clipboard/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type SetString = (text: string) => void;
type SetHtml = (html: string, text: string) => void;
type CanSetHtml = (() => (...args: ClipboardItems) => Promise<void>) | (() => boolean);

export type {SetString, CanSetHtml, SetHtml};
11 changes: 11 additions & 0 deletions src/types/modules/react-native-web.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable import/prefer-default-export */
/* eslint-disable @typescript-eslint/consistent-type-definitions */
declare module 'react-native-web' {
class Clipboard {
static isAvailable(): boolean;
static getString(): Promise<string>;
static setString(text: string): boolean;
}

export {Clipboard};
}
Loading