From 3a0bf3e1610a4eaad256a71a7c9296fb6c812d7d Mon Sep 17 00:00:00 2001 From: Amelia Wattenberger Date: Fri, 21 Jan 2022 13:36:23 -0500 Subject: [PATCH 01/15] fix: make color swatches em width in case font-size is decreased --- src/components/cells/color.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/cells/color.tsx b/src/components/cells/color.tsx index d0b2f33..3dbd791 100644 --- a/src/components/cells/color.tsx +++ b/src/components/cells/color.tsx @@ -11,7 +11,7 @@ interface ColorCellProps { export function ColorCell(props: ColorCellProps) { return ( -
Date: Fri, 21 Jan 2022 13:37:25 -0500 Subject: [PATCH 02/15] v0.13.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 78cf4e2..a06b474 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.13.0", + "version": "0.13.1", "license": "MIT", "main": "dist/index.js", "typings": "dist/index.d.ts", From 00df00c04bfa9eac55ca91ce46bbc2b0ebbc37e6 Mon Sep 17 00:00:00 2001 From: Amelia Wattenberger Date: Mon, 31 Jan 2022 12:20:34 -0500 Subject: [PATCH 03/15] fix: make color check less eager --- src/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store.ts b/src/store.ts index 3daafd7..67d1b0c 100644 --- a/src/store.ts +++ b/src/store.ts @@ -577,7 +577,7 @@ function generateSchema(data: any[]) { try { if (typeof value === 'string') { const color = rgb(value); - return !!color; + return !!color && !Number.isNaN(color.r); } return false; } catch (e) { From f4bd1bbeaad9eb69a3c93d5ef3924c9795dee27a Mon Sep 17 00:00:00 2001 From: Amelia Wattenberger Date: Mon, 31 Jan 2022 12:23:02 -0500 Subject: [PATCH 04/15] v0.13.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a06b474..23f63b2 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.13.1", + "version": "0.13.2", "license": "MIT", "main": "dist/index.js", "typings": "dist/index.d.ts", From e19b686b1da93803449430c396b82d4caac405e4 Mon Sep 17 00:00:00 2001 From: Amelia Wattenberger Date: Mon, 31 Jan 2022 16:39:26 -0500 Subject: [PATCH 05/15] fix: sort blank number cells to end --- src/store.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/store.ts b/src/store.ts index 67d1b0c..bf9e54f 100644 --- a/src/store.ts +++ b/src/store.ts @@ -513,6 +513,10 @@ const getSortFunction = (sort: string[], typeOfValue: string) => { aVal = (aVal || '')?.toUpperCase?.() || ''; if (!aVal || aVal === '\n') aVal = direction === 'asc' ? 'zzzzzz' : ''; aVal = aVal.trimStart(); + } else if (typeOfValue === 'number') { + aVal = Number.isFinite(aVal) + ? aVal + : Infinity * (direction === 'asc' ? 1 : -1); } // @ts-ignore let bVal = b[columnName]; @@ -520,8 +524,11 @@ const getSortFunction = (sort: string[], typeOfValue: string) => { bVal = (bVal || '')?.toUpperCase?.() || ''; if (!bVal || bVal === '\n') bVal = direction === 'asc' ? 'zzzzzz' : ''; bVal = bVal.trimStart(); + } else if (typeOfValue === 'number') { + bVal = Number.isFinite(bVal) + ? bVal + : Infinity * (direction === 'asc' ? 1 : -1); } - return direction == 'desc' ? // @ts-ignore descending(aVal, bVal) From 5c2d7a5c4e287e5b9eb59bcc7b4920ee7faf3768 Mon Sep 17 00:00:00 2001 From: Amelia Wattenberger Date: Mon, 31 Jan 2022 16:42:12 -0500 Subject: [PATCH 06/15] v0.13.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 23f63b2..43261bf 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.13.2", + "version": "0.13.3", "license": "MIT", "main": "dist/index.js", "typings": "dist/index.d.ts", From c7faa3e2c96481a9207ac68f2220ec9f1daa93f5 Mon Sep 17 00:00:00 2001 From: Matt Rothenberg Date: Tue, 8 Feb 2022 15:08:19 -0500 Subject: [PATCH 07/15] security: bump immer (#24) --- package.json | 4 ++-- src/store.ts | 41 ++++++++++++++++++++++++++++++++++++----- yarn.lock | 16 ++++++++-------- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 43261bf..1871768 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "date-fns": "^2.19.0", "dompurify": "^2.2.9", "downshift": "^6.1.1", - "immer": "^8.0.2", + "immer": "^9.0.12", "lodash": "^4.17.21", "lodash-es": "^4.17.21", "match-sorter": "^6.3.0", @@ -115,6 +115,6 @@ "react-virtualized-auto-sizer": "^1.0.5", "react-window": "^1.8.6", "twin.macro": "^2.6.2", - "zustand": "^3.3.3" + "zustand": "^3.6.9" } } diff --git a/src/store.ts b/src/store.ts index bf9e54f..070febd 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,6 +1,13 @@ import tw from 'twin.macro'; -import create, { StateCreator } from 'zustand'; +import create, { + GetState, + SetState, + State, + StateCreator, + StoreApi, +} from 'zustand'; import produce from 'immer'; +import type { Draft } from 'immer'; import { format as d3Format, timeFormat, @@ -29,10 +36,34 @@ import { StringFilter } from './components/filters/string'; import { CategoryFilter } from './components/filters/category'; import { RangeFilter } from './components/filters/range'; -export const immer = ( - config: StateCreator void) => void> -): StateCreator => (set, get, api) => - config((fn) => set(produce(fn) as (state: T) => T), get, api); +const immer = < + T extends State, + CustomSetState extends SetState, + CustomGetState extends GetState, + CustomStoreApi extends StoreApi +>( + config: StateCreator< + T, + (partial: ((draft: Draft) => void) | T, replace?: boolean) => void, + CustomGetState, + CustomStoreApi + > +): StateCreator => ( + set, + get, + api +) => + config( + (partial, replace) => { + const nextState = + typeof partial === 'function' + ? produce(partial as (state: Draft) => T) + : (partial as T); + return set(nextState, replace); + }, + get, + api + ); export type GridState = { data: any[]; diff --git a/yarn.lock b/yarn.lock index 4a33c5a..527bcc3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8398,10 +8398,10 @@ immer@8.0.1: resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA== -immer@^8.0.2: - version "8.0.4" - resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.4.tgz#3a21605a4e2dded852fb2afd208ad50969737b7a" - integrity sha512-jMfL18P+/6P6epANRvRk6q8t+3gGhqsJ9EuJ25AXE+9bNTYtssvzeYbEd0mXRYWCmmXSIbnlpz6vd6iJlmGGGQ== +immer@^9.0.12: + version "9.0.12" + resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.12.tgz#2d33ddf3ee1d247deab9d707ca472c8c942a0f20" + integrity sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA== import-cwd@^3.0.0: version "3.0.0" @@ -15567,10 +15567,10 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zustand@^3.3.3: - version "3.5.6" - resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.5.6.tgz#c28cfbdfdd999d26d1a94ea105a6fd1da56ed38a" - integrity sha512-8XrpRO5scF8MSxeAlu7vFupmLG+5MTWhT+6+3QNsihs0QZfOjaArFyvenUgrk30WdZVGVHLHXBhbqC2/QzLeMA== +zustand@^3.6.9: + version "3.6.9" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.6.9.tgz#f61a756ddea9f95c7ee7cfd3af2f88c10078afbc" + integrity sha512-OvDNu/jEWpRnEC7k8xh8GKjqYog7td6FZrLMuHs/IeI8WhrCwV+FngVuwMIFhp5kysZXr6emaeReMqjLGaldAQ== zwitch@^1.0.0: version "1.0.5" From 5bbba37df6424cb82255eee269a96c4d0d2902fa Mon Sep 17 00:00:00 2001 From: Matt Rothenberg Date: Tue, 8 Feb 2022 15:10:45 -0500 Subject: [PATCH 08/15] v0.13.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1871768..2221477 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.13.3", + "version": "0.13.4", "license": "MIT", "main": "dist/index.js", "typings": "dist/index.d.ts", From cd365cd78212cfd05835f063b36b60de6c5509a4 Mon Sep 17 00:00:00 2001 From: Matt Rothenberg Date: Wed, 9 Feb 2022 11:40:54 -0500 Subject: [PATCH 09/15] 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" From 2eb6655de353a22573ea281b1ba34414ae3fd782 Mon Sep 17 00:00:00 2001 From: Matt Rothenberg Date: Wed, 9 Feb 2022 11:43:03 -0500 Subject: [PATCH 10/15] v0.13.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6faa915..0ef174e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.13.4", + "version": "0.13.5", "license": "MIT", "main": "dist/index.js", "typings": "dist/index.d.ts", From d4864ac6e9438b8a172e71f3a4f26a0876cd3b26 Mon Sep 17 00:00:00 2001 From: Amelia Wattenberger Date: Fri, 25 Mar 2022 15:26:29 -0700 Subject: [PATCH 11/15] feat: add number of columns fixes #26 --- src/components/grid.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/grid.tsx b/src/components/grid.tsx index 133bb43..d2fcbb9 100644 --- a/src/components/grid.tsx +++ b/src/components/grid.tsx @@ -527,7 +527,7 @@ export function Grid(props: GridProps) {
Showing {filteredData.length.toLocaleString()} {isFiltered && ` of ${data.length.toLocaleString()}`} row - {(isFiltered ? filteredData : data).length === 1 ? '' : 's'} + {data.length === 1 ? '' : 's'} × {columnNames.length.toLocaleString()} column{columnNames.length === 1 ? '' : 's'}
From 97f767948fbde49cbd9bf3b68a8fe9ee6ba247f8 Mon Sep 17 00:00:00 2001 From: Amelia Wattenberger Date: Fri, 25 Mar 2022 15:27:28 -0700 Subject: [PATCH 12/15] v0.14.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ef174e..5f86648 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.13.5", + "version": "0.14.0", "license": "MIT", "main": "dist/index.js", "typings": "dist/index.d.ts", From 6dff1ef76b1fe52c8392fbac42194ce9a66b4cbb Mon Sep 17 00:00:00 2001 From: Amelia Wattenberger Date: Thu, 5 May 2022 13:36:40 -0700 Subject: [PATCH 13/15] fix: handle getCellIndicies when child is undefined --- src/components/sticky-grid.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/sticky-grid.tsx b/src/components/sticky-grid.tsx index 06d7d46..dfcce35 100644 --- a/src/components/sticky-grid.tsx +++ b/src/components/sticky-grid.tsx @@ -6,7 +6,10 @@ import tw from 'twin.macro'; import { FilterValue } from '../types'; function getCellIndicies(child) { - return { row: child.props.rowIndex, column: child.props.columnIndex }; + return { + row: child?.props.rowIndex || 0, + column: child?.props.columnIndex || 0 + }; } function getShownIndicies(children) { From 261d676352230fe2ed20e670cb22d30a3480aaf1 Mon Sep 17 00:00:00 2001 From: Amelia Wattenberger Date: Thu, 5 May 2022 13:40:42 -0700 Subject: [PATCH 14/15] v0.14.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5f86648..e05b812 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.14.0", + "version": "0.14.1", "license": "MIT", "main": "dist/index.js", "typings": "dist/index.d.ts", From cfd8b6ef8817c511f2e794518eedfb4ed32bda89 Mon Sep 17 00:00:00 2001 From: Viktor Persson Date: Mon, 6 Jun 2022 20:23:05 +0200 Subject: [PATCH 15/15] Update readme; fix docs on defaultSort. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f12af83..f967a86 100644 --- a/README.md +++ b/README.md @@ -88,9 +88,9 @@ The user can interact with the table and update the filters, but the table will ### defaultSort -`string` +`array` -The name of the column you want the table to initialize sorting by. The user can interact with the table and update the sort, but the table will use the default sort when `defaultSort` or `data` changes. +The name of the column and the order you want the table to initialize sorting by (e.g. `["Location", "desc"]`). The user can interact with the table and update the sort, but the table will use the default sort when `defaultSort` or `data` changes. ### defaultStickyColumnName