@reykjavik/hanna-react 0.10.63 → 0.10.64

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 CHANGED
@@ -4,11 +4,16 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
- ## 0.10.63
7
+ ## 0.10.63 – 0.10.64
8
8
 
9
9
  _2022-08-29_
10
10
 
11
- - fix: `FileInput` in single-file mode doesn't report deleted files on add
11
+ - feat: Changes to `FileInput`
12
+ - feat: Add prop `FileList` to suppress (`false`) or customize its rendering
13
+ - feat: Add props `multiple`, `accept`
14
+ - feat: Deprecate prop `dropzoneProps`
15
+ - fix: report deleted when adding files in single-file mode
16
+ - fix: Make `dropZoneProps` optional, as originally indented
12
17
  - fix: Hide `Carousel` mouse-cursor scroll controls at start/end positions
13
18
  - fix: Pass `id` and other HTML props to static (span) `TagPill`s
14
19
 
@@ -0,0 +1,11 @@
1
+ import type { formatBytes } from './_FileInput.utils';
2
+ import { CustomFile } from './_FileInput.utils';
3
+ export declare type FileListProps = {
4
+ files: Array<CustomFile>;
5
+ showFileSize?: boolean;
6
+ showImagePreviews?: boolean;
7
+ removeFileText: string;
8
+ removeFile: (file: File | string) => void;
9
+ formatBytes: typeof formatBytes;
10
+ };
11
+ export declare const DefaultFileList: (props: FileListProps) => JSX.Element | null;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultFileList = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const DefaultFileList = (props) => {
7
+ const { files, showFileSize, showImagePreviews, removeFileText, removeFile, formatBytes, } = props;
8
+ if (!files.length) {
9
+ return null;
10
+ }
11
+ return (react_1.default.createElement("ul", { className: "FileInput__filelist" }, files.map((file) => (react_1.default.createElement("li", { key: file.name, className: "FileInput__file" },
12
+ react_1.default.createElement("button", { className: "FileInput__file-remove", type: "button", onClick: () => removeFile(file), "aria-label": removeFileText }, removeFileText),
13
+ react_1.default.createElement("span", { className: "FileInput__fileinfo" },
14
+ showImagePreviews && file.preview && (react_1.default.createElement(react_1.default.Fragment, null,
15
+ react_1.default.createElement("span", { className: "FileInput__preview" },
16
+ react_1.default.createElement("img", { src: file.preview })),
17
+ ' ')),
18
+ react_1.default.createElement("span", { className: "FileInput__filename" }, file.name),
19
+ showFileSize && (react_1.default.createElement("small", { className: "FileInput__filesize" },
20
+ " - (",
21
+ formatBytes(file.size),
22
+ ")"))))))));
23
+ };
24
+ exports.DefaultFileList = DefaultFileList;
package/FileInput.d.ts CHANGED
@@ -1,14 +1,23 @@
1
+ import { FileListProps } from './FileInput/_FileInputFileList';
1
2
  import { FormFieldWrappingProps } from './FormField';
2
- declare type DropzonePropsProps = {
3
- accept?: string;
4
- multiple?: boolean;
5
- };
6
3
  export declare type FileInputProps = {
7
- dropzoneProps: DropzonePropsProps;
4
+ /**
5
+ * Flags if the input should accept multiple, or just a single file at a time.
6
+ *
7
+ * Default: `true`
8
+ */
9
+ multiple?: boolean;
10
+ /**
11
+ * Accepted file mime type(s).
12
+ *
13
+ * Default: no restrictions.
14
+ */
15
+ accept?: string | Array<string>;
8
16
  dropzoneText: string | JSX.Element;
17
+ removeFileText: string;
9
18
  showFileSize?: boolean;
10
19
  showImagePreviews?: boolean;
11
- removeFileText: string;
20
+ FileList?: false | ((props: FileListProps) => JSX.Element | null);
12
21
  onFilesUpdated?: (
13
22
  /** Updated, full list of Files. */
14
23
  files: Array<File>,
@@ -27,6 +36,13 @@ export declare type FileInputProps = {
27
36
  }) => void;
28
37
  name?: string;
29
38
  value?: ReadonlyArray<File>;
39
+ /**
40
+ * @deprecated Use props `multiple`, `accept` instead (Will be removed in v0.11)
41
+ */
42
+ dropzoneProps?: {
43
+ accept?: string;
44
+ multiple?: boolean;
45
+ };
30
46
  } & FormFieldWrappingProps;
