Skip to content

Commit

Permalink
Merge pull request #28789 from teneeto/ts-migration/clipboard
Browse files Browse the repository at this point in the history
[No QA][TS migration] Migrate 'Clipboard' lib to TypeScript
  • Loading branch information
Julesssss authored Nov 3, 2023
2 parents 839545e + 9618154 commit 399b65e
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 39 deletions.
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 {CanSetHtml, SetHtml, SetString} 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,
};
63 changes: 42 additions & 21 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 * as Browser from '@libs/Browser';
import CONST from '@src/CONST';
import {CanSetHtml, SetHtml, SetString} 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 | 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,21 @@ 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();

if (selection === null) {
return;
}

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 Down Expand Up @@ -60,23 +83,23 @@ function setHTMLSync(html, text) {

selection.removeAllRanges();

if (isComposer) {
const anchorSelection = originalSelection as AnchorSelection;

if (isComposer && 'start' in originalSelection) {
firstAnchorChild.setSelectionRange(originalSelection.start, originalSelection.end, originalSelection.direction);
} else if (originalSelection.anchorNode && originalSelection.focusNode) {
} else if (anchorSelection.anchorNode && anchorSelection.focusNode) {
// When copying to the clipboard here, the original values of anchorNode and focusNode will be null since there will be no user selection.
// We are adding a check to prevent null values from being passed to setBaseAndExtent, in accordance with the standards of the Selection API as outlined here: https://w3c.github.io/selection-api/#dom-selection-setbaseandextent.
selection.setBaseAndExtent(originalSelection.anchorNode, originalSelection.anchorOffset, originalSelection.focusNode, originalSelection.focusOffset);
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 @@ -93,8 +116,8 @@ const setHtml = (html, text) => {
setHTMLSync(html, text);
} else {
navigator.clipboard.write([
// eslint-disable-next-line no-undef
new ClipboardItem({
/* eslint-disable @typescript-eslint/naming-convention */
'text/html': new Blob([html], {type: 'text/html'}),
'text/plain': new Blob([text], {type: 'text/plain'}),
}),
Expand All @@ -104,10 +127,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};
}

0 comments on commit 399b65e

Please sign in to comment.