@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 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 dropzonePropsProps = {
2
+ declare type DropzonePropsProps = {
3
3
  accept?: string;
4
4
  multiple?: boolean;
5
5
  };
6
6
  export declare type FileInputProps = {
7
- dropzoneProps: dropzonePropsProps;
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 deleted = [];
102
- const retained = [];
103
- const oldFiles = dropzoneProps.multiple ? files : [];
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 must not be removable');
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.62",
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.3",
19
- "@reykjavik/hanna-utils": "^0.1.7",
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",