31
47
  declare const FileInput: (props: FileInputProps) => JSX.Element;
32
48
  export default FileInput;
package/FileInput.js CHANGED
@@ -5,7 +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
+ const _FileInput_utils_1 = require("./FileInput/_FileInput.utils");
9
+ const _FileInputFileList_1 = require("./FileInput/_FileInputFileList");
9
10
  const FormField_1 = tslib_1.__importDefault(require("./FormField"));
10
11
  const arrayToFileList = (arr) => {
11
12
  const fileList = new DataTransfer();
@@ -15,54 +16,63 @@ const arrayToFileList = (arr) => {
15
16
  return fileList.files;
16
17
  };
17
18
  const FileInput = (props) => {
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"]);
19
+ const { className, id, label, hideLabel, dropzoneProps = { multiple: true }, multiple = dropzoneProps.multiple, accept, dropzoneText, removeFileText, assistText, disabled, invalid, errorMessage, required, reqText, FileList = _FileInputFileList_1.DefaultFileList, onFilesUpdated = () => undefined, showFileSize, showImagePreviews, value = [] } = props, inputElementProps = tslib_1.__rest(props, ["className", "id", "label", "hideLabel", "dropzoneProps", "multiple", "accept", "dropzoneText", "removeFileText", "assistText", "disabled", "invalid", "errorMessage", "required", "reqText", "FileList", "onFilesUpdated", "showFileSize", "showImagePreviews", "value"]);
19
20
  const domid = (0, hooks_1.useDomid)(id);
20
21
  const fileInputWrapper = (0, react_1.useRef)(null);
21
22
  const fileInput = (0, react_1.useRef)(null);
22
23
  const files = value;
23
24
  const [isHover, setIsHover] = (0, react_1.useState)(false);
24
- const { getRootProps, getInputProps, isDragReject, inputRef } = (0, react_dropzone_1.useDropzone)(Object.assign({ onDrop: (acceptedFiles) => {
25
+ const { getRootProps, getInputProps, isDragReject, inputRef } = (0, react_dropzone_1.useDropzone)({
26
+ onDrop: (acceptedFiles) => {
25
27
  acceptedFiles = acceptedFiles.map((file) => {
26
- (0, FileInput_utils_1.addPreview)(file);
28
+ (0, _FileInput_utils_1.addPreview)(file);
27
29
  return file;
28
30
  });
29
31
  addFiles(acceptedFiles); // eslint-disable-line
30
32
  setIsHover(false);
31
- }, onDropRejected: (rejectedFiles) => {
33
+ },
34
+ onDropRejected: (rejectedFiles) => {
32
35
  window.alert('Error:\n' +
33
36
  rejectedFiles
34
37
  .map((elm) => {
35
38
  return elm.name;
36
39
  })
37
40
  .join(', '));
38
- }, onDragEnter: () => {
41
+ },
42
+ onDragEnter: () => {
39
43
  // 'dragLeave' always fires right after 'dragEnter', use 'dragOver' instead
40
44
  // console.log('enter');
41
45
  // setIsHover(true);
42
- }, onDragLeave: () => {
46
+ },
47
+ onDragLeave: () => {
43
48
  // console.log('leave');
44
49
  setIsHover(false);
45
- }, onDragOver: () => {
50
+ },
51
+ onDragOver: () => {
46
52
  // TODO: add error icon? 'isDragReject' gives unstable results
47
53
  // console.log(isDragReject);
48
54
  setIsHover(true);
49
- } }, dropzoneProps));
55
+ },
56
+ multiple,
57
+ accept,
58
+ });
50
59
  // Add previews on incoming files
51
60
  // (NOTE: `addPreview` ignores files that already have preview.)
52
- files.forEach(FileInput_utils_1.addPreview);
61
+ files.forEach(_FileInput_utils_1.addPreview);
53
62
  (0, react_1.useEffect)(() => () => {
54
63
  // Make sure to revoke the data uris on unmount to avoid memory leaks
55
- files.forEach(FileInput_utils_1.releasePreview);
64
+ files.forEach(_FileInput_utils_1.releasePreview);
56
65
  }, [files]);
57
- const removeFile = (name) => {
66
+ const removeFile = (removeTarget) => {
58
67
  if (fileInput.current) {
59
68
  const deleted = [];
69
+ const targetName = typeof removeTarget !== 'string' ? removeTarget.name : removeTarget;
60
70
  const newFileList = files.filter((file) => {
61
- if (file.name !== name) {
71
+ if (file.name !== targetName) {
62
72
  return true;
63
73
  }
64
74
  deleted.push(file);
65
- (0, FileInput_utils_1.releasePreview)(file);
75
+ (0, _FileInput_utils_1.releasePreview)(file);
66
76
  return false;
67
77
  });
68
78
  fileInput.current.files = arrayToFileList(newFileList);
@@ -71,7 +81,7 @@ const FileInput = (props) => {
71
81
  };
72
82
  const addFiles = (added) => {
73
83
  if (fileInput.current) {
74
- const { fileList, diff } = (0, FileInput_utils_1.getFileListUpdate)(files, added, !dropzoneProps.multiple);
84
+ const { fileList, diff } = (0, _FileInput_utils_1.getFileListUpdate)(files, added, !multiple);
75
85
  fileInput.current.files = arrayToFileList(fileList);
76
86
  onFilesUpdated(fileList, diff);
77
87
  }
@@ -80,27 +90,22 @@ const FileInput = (props) => {
80
90
  inputRef.current.files = arrayToFileList([]);
81
91
  }
82
92
  };
83
- const filesList = files.map((file) => (react_1.default.createElement("li", { key: file.name, className: "FileInput__file" },
84
- react_1.default.createElement("button", { className: "FileInput__file-remove", type: "button", onClick: () => removeFile(file.name), "aria-label": removeFileText }, removeFileText),
85
- react_1.default.createElement("span", { className: "FileInput__fileinfo" },
86
- showImagePreviews && file.preview && (react_1.default.createElement(react_1.default.Fragment, null,
87
- react_1.default.createElement("span", { className: "FileInput__preview" },
88
- react_1.default.createElement("img", { src: file.preview })),
89
- ' ')),
90
- react_1.default.createElement("span", { className: "FileInput__filename" }, file.name),
91
- showFileSize && (react_1.default.createElement("small", { className: "FileInput__filesize" },
92
- " - (",
93
- (0, FileInput_utils_1.formatBytes)(file.size),
94
- ")"))))));
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 */) => {
93
+ return (react_1.default.createElement(FormField_1.default, { className: (0, getBemClass_1.default)('FileInput', [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 */) => {
96
94
  return (react_1.default.createElement("div", { className: className.control, ref: fileInputWrapper },
97
- react_1.default.createElement("input", { className: "FileInput__input", name: inputElementProps.name, id: domid, ref: fileInput, type: "file", style: { display: 'none' }, multiple: dropzoneProps.multiple || undefined, required: inputProps.required }),
95
+ react_1.default.createElement("input", { className: "FileInput__input", name: inputElementProps.name, id: domid, ref: fileInput, type: "file", style: { display: 'none' }, multiple: multiple || undefined, required: inputProps.required }),
98
96
  ' ',
99
- react_1.default.createElement("input", Object.assign({ className: "FileInput__input--fake" }, getInputProps(), { tabIndex: undefined, style: undefined, multiple: dropzoneProps.multiple || undefined }, inputProps, { required: undefined })),
97
+ react_1.default.createElement("input", Object.assign({ className: "FileInput__input--fake" }, getInputProps(), { tabIndex: undefined, style: undefined, multiple: multiple || undefined }, inputProps, { required: undefined })),
100
98
  ' ',
101
99
  react_1.default.createElement("div", Object.assign({ className: (0, getBemClass_1.default)('FileInput__dropzone', [isHover && 'highlight']) }, getRootProps({ isDragReject }), { tabIndex: undefined }),
102
100
  react_1.default.createElement("p", { className: "FileInput__droptext" }, dropzoneText)),
103
- filesList.length ? react_1.default.createElement("ul", { className: "FileInput__filelist" }, filesList) : ''));
101
+ FileList && (react_1.default.createElement(FileList, Object.assign({}, {
102
+ files,
103
+ showFileSize,
104
+ showImagePreviews,
105
+ removeFileText,
106
+ removeFile,
107
+ formatBytes: _FileInput_utils_1.formatBytes,
108
+ })))));
104
109
  } }));
105
110
  };
106
111
  exports.default = FileInput;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/hanna-react",
3
- "version": "0.10.63",
3
+ "version": "0.10.64",
4
4
  "author": "Reykjavík (http://www.reykjavik.is)",
5
5
  "contributors": [
6
6
  "Hugsmiðjan ehf (http://www.hugsmidjan.is)",