From cd365cd78212cfd05835f063b36b60de6c5509a4 Mon Sep 17 00:00:00 2001 From: Matt Rothenberg Date: Wed, 9 Feb 2022 11:40:54 -0500 Subject: [PATCH] feat: replace anchorme with linkify-it (#25) --- package.json | 3 +- src/components/cell.tsx | 82 ++++++++++++++++++++++------------------- yarn.lock | 22 ++++++++--- 3 files changed, 63 insertions(+), 44 deletions(-) diff --git a/package.json b/package.json index 2221477..6faa915 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "@storybook/addons": "^6.1.21", "@storybook/react": "^6.1.21", "@types/d3": "^6.3.0", + "@types/linkify-it": "^3.0.2", "@types/react": "^17.0.3", "@types/react-dom": "^17.0.2", "@types/react-virtualized": "^9.21.11", @@ -101,12 +102,12 @@ "@types/lodash": "^4.0.6", "@types/react-virtualized-auto-sizer": "^1.0.0", "@types/react-window": "^1.8.2", - "anchorme": "^2.1.2", "d3": "^6.6.0", "date-fns": "^2.19.0", "dompurify": "^2.2.9", "downshift": "^6.1.1", "immer": "^9.0.12", + "linkify-it": "^3.0.3", "lodash": "^4.17.21", "lodash-es": "^4.17.21", "match-sorter": "^6.3.0", diff --git a/src/components/cell.tsx b/src/components/cell.tsx index 8eba132..8276273 100644 --- a/src/components/cell.tsx +++ b/src/components/cell.tsx @@ -1,12 +1,14 @@ import React, { useEffect } from 'react'; import { areEqual } from 'react-window'; import tw, { TwStyle } from 'twin.macro'; -import anchorme from 'anchorme'; +import Linkify from 'linkify-it'; import { cellTypeMap } from '../store'; import { DashIcon, DiffModifiedIcon, PlusIcon } from '@primer/octicons-react'; import DOMPurify from 'dompurify'; import { EditableCell } from './editable-cell'; +const linkify = Linkify().add('ftp:', null).add('mailto:', null); + interface CellProps { type: string; value: any; @@ -47,37 +49,39 @@ export const Cell = React.memo(function (props: CellProps) { onFocusChange, background, style = {}, - onMouseEnter = () => { }, + onMouseEnter = () => {}, } = props; // @ts-ignore const cellInfo = cellTypeMap[type]; - const { cell: CellComponent } = cellInfo || {} + const { cell: CellComponent } = cellInfo || {}; - const displayValue = (formattedValue || value || "").toString(); + const displayValue = (formattedValue || value || '').toString(); const isLongValue = (displayValue || '').length > 23; - const stringWithLinks = React.useMemo( - () => displayValue ? ( - DOMPurify.sanitize( - anchorme({ - input: displayValue + '', - options: { - attributes: { - target: '_blank', - rel: 'noopener', - }, - }, - }) - ) - ) : "", - [value] - ) + const stringWithLinks = React.useMemo(() => { + if (!displayValue) return ''; + + const sanitized = DOMPurify.sanitize(displayValue); + // Does the sanitized string contain any links? + if (!linkify.test(sanitized)) return sanitized; + + // If so, we need to linkify it. + const matches = linkify.match(sanitized); + + // If there are no matches, we can just return the sanitized string. + if (!matches || matches.length === 0) return sanitized; + + // Otherwise, let's naively use the first match. + return ` + ${matches[0].url} + `; + }, [value]); useEffect(() => { - if (!isFocused) return - onMouseEnter() - }, [isFocused]) + if (!isFocused) return; + onMouseEnter(); + }, [isFocused]); if (!cellInfo) return null; @@ -91,14 +95,15 @@ export const Cell = React.memo(function (props: CellProps) { 'modified-row': DiffModifiedIcon, }[status || '']; const statusColor = - isFirstColumn && - // @ts-ignore - { - new: 'text-green-400', - old: 'text-pink-400', - modified: 'text-yellow-500', - 'modified-row': 'text-yellow-500', - }[status || ''] || "" + (isFirstColumn && + // @ts-ignore + { + new: 'text-green-400', + old: 'text-pink-400', + modified: 'text-yellow-500', + 'modified-row': 'text-yellow-500', + }[status || '']) || + ''; return (
+ }} + > + onRowDelete={onRowDelete} + > onMouseEnter?.()} > @@ -214,5 +220,5 @@ const CellInner = React.memo(function CellInner({
)} - ) -}) \ No newline at end of file + ); +}); diff --git a/yarn.lock b/yarn.lock index 527bcc3..f88d809 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3251,6 +3251,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== +"@types/linkify-it@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.2.tgz#fd2cd2edbaa7eaac7e7f3c1748b52a19143846c9" + integrity sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA== + "@types/lodash@^4.0.6": version "4.14.171" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.171.tgz#f01b3a5fe3499e34b622c362a46a609fdb23573b" @@ -3837,11 +3842,6 @@ alphanum-sort@^1.0.0, alphanum-sort@^1.0.2: resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= -anchorme@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/anchorme/-/anchorme-2.1.2.tgz#4abc7e128a8a42d0036a61ebb9b18bbc032fa52a" - integrity sha512-2iPY3kxDDZvtRzauqKDb4v7a5sTF4GZ+esQTY8nGYvmhAtGTeFPMn4cRnvyWS1qmtPTP0Mv8hyLOp9l3ZzWMKg== - ansi-align@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" @@ -9793,6 +9793,13 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= +linkify-it@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e" + integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ== + dependencies: + uc.micro "^1.0.1" + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -14795,6 +14802,11 @@ ua-parser-js@^0.7.18: resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g== +uc.micro@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"