@reykjavik/hanna-react 0.10.62 → 0.10.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/FileInput/FileInput.utils.d.ts +36 -0
- package/FileInput/FileInput.utils.js +69 -0
- package/FileInput.d.ts +2 -2
- package/FileInput.js +9 -48
- package/TagPill.js +3 -2
- package/_abstract/_AbstractCarousel.js +4 -4
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
- ... <!-- Add new lines here. -->
|
|
6
6
|
|
|
7
|
+
## 0.10.63
|
|
8
|
+
|
|
9
|
+
_2022-08-29_
|
|
10
|
+
|
|
11
|
+
- fix: `FileInput` in single-file mode doesn't report deleted files on add
|
|
12
|
+
- fix: Hide `Carousel` mouse-cursor scroll controls at start/end positions
|
|
13
|
+
- fix: Pass `id` and other HTML props to static (span) `TagPill`s
|
|
14
|
+
|
|
7
15
|
## 0.10.62
|
|
8
16
|
|
|
9
17
|
_2022-08-23_
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare type CustomFile = {
|
|
2
|
+
preview?: string;
|
|
3
|
+
} & File;
|
|
4
|
+
/**
|
|
5
|
+
* Attaches a `preview` prop to file objects that don't already have a `preview` key defined
|
|
6
|
+
*
|
|
7
|
+
* The preview's value is either a data URI (for image-type files) or `undefined`
|
|
8
|
+
*/
|
|
9
|
+
export declare const addPreview: (file: CustomFile) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Revokes `preview` data URIs to avoid memory leaks
|
|
12
|
+
*
|
|
13
|
+
* (See: https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL)
|
|
14
|
+
*/
|
|
15
|
+
export declare const releasePreview: (file: CustomFile) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Small+stupid file size pretty-printer.
|
|
18
|
+
*/
|
|
19
|
+
export declare const formatBytes: (bytes: number, decimals?: number) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Figures out how to handle adding files to a FileInput
|
|
22
|
+
* Which files to retaine, which too delete, and
|
|
23
|
+
* what the updated fileList should look like.
|
|
24
|
+
*
|
|
25
|
+
*
|
|
26
|
+
*/
|
|
27
|
+
export declare const getFileListUpdate: (oldFileList: Array<File>, added: Array<File>, replaceMode: boolean) => {
|
|
28
|
+
fileList: File[];
|
|
29
|
+
diff: {
|
|
30
|
+
added: File[];
|
|
31
|
+
deleted: File[];
|
|
32
|
+
} | {
|
|
33
|
+
added: File[];
|
|
34
|
+
deleted?: undefined;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getFileListUpdate = exports.formatBytes = exports.releasePreview = exports.addPreview = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Attaches a `preview` prop to file objects that don't already have a `preview` key defined
|
|
6
|
+
*
|
|
7
|
+
* The preview's value is either a data URI (for image-type files) or `undefined`
|
|
8
|
+
*/
|
|
9
|
+
const addPreview = (file) => {
|
|
10
|
+
if (!('preview' in file)) {
|
|
11
|
+
file.preview = file.type.includes('image/') ? URL.createObjectURL(file) : undefined;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
exports.addPreview = addPreview;
|
|
15
|
+
/**
|
|
16
|
+
* Revokes `preview` data URIs to avoid memory leaks
|
|
17
|
+
*
|
|
18
|
+
* (See: https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL)
|
|
19
|
+
*/
|
|
20
|
+
const releasePreview = (file) => {
|
|
21
|
+
file.preview && URL.revokeObjectURL(file.preview);
|
|
22
|
+
delete file.preview;
|
|
23
|
+
};
|
|
24
|
+
exports.releasePreview = releasePreview;
|
|
25
|
+
/**
|
|
26
|
+
* Small+stupid file size pretty-printer.
|
|
27
|
+
*/
|
|
28
|
+
const formatBytes = (bytes, decimals = 2) => {
|
|
29
|
+
if (bytes === 0) {
|
|
30
|
+
return '0 Bytes';
|
|
31
|
+
}
|
|
32
|
+
const k = 1024;
|
|
33
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
34
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
35
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
|
|
36
|
+
};
|
|
37
|
+
exports.formatBytes = formatBytes;
|
|
38
|
+
/**
|
|
39
|
+
* Figures out how to handle adding files to a FileInput
|
|
40
|
+
* Which files to retaine, which too delete, and
|
|
41
|
+
* what the updated fileList should look like.
|
|
42
|
+
*
|
|
43
|
+
*
|
|
44
|
+
*/
|
|
45
|
+
const getFileListUpdate = (oldFileList, added,
|
|
46
|
+
/**
|
|
47
|
+
* `replaceMode: true` is the default "single-file" input behavior.
|
|
48
|
+
*
|
|
49
|
+
* Pass `false` to this argument when the "multiple" prop is true.
|
|
50
|
+
*/
|
|
51
|
+
replaceMode) => {
|
|
52
|
+
const deleted = replaceMode ? oldFileList : [];
|
|
53
|
+
const retained = [];
|
|
54
|
+
if (!replaceMode) {
|
|
55
|
+
oldFileList.forEach((oldFile) => {
|
|
56
|
+
if (added.find(({ name }) => name === oldFile.name)) {
|
|
57
|
+
deleted.push(oldFile);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
retained.push(oldFile);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
fileList: retained.concat(added),
|
|
66
|
+
diff: deleted.length ? { added, deleted } : { added },
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
exports.getFileListUpdate = getFileListUpdate;
|
package/FileInput.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { FormFieldWrappingProps } from './FormField';
|
|
2
|
-
declare type
|
|
2
|
+
declare type DropzonePropsProps = {
|
|
3
3
|
accept?: string;
|
|
4
4
|
multiple?: boolean;
|
|
5
5
|
};
|
|
6
6
|
export declare type FileInputProps = {
|
|
7
|
-
dropzoneProps:
|
|
7
|
+
dropzoneProps: DropzonePropsProps;
|
|
8
8
|
dropzoneText: string | JSX.Element;
|
|
9
9
|
showFileSize?: boolean;
|
|
10
10
|
showImagePreviews?: boolean;
|
package/FileInput.js
CHANGED
|
@@ -5,26 +5,8 @@ const react_1 = tslib_1.__importStar(require("react"));
|
|
|
5
5
|
const react_dropzone_1 = require("react-dropzone"); // https://react-dropzone.js.org/#!/Dropzone
|
|
6
6
|
const hooks_1 = require("@hugsmidjan/react/hooks");
|
|
7
7
|
const getBemClass_1 = tslib_1.__importDefault(require("@hugsmidjan/react/utils/getBemClass"));
|
|
8
|
+
const FileInput_utils_1 = require("./FileInput/FileInput.utils");
|
|
8
9
|
const FormField_1 = tslib_1.__importDefault(require("./FormField"));
|
|
9
|
-
/**
|
|
10
|
-
* Attaches a `preview` prop to file objects that don't already have a `preview` key defined
|
|
11
|
-
*
|
|
12
|
-
* The preview's value is either a data URI (for image-type files) or `undefined`
|
|
13
|
-
*/
|
|
14
|
-
const addPreview = (file) => {
|
|
15
|
-
if (!('preview' in file)) {
|
|
16
|
-
file.preview = file.type.includes('image/') ? URL.createObjectURL(file) : undefined;
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* Revokes `preview` data URIs to avoid memory leaks
|
|
21
|
-
*
|
|
22
|
-
* (See: https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL)
|
|
23
|
-
*/
|
|
24
|
-
const releasePreview = (file) => {
|
|
25
|
-
file.preview && URL.revokeObjectURL(file.preview);
|
|
26
|
-
delete file.preview;
|
|
27
|
-
};
|
|
28
10
|
const arrayToFileList = (arr) => {
|
|
29
11
|
const fileList = new DataTransfer();
|
|
30
12
|
arr.forEach((item) => {
|
|
@@ -32,15 +14,6 @@ const arrayToFileList = (arr) => {
|
|
|
32
14
|
});
|
|
33
15
|
return fileList.files;
|
|
34
16
|
};
|
|
35
|
-
const formatBytes = (bytes, decimals = 2) => {
|
|
36
|
-
if (bytes === 0) {
|
|
37
|
-
return '0 Bytes';
|
|
38
|
-
}
|
|
39
|
-
const k = 1024;
|
|
40
|
-
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
41
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
42
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
|
|
43
|
-
};
|
|
44
17
|
const FileInput = (props) => {
|
|
45
18
|
const { className, id, label, hideLabel, dropzoneProps = { multiple: true }, dropzoneText, removeFileText, assistText, disabled, invalid, errorMessage, required, reqText, onFilesUpdated = () => undefined, showFileSize, showImagePreviews, value = [] } = props, inputElementProps = tslib_1.__rest(props, ["className", "id", "label", "hideLabel", "dropzoneProps", "dropzoneText", "removeFileText", "assistText", "disabled", "invalid", "errorMessage", "required", "reqText", "onFilesUpdated", "showFileSize", "showImagePreviews", "value"]);
|
|
46
19
|
const domid = (0, hooks_1.useDomid)(id);
|
|
@@ -50,7 +23,7 @@ const FileInput = (props) => {
|
|
|
50
23
|
const [isHover, setIsHover] = (0, react_1.useState)(false);
|
|
51
24
|
const { getRootProps, getInputProps, isDragReject, inputRef } = (0, react_dropzone_1.useDropzone)(Object.assign({ onDrop: (acceptedFiles) => {
|
|
52
25
|
acceptedFiles = acceptedFiles.map((file) => {
|
|
53
|
-
addPreview(file);
|
|
26
|
+
(0, FileInput_utils_1.addPreview)(file);
|
|
54
27
|
return file;
|
|
55
28
|
});
|
|
56
29
|
addFiles(acceptedFiles); // eslint-disable-line
|
|
@@ -76,10 +49,10 @@ const FileInput = (props) => {
|
|
|
76
49
|
} }, dropzoneProps));
|
|
77
50
|
// Add previews on incoming files
|
|
78
51
|
// (NOTE: `addPreview` ignores files that already have preview.)
|
|
79
|
-
files.forEach(addPreview);
|
|
52
|
+
files.forEach(FileInput_utils_1.addPreview);
|
|
80
53
|
(0, react_1.useEffect)(() => () => {
|
|
81
54
|
// Make sure to revoke the data uris on unmount to avoid memory leaks
|
|
82
|
-
files.forEach(releasePreview);
|
|
55
|
+
files.forEach(FileInput_utils_1.releasePreview);
|
|
83
56
|
}, [files]);
|
|
84
57
|
const removeFile = (name) => {
|
|
85
58
|
if (fileInput.current) {
|
|
@@ -89,7 +62,7 @@ const FileInput = (props) => {
|
|
|
89
62
|
return true;
|
|
90
63
|
}
|
|
91
64
|
deleted.push(file);
|
|
92
|
-
releasePreview(file);
|
|
65
|
+
(0, FileInput_utils_1.releasePreview)(file);
|
|
93
66
|
return false;
|
|
94
67
|
});
|
|
95
68
|
fileInput.current.files = arrayToFileList(newFileList);
|
|
@@ -98,21 +71,9 @@ const FileInput = (props) => {
|
|
|
98
71
|
};
|
|
99
72
|
const addFiles = (added) => {
|
|
100
73
|
if (fileInput.current) {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
oldFiles.forEach((oldFile) => {
|
|
105
|
-
if (added.find(({ name }) => name === oldFile.name)) {
|
|
106
|
-
deleted.push(oldFile);
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
retained.push(oldFile);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
const newFileList = retained.concat(added);
|
|
113
|
-
fileInput.current.files = arrayToFileList(newFileList);
|
|
114
|
-
const diff = deleted.length ? { added, deleted } : { added };
|
|
115
|
-
onFilesUpdated(newFileList, diff);
|
|
74
|
+
const { fileList, diff } = (0, FileInput_utils_1.getFileListUpdate)(files, added, !dropzoneProps.multiple);
|
|
75
|
+
fileInput.current.files = arrayToFileList(fileList);
|
|
76
|
+
onFilesUpdated(fileList, diff);
|
|
116
77
|
}
|
|
117
78
|
if (inputRef.current) {
|
|
118
79
|
// Empty on every add
|
|
@@ -129,7 +90,7 @@ const FileInput = (props) => {
|
|
|
129
90
|
react_1.default.createElement("span", { className: "FileInput__filename" }, file.name),
|
|
130
91
|
showFileSize && (react_1.default.createElement("small", { className: "FileInput__filesize" },
|
|
131
92
|
" - (",
|
|
132
|
-
formatBytes(file.size),
|
|
93
|
+
(0, FileInput_utils_1.formatBytes)(file.size),
|
|
133
94
|
")"))))));
|
|
134
95
|
return (react_1.default.createElement(FormField_1.default, { className: (0, getBemClass_1.default)('FileInput', [dropzoneProps.multiple && 'multi'], className), label: label, id: domid + '-fake', LabelTag: "h4", assistText: assistText, hideLabel: hideLabel, disabled: disabled, invalid: invalid, errorMessage: errorMessage, required: required, reqText: reqText, renderInput: (className, inputProps /* , addFocusProps */) => {
|
|
135
96
|
return (react_1.default.createElement("div", { className: className.control, ref: fileInputWrapper },
|
package/TagPill.js
CHANGED
|
@@ -18,10 +18,11 @@ const TagPill = (props) => {
|
|
|
18
18
|
removable &&
|
|
19
19
|
isStatic &&
|
|
20
20
|
!onRemove &&
|
|
21
|
-
console.warn('static (non-button) `TagPill`s
|
|
21
|
+
console.warn('Removable static (non-button) `TagPill`s ' +
|
|
22
|
+
'must have an `onRemove` handler defined');
|
|
22
23
|
const modifiers = [modifier, large && 'large', colors[color]];
|
|
23
24
|
const removeBtn = removable && (react_1.default.createElement("button", { className: "TagPill__remove", onClick: onRemove && (() => onRemove()), "aria-label": removeLabelLong, type: "button" }, removeLabel));
|
|
24
|
-
return isStatic ? (react_1.default.createElement("span", { className: (0, getBemClass_1.default)('TagPill', modifiers) },
|
|
25
|
+
return isStatic ? (react_1.default.createElement("span", Object.assign({ className: (0, getBemClass_1.default)('TagPill', modifiers) }, buttonProps),
|
|
25
26
|
label,
|
|
26
27
|
" ",
|
|
27
28
|
removeBtn)) : onRemove ? (react_1.default.createElement("span", { className: (0, getBemClass_1.default)('TagPill', modifiers) },
|
|
@@ -95,14 +95,14 @@ const AbstractCarousel = (props) => {
|
|
|
95
95
|
title && react_1.default.createElement("h2", { className: bem + '__title' }, title),
|
|
96
96
|
isBrowser ? (react_1.default.createElement("div", { className: bem + '__itemlist-wrapper' },
|
|
97
97
|
itemList,
|
|
98
|
-
react_1.default.createElement("div", { className: bem + '__itemlist-goLeft', onClick: () => {
|
|
98
|
+
activeItem > 0 && (react_1.default.createElement("div", { className: bem + '__itemlist-goLeft', onClick: () => {
|
|
99
99
|
delayedScrollLeft.cancel();
|
|
100
100
|
scrollToItem(activeItem - 1);
|
|
101
|
-
}, onMouseOver: () => delayedScrollLeft(activeItem), onMouseOut: () => delayedScrollLeft.cancel() }),
|
|
102
|
-
react_1.default.createElement("div", { className: bem + '__itemlist-goRight', onClick: () => {
|
|
101
|
+
}, onMouseOver: () => delayedScrollLeft(activeItem), onMouseOut: () => delayedScrollLeft.cancel() })),
|
|
102
|
+
activeItem < itemCount - 1 && (react_1.default.createElement("div", { className: bem + '__itemlist-goRight', onClick: () => {
|
|
103
103
|
delayedScrollRight.cancel();
|
|
104
104
|
scrollToItem(activeItem + 1);
|
|
105
|
-
}, onMouseOver: () => delayedScrollRight(activeItem), onMouseOut: () => delayedScrollRight.cancel() }))) : (itemList),
|
|
105
|
+
}, onMouseOver: () => delayedScrollRight(activeItem), onMouseOut: () => delayedScrollRight.cancel() })))) : (itemList),
|
|
106
106
|
isBrowser && (react_1.default.createElement(CarouselStepper_1.default, { itemCount: itemCount, setCurrent: scrollToItem, current: activeItem }))));
|
|
107
107
|
};
|
|
108
108
|
exports.default = AbstractCarousel;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reykjavik/hanna-react",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.63",
|
|
4
4
|
"author": "Reykjavík (http://www.reykjavik.is)",
|
|
5
5
|
"contributors": [
|
|
6
6
|
"Hugsmiðjan ehf (http://www.hugsmidjan.is)",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@hugsmidjan/qj": "^4.10.2",
|
|
17
17
|
"@hugsmidjan/react": "^0.4.17",
|
|
18
|
-
"@reykjavik/hanna-css": "^0.3.
|
|
19
|
-
"@reykjavik/hanna-utils": "^0.1.
|
|
18
|
+
"@reykjavik/hanna-css": "^0.3.7",
|
|
19
|
+
"@reykjavik/hanna-utils": "^0.1.11",
|
|
20
20
|
"@types/react": "^17.0.24",
|
|
21
21
|
"@types/react-autosuggest": "^10.1.0",
|
|
22
22
|
"@types/react-datepicker": "^3.0.2",
|