Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-csv-importer

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-csv-importer - npm Package Compare versions

Comparing version 0.3.0 to 0.4.0

1

dist/index.d.ts
// Generated by dts-bundle-generator v5.4.0
/// <reference types="mocha" />
/// <reference types="papaparse" />

@@ -4,0 +5,0 @@ /// <reference types="prop-types" />

166

dist/index.js

@@ -220,2 +220,5 @@ module.exports =

// CONCATENATED MODULE: ./src/components/ImporterProps.ts
// EXTERNAL MODULE: external "react"

@@ -573,2 +576,46 @@ var external_react_ = __webpack_require__(0);

// CONCATENATED MODULE: ./src/components/ColumnPreview.tsx
// spreadsheet-style column code computation (A, B, ..., Z, AA, AB, ..., etc)
function generateColumnCode(value) {
// ignore dummy index
if (value < 0) {
return '';
}
// first, determine how many base-26 letters there should be
// (because the notation is not purely positional)
let digitCount = 1;
let base = 0;
let next = 26;
while (next <= value) {
digitCount += 1;
base = next;
next = next * 26 + 26;
}
// then, apply normal positional digit computation on remainder above base
let remainder = value - base;
const digits = [];
while (digits.length < digitCount) {
const lastDigit = remainder % 26;
remainder = Math.floor((remainder - lastDigit) / 26); // applying floor just in case
// store ASCII code, with A as 0
digits.unshift(65 + lastDigit);
}
return String.fromCharCode.apply(null, digits);
}
// prepare spreadsheet-like column display information for given raw data preview
function generatePreviewColumns(preview) {
const columnStubs = [...new Array(preview.firstRows[0].length)];
return columnStubs.map((empty, index) => {
const values = preview.firstRows.map((row) => row[index] || '');
const dataValues = [...values];
const headerValue = preview.hasHeaders ? values.shift() : undefined;
return {
index,
code: generateColumnCode(index),
header: headerValue,
values: dataValues
};
});
}
// EXTERNAL MODULE: external "react-use-gesture"

@@ -580,3 +627,3 @@ var external_react_use_gesture_ = __webpack_require__(4);

function useColumnDragState(fields, onTouched) {
function useColumnDragState(fields, initialAssignments, onTouched) {
// wrap in ref to avoid re-triggering

@@ -586,3 +633,3 @@ const onTouchedRef = Object(external_react_["useRef"])(onTouched);

const [dragState, setDragState] = Object(external_react_["useState"])(null);
const [fieldAssignments, setFieldAssignments] = Object(external_react_["useState"])({});
const [fieldAssignments, setFieldAssignments] = Object(external_react_["useState"])(initialAssignments);
// make sure there are no extra fields

@@ -720,3 +767,3 @@ Object(external_react_["useEffect"])(() => {

// @todo sort out "grabbing" cursor state (does not work with pointer-events:none)
const ColumnDragCard_ColumnDragCard = ({ hasHeaders, column: optionalColumn, rowCount = PREVIEW_ROW_COUNT, hasError, isShadow, isDraggable, isDragged, isDropIndicator }) => {
const ColumnDragCard_ColumnDragCard = ({ column: optionalColumn, rowCount = PREVIEW_ROW_COUNT, hasError, isAssigned, isShadow, isDraggable, isDragged, isDropIndicator }) => {
const isDummy = !optionalColumn;

@@ -728,4 +775,4 @@ const column = Object(external_react_["useMemo"])(() => optionalColumn || {

}, [optionalColumn]);
const headerValue = hasHeaders ? column.values[0] : undefined;
const dataValues = column.values.slice(hasHeaders ? 1 : 0, rowCount);
const headerValue = column.header;
const dataValues = column.values.slice(headerValue === undefined ? 0 : 1, rowCount);
return (

@@ -738,3 +785,3 @@ // not changing variant dynamically because it causes a height jump

column.code)),
isDummy ? '\u00a0' : external_react_default.a.createElement("b", { "aria-hidden": true }, column.code)),
isDummy || isAssigned ? '\u00a0' : external_react_default.a.createElement("b", { "aria-hidden": true }, column.code)),
headerValue !== undefined ? (external_react_default.a.createElement("div", { className: "CSVImporter_ColumnDragCard__cardValue", "data-header": true }, headerValue || '\u00a0')) : null,

@@ -752,3 +799,3 @@ external_react_default.a.createElement("div", { role: "text" }, dataValues.map((value, valueIndex) => (external_react_default.a.createElement("div", { key: valueIndex, className: "CSVImporter_ColumnDragCard__cardValue" }, value || '\u00a0'))))));

const ColumnDragObject_ColumnDragObject = ({ hasHeaders, dragState }) => {
const ColumnDragObject_ColumnDragObject = ({ dragState }) => {
const referenceBoxRef = Object(external_react_["useRef"])(null);

@@ -760,3 +807,3 @@ // @todo wrap in a no-events overlay to clip against screen edges

external_react_default.a.createElement("div", { className: "CSVImporter_ColumnDragObject__holder" },
external_react_default.a.createElement(ColumnDragCard_ColumnDragCard, { hasHeaders: hasHeaders, column: dragState.column, isDragged: true }))), document.body)
external_react_default.a.createElement(ColumnDragCard_ColumnDragCard, { column: dragState.column, isDragged: true }))), document.body)
: null;

@@ -809,3 +856,3 @@ // set up initial position

// @todo readable status text if not mouse-drag
const SourceBox = ({ hasHeaders, column, fieldAssignments, dragState, eventBinder, onSelect, onUnassign }) => {
const SourceBox = ({ column, fieldAssignments, dragState, eventBinder, onSelect, onUnassign }) => {
const isDragged = dragState ? column === dragState.column : false;

@@ -819,3 +866,3 @@ const isAssigned = Object(external_react_["useMemo"])(() => Object.keys(fieldAssignments).some((fieldName) => fieldAssignments[fieldName] === column.index), [fieldAssignments, column]);

external_react_default.a.createElement("div", Object.assign({}, (isAssigned ? {} : eventHandlers)),
external_react_default.a.createElement(ColumnDragCard_ColumnDragCard, { hasHeaders: hasHeaders, column: column, isShadow: isDragged || isAssigned, isDraggable: !dragState && !isDragged && !isAssigned })),
external_react_default.a.createElement(ColumnDragCard_ColumnDragCard, { column: column, isAssigned: isAssigned, isShadow: isDragged || isAssigned, isDraggable: !dragState && !isDragged && !isAssigned })),
external_react_default.a.createElement("div", { className: "CSVImporter_ColumnDragSourceArea__boxAction" }, isAssigned ? (external_react_default.a.createElement(IconButton_IconButton, { key: "clear" // key-prop helps clear focus on click

@@ -832,3 +879,3 @@ , label: "Clear column assignment", small: true, type: "replay", onClick: () => {

// @todo current page indicator (dots)
const ColumnDragSourceArea_ColumnDragSourceArea = ({ hasHeaders, columns, fieldAssignments, dragState, eventBinder, onSelect, onUnassign }) => {
const ColumnDragSourceArea_ColumnDragSourceArea = ({ columns, fieldAssignments, dragState, eventBinder, onSelect, onUnassign }) => {
const [page, setPage] = Object(external_react_["useState"])(0);

@@ -840,3 +887,3 @@ const [pageChanged, setPageChanged] = Object(external_react_["useState"])(false);

.slice(start, start + SOURCES_PAGE_SIZE)
.map((column, columnIndex) => (external_react_default.a.createElement(SourceBox, { key: columnIndex, hasHeaders: hasHeaders, column: column, fieldAssignments: fieldAssignments, dragState: dragState, eventBinder: eventBinder, onSelect: onSelect, onUnassign: onUnassign })));
.map((column, columnIndex) => (external_react_default.a.createElement(SourceBox, { key: columnIndex, column: column, fieldAssignments: fieldAssignments, dragState: dragState, eventBinder: eventBinder, onSelect: onSelect, onUnassign: onUnassign })));
while (pageContents.length < SOURCES_PAGE_SIZE) {

@@ -877,3 +924,3 @@ pageContents.push(external_react_default.a.createElement("div", { key: pageContents.length, className: "CSVImporter_ColumnDragSourceArea__pageFiller" }));

const TargetBox = ({ hasHeaders, field, touched, assignedColumn, dragState, eventBinder, onHover, onAssign, onUnassign }) => {
const TargetBox = ({ field, touched, assignedColumn, dragState, eventBinder, onHover, onAssign, onUnassign }) => {
const mouseHoverHandlers = dragState && dragState.pointerStartInfo

@@ -895,10 +942,10 @@ ? {

if (sourceColumn) {
return (external_react_default.a.createElement(ColumnDragCard_ColumnDragCard, { hasHeaders: hasHeaders, rowCount: 3, column: sourceColumn, isDropIndicator: true }));
return (external_react_default.a.createElement(ColumnDragCard_ColumnDragCard, { rowCount: 3, column: sourceColumn, isDropIndicator: true }));
}
if (assignedColumn) {
return (external_react_default.a.createElement(ColumnDragCard_ColumnDragCard, { hasHeaders: hasHeaders, rowCount: 3, column: assignedColumn, isShadow: isReDragged, isDraggable: !isReDragged }));
return (external_react_default.a.createElement(ColumnDragCard_ColumnDragCard, { rowCount: 3, column: assignedColumn, isShadow: isReDragged, isDraggable: !isReDragged }));
}
const hasError = touched && !field.isOptional;
return (external_react_default.a.createElement(ColumnDragCard_ColumnDragCard, { hasHeaders: hasHeaders, rowCount: 3, hasError: hasError }));
}, [field, hasHeaders, touched, assignedColumn, sourceColumn, isReDragged]);
return external_react_default.a.createElement(ColumnDragCard_ColumnDragCard, { rowCount: 3, hasError: hasError });
}, [field, touched, assignedColumn, sourceColumn, isReDragged]);
// @todo mouse cursor changes to reflect draggable state

@@ -917,6 +964,6 @@ return (external_react_default.a.createElement("section", Object.assign({ className: "CSVImporter_ColumnDragTargetArea__box", "aria-label": `${field.label} (${field.isOptional ? 'optional' : 'required'})` }, mouseHoverHandlers),

};
const ColumnDragTargetArea_ColumnDragTargetArea = ({ fields, columns, hasHeaders, fieldTouched, fieldAssignments, dragState, eventBinder, onHover, onAssign, onUnassign }) => {
const ColumnDragTargetArea_ColumnDragTargetArea = ({ fields, columns, fieldTouched, fieldAssignments, dragState, eventBinder, onHover, onAssign, onUnassign }) => {
return (external_react_default.a.createElement("section", { className: "CSVImporter_ColumnDragTargetArea", "aria-label": "Target fields" }, fields.map((field) => {
const assignedColumnIndex = fieldAssignments[field.name];
return (external_react_default.a.createElement(TargetBox, { key: field.name, hasHeaders: hasHeaders, field: field, touched: fieldTouched[field.name], assignedColumn: assignedColumnIndex !== undefined
return (external_react_default.a.createElement(TargetBox, { key: field.name, field: field, touched: fieldTouched[field.name], assignedColumn: assignedColumnIndex !== undefined
? columns[assignedColumnIndex]

@@ -934,43 +981,45 @@ : null, dragState: dragState, eventBinder: eventBinder, onHover: onHover, onAssign: onAssign, onUnassign: onUnassign }));

// spreadsheet-style column code computation (A, B, ..., Z, AA, AB, ..., etc)
function generateColumnCode(value) {
// ignore dummy index
if (value < 0) {
return '';
}
// first, determine how many base-26 letters there should be
// (because the notation is not purely positional)
let digitCount = 1;
let base = 0;
let next = 26;
while (next <= value) {
digitCount += 1;
base = next;
next = next * 26 + 26;
}
// then, apply normal positional digit computation on remainder above base
let remainder = value - base;
const digits = [];
while (digits.length < digitCount) {
const lastDigit = remainder % 26;
remainder = Math.floor((remainder - lastDigit) / 26); // applying floor just in case
// store ASCII code, with A as 0
digits.unshift(65 + lastDigit);
}
return String.fromCharCode.apply(null, digits);
}
const ColumnPicker = ({ fields, preview, onAccept, onCancel }) => {
const columns = Object(external_react_["useMemo"])(() => {
return [...new Array(preview.firstRows[0].length)].map((empty, index) => {
return {
index,
code: generateColumnCode(index),
values: preview.firstRows.map((row) => row[index] || '')
};
const columns = Object(external_react_["useMemo"])(() => generatePreviewColumns(preview), [
preview
]);
const initialAssignments = Object(external_react_["useMemo"])(() => {
// prep insensitive/fuzzy match stems for known columns
const columnStems = columns.map((column) => {
const trimmed = column.header && column.header.trim();
if (!trimmed) {
return undefined;
}
return trimmed.toLowerCase();
});
}, [preview]);
// pre-assign corresponding fields
const result = {};
const assignedColumnIndexes = [];
fields.forEach((field) => {
// find by field stem
const fieldLabelStem = field.label.trim().toLowerCase(); // @todo consider normalizing other whitespace/non-letters
const matchingColumnIndex = columnStems.findIndex((columnStem, columnIndex) => {
// no headers or no meaningful stem value
if (columnStem === undefined) {
return false;
}
// always check against assigning twice
if (assignedColumnIndexes[columnIndex]) {
return false;
}
return columnStem === fieldLabelStem;
});
// assign if found
if (matchingColumnIndex !== -1) {
assignedColumnIndexes[matchingColumnIndex] = true;
result[field.name] = matchingColumnIndex;
}
});
return result;
}, [fields, columns]);
// track which fields need to show validation warning
const [fieldTouched, setFieldTouched] = Object(external_react_["useState"])({});
const [validationError, setValidationError] = Object(external_react_["useState"])(null);
const { fieldAssignments, dragState, dragEventBinder, dragHoverHandler, columnSelectHandler, assignHandler, unassignHandler } = useColumnDragState(fields, (fieldName) => {
const { fieldAssignments, dragState, dragEventBinder, dragHoverHandler, columnSelectHandler, assignHandler, unassignHandler } = useColumnDragState(fields, initialAssignments, (fieldName) => {
setFieldTouched((prev) => {

@@ -1001,5 +1050,5 @@ if (prev[fieldName]) {

} },
external_react_default.a.createElement(ColumnDragSourceArea_ColumnDragSourceArea, { hasHeaders: preview.hasHeaders, columns: columns, fieldAssignments: fieldAssignments, dragState: dragState, eventBinder: dragEventBinder, onSelect: columnSelectHandler, onUnassign: unassignHandler }),
external_react_default.a.createElement(ColumnDragTargetArea_ColumnDragTargetArea, { fields: fields, columns: columns, hasHeaders: preview.hasHeaders, fieldTouched: fieldTouched, fieldAssignments: fieldAssignments, dragState: dragState, eventBinder: dragEventBinder, onHover: dragHoverHandler, onAssign: assignHandler, onUnassign: unassignHandler }),
external_react_default.a.createElement(ColumnDragObject_ColumnDragObject, { hasHeaders: preview.hasHeaders, dragState: dragState })));
external_react_default.a.createElement(ColumnDragSourceArea_ColumnDragSourceArea, { columns: columns, fieldAssignments: fieldAssignments, dragState: dragState, eventBinder: dragEventBinder, onSelect: columnSelectHandler, onUnassign: unassignHandler }),
external_react_default.a.createElement(ColumnDragTargetArea_ColumnDragTargetArea, { fields: fields, columns: columns, fieldTouched: fieldTouched, fieldAssignments: fieldAssignments, dragState: dragState, eventBinder: dragEventBinder, onHover: dragHoverHandler, onAssign: assignHandler, onUnassign: unassignHandler }),
external_react_default.a.createElement(ColumnDragObject_ColumnDragObject, { dragState: dragState })));
};

@@ -1229,4 +1278,5 @@

/***/ })
/******/ ]);
//# sourceMappingURL=index.js.map
{
"name": "react-csv-importer",
"version": "0.3.0",
"version": "0.4.0",
"description": "React CSV import widget with user-customizable mapping",

@@ -30,2 +30,3 @@ "keywords": [

"stylelint-fix": "stylelint \"src/**/*.scss\" --fix",
"test": "cross-env TS_NODE_COMPILER_OPTIONS={\\\"module\\\":\\\"commonjs\\\"} mocha --require ts-node/register --timeout 10000 test/**/*.test.ts",
"storybook": "start-storybook -p 6006",

@@ -39,3 +40,4 @@ "build-storybook": "build-storybook",

"src/**/*.{ts,tsx}": "eslint --max-warnings=0",
"src/**/*.scss": "stylelint"
"src/**/*.scss": "stylelint",
"test/**/*.{js,ts}": "eslint --max-warnings=0"
},

@@ -49,9 +51,16 @@ "devDependencies": {

"@storybook/react": "^6.0.21",
"@types/chai": "^4.2.14",
"@types/mocha": "^8.2.0",
"@types/papaparse": "^5.2.2",
"@types/react": "^16.9.49",
"@types/react-dom": "^16.9.8",
"@types/selenium-webdriver": "^4.0.11",
"@types/webpack-dev-server": "^3.11.1",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",
"babel-loader": "^8.1.0",
"chai": "^4.2.0",
"chromedriver": "^87.0.7",
"clean-webpack-plugin": "^3.0.0",
"cross-env": "^7.0.3",
"css-loader": "^4.3.0",

@@ -65,6 +74,10 @@ "dotenv-webpack": "^2.0.0",

"eslint-plugin-react-hooks": "^4.1.0",
"expose-loader": "^1.0.3",
"file-loader": "^6.1.0",
"lint-staged": "^10.3.0",
"mini-css-extract-plugin": "^0.11.1",
"mocha": "^8.2.1",
"prettier": "^2.1.1",
"react": "^16.8.3",
"react-dom": "^16.8.3",
"react-is": "^16.13.1",

@@ -74,2 +87,3 @@ "rimraf": "^3.0.2",

"sass-loader": "^10.0.2",
"selenium-webdriver": "^4.0.0-alpha.8",
"style-loader": "^1.2.1",

@@ -81,5 +95,7 @@ "stylelint": "^13.7.0",

"ts-loader": "^8.0.3",
"ts-node": "^9.1.1",
"typescript": "^4.0.2",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.7.0"
},

@@ -86,0 +102,0 @@ "peerDependencies": {

# React CSV Importer
![https://www.npmjs.com/package/react-csv-importer](https://img.shields.io/npm/v/react-csv-importer) ![https://github.com/beamworks/react-csv-importer/actions](https://github.com/beamworks/react-csv-importer/actions/workflows/test.yml/badge.svg)
This library combines an uploader + CSV parser + raw file preview + UI for custom user column

@@ -21,2 +23,3 @@ mapping, all in one. Relies on the popular PapaParse CSV library to preview and process file contents directly in the browser.

- raw file preview
- auto-map fields to matching column names
- user-selectable column mapping (drag-drop UI)

@@ -82,2 +85,4 @@ - optional fields

In the above example, if the user uploads a CSV file with column headers "Name", "Email" and so on, the columns will be automatically matched to fields with same labels. If any of the headers do not match, the user will have an opportunity to manually remap columns to the defined fields.
## Dependencies

@@ -89,4 +94,30 @@

## Local Development
Perform local `git clone`, etc. Then ensure modules are installed:
```sh
yarn # root folder only needs this for Husky pre-commit triggers
cd package-core
yarn # main package dev dependencies
```
Most of the interesting stuff is inside `package-core` folder.
To start Storybook to have a hot-reloaded local sandbox:
```sh
yarn storybook
```
To run the end-to-end test suite:
```sh
yarn test
```
## Changes
- 0.4.0
- auto-assign column headers
- 0.3.0

@@ -93,0 +124,0 @@ - allow passing PapaParse config options

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc