@luscii-healthtech/web-ui 6.2.2 → 6.3.0

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.
@@ -5,5 +5,5 @@ interface FormImagePickerProps extends ImagePickerFieldConfiguration, FormFieldL
5
5
  control: Control;
6
6
  rules?: Exclude<RegisterOptions, "valueAsNumber" | "valueAsDate" | "setValueAs">;
7
7
  }
8
- export declare const FormImagePicker: React.ForwardRefExoticComponent<Omit<FormImagePickerProps, "ref"> & React.RefAttributes<any>>;
8
+ export declare const FormImagePicker: React.ForwardRefExoticComponent<FormImagePickerProps & React.RefAttributes<any>>;
9
9
  export {};
@@ -3,7 +3,7 @@ import React, { HTMLInputTypeAttribute } from "react";
3
3
  import { InputProps } from "../Input/Input";
4
4
  import { RadioGroupProps } from "../RadioGroup/RadioGroupV2";
5
5
  import { SelectProps } from "../Select/Select";
6
- import { ImagePickerProps } from "../ImagePicker/ImagePicker";
6
+ import { ImagePickerProps } from "../MediaPicker/MediaPicker";
7
7
  import { PartialProperties } from "../../types/general.types";
8
8
  import { CheckboxProps } from "../Checkbox/Checkbox";
9
9
  import { CheckboxListProps } from "../CheckboxList/CheckboxList.types";
@@ -3,8 +3,11 @@ import { CategoryProps } from "./ImageCategory";
3
3
  export interface TargetProps {
4
4
  target: {
5
5
  name: string | null | undefined;
6
+ /**
7
+ * It is called imageData, but it can any file that was selected. The name is kept for backwards compatibility for now.
8
+ */
6
9
  value: string | null | undefined | {
7
- imageData: string;
10
+ imageData: FormData;
8
11
  };
9
12
  };
10
13
  }
@@ -12,9 +15,8 @@ export interface ImagePickerProps {
12
15
  name: string;
13
16
  images: string[];
14
17
  categories: CategoryProps[];
15
- preselectedImage?: string | {
16
- imageData: string;
17
- };
18
+ preselectedImage?: string;
19
+ preselectedMediaType?: "image" | "video";
18
20
  /**
19
21
  * Array of [valid MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types).
20
22
  *
@@ -34,6 +36,8 @@ export interface ImagePickerProps {
34
36
  handleChange: (target: TargetProps) => void;
35
37
  isUploadButtonVisible?: boolean;
36
38
  isClearButtonVisible?: boolean;
39
+ isUploading?: boolean;
40
+ isErrorUploading?: boolean;
37
41
  localisation: Partial<Record<"openModalButton" | "selectButton" | "error" | "uploadImage" | "clearSelection" | "cancel" | "search" | "unsupportedMediaType" | "modalTitle", string>>;
38
42
  }
39
43
  export declare const ImagePickerInner: React.FC<ImagePickerProps & {
@@ -47,16 +51,37 @@ export interface MediaPickerProps {
47
51
  name: string;
48
52
  media: string[];
49
53
  categories: CategoryProps[];
50
- preselectedMedia?: string | {
51
- mediaData: string;
52
- };
54
+ /**
55
+ * If you add preselectedMedia you must also specify preselectedMediaType, especially if it is not an image.
56
+ *
57
+ * @example
58
+ * ```tsx
59
+ * <MediaPicker preselectedMedia="http://www.domain.com/someVideo.mp4" preselectedMediaType="video"/>
60
+ * ```
61
+ */
62
+ preselectedMedia?: string;
63
+ preselectedMediaType?: "image" | "video";
53
64
  clearIndex?: number;
54
65
  type?: "normal" | "compact";
66
+ /**
67
+ * Array of [valid MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types).
68
+ *
69
+ * If the media the user tries to upload doesn't match one of the
70
+ * MIME types in this array, an error message will be displayed in
71
+ * the UI.
72
+ *
73
+ * @example
74
+ * ```tsx
75
+ * <MediaPicker acceptedMimeTypes={[ "text/*", "image/jpeg", "video/mp4"]} />
76
+ * ```
77
+ */
55
78
  acceptedMimeTypes?: string[];
56
79
  isDisabled?: boolean;
57
80
  onChange: (target: TargetProps) => void;
58
81
  isUploadButtonVisible?: boolean;
59
82
  isClearButtonVisible?: boolean;
83
+ isUploading?: boolean;
84
+ isErrorUploading?: boolean;
60
85
  localisation: Partial<Record<"openModalButton" | "selectButton" | "error" | "upload" | "clearSelection" | "cancel" | "search" | "unsupportedMediaType" | "modalTitle", string>>;
61
86
  }
62
87
  export declare const MediaPicker: (props: MediaPickerProps) => React.JSX.Element;
@@ -1537,16 +1537,6 @@ const isSubstring = (string, searchTerm, caseSensitive = false) => {
1537
1537
  const searchTermWithCase = caseSensitive ? searchTerm : searchTerm === null || searchTerm === void 0 ? void 0 : searchTerm.toLowerCase();
1538
1538
  return (stringToSearch === null || stringToSearch === void 0 ? void 0 : stringToSearch.indexOf(searchTermWithCase)) > -1;
1539
1539
  };
1540
- const getBase64 = (file) => {
1541
- return new Promise((resolve, reject) => {
1542
- const fr = new FileReader();
1543
- fr.onerror = reject;
1544
- fr.onload = function() {
1545
- resolve(fr.result.split(",")[1]);
1546
- };
1547
- fr.readAsDataURL(file);
1548
- });
1549
- };
1550
1540
 
1551
1541
  var img$i = "data:image/svg+xml,%3csvg width='144' height='144' viewBox='0 0 144 144' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M72 144C111.765 144 144 111.765 144 72C144 32.2355 111.765 0 72 0C32.2355 0 0 32.2355 0 72C0 111.765 32.2355 144 72 144Z' fill='%23BBE3D7'/%3e%3cpath d='M100 28H44C41.7909 28 40 29.7909 40 32V112C40 114.209 41.7909 116 44 116H100C102.209 116 104 114.209 104 112V32C104 29.7909 102.209 28 100 28Z' fill='%23FBFEFF'/%3e%3cellipse cx='58' cy='98' rx='4' ry='3' fill='%23FFD3E5'/%3e%3cellipse cx='58' cy='98' rx='4' ry='3' fill='%23FFD3E5'/%3e%3cellipse cx='58' cy='98' rx='4' ry='3' fill='%23FFD3E5'/%3e%3cellipse cx='86' cy='98' rx='4' ry='3' fill='%23FFD3E5'/%3e%3cellipse cx='86' cy='98' rx='4' ry='3' fill='%23FFD3E5'/%3e%3cellipse cx='86' cy='98' rx='4' ry='3' fill='%23FFD3E5'/%3e%3cpath d='M71.9007 101.202C70.0421 101.202 68.5352 99.6148 68.5352 97.6578V96H75.2663V97.6578C75.2663 99.6148 73.7594 101.202 71.9007 101.202Z' fill='%23454545'/%3e%3cpath d='M60.0918 96.0458C59.8056 95.6233 59.3882 95.5719 59.2189 95.5719C59.0496 95.5719 58.6252 95.6181 58.3461 96.0458C57.9584 96.6394 57.1663 96.8045 56.5764 96.4145C55.9865 96.025 55.8224 95.2273 56.2101 94.6338C56.8775 93.6111 58.0023 93 59.2189 93C60.4355 93 61.5603 93.6111 62.2284 94.6338C62.6154 95.2273 62.4513 96.025 61.8614 96.4145C61.645 96.5575 60.6888 96.9281 60.0918 96.0458Z' fill='%23454545'/%3e%3cpath d='M85.6543 96.0458C85.3681 95.6233 84.9513 95.5719 84.7814 95.5719C84.6121 95.5719 84.1883 95.6181 83.9092 96.0458C83.5215 96.6394 82.7288 96.8045 82.1389 96.4145C81.549 96.025 81.3849 95.2273 81.7726 94.6338C82.4406 93.6111 83.5655 93 84.7821 93C85.998 93 87.1228 93.6111 87.7909 94.6338C88.1786 95.2273 88.0144 96.025 87.4246 96.4145C87.2081 96.5575 86.2519 96.9281 85.6543 96.0458Z' fill='%23454545'/%3e%3crect x='88.8281' y='45.8281' width='8' height='4' rx='2' transform='rotate(45 88.8281 45.8281)' fill='%23BBE3D7'/%3e%3crect x='100.143' y='45.8281' width='12' height='4' rx='2' transform='rotate(135 100.143 45.8281)' fill='%23BBE3D7'/%3e%3crect x='46' y='46' width='34' height='8' rx='4' fill='%23C0E9FA'/%3e%3crect x='88.8281' y='65.8281' width='8' height='4' rx='2' transform='rotate(45 88.8281 65.8281)' fill='%23BBE3D7'/%3e%3crect x='100.143' y='65.8281' width='12' height='4' rx='2' transform='rotate(135 100.143 65.8281)' fill='%23BBE3D7'/%3e%3crect x='46' y='66' width='34' height='8' rx='4' fill='%23C0E9FA'/%3e%3c/svg%3e";
1552
1542
 
@@ -4818,7 +4808,7 @@ const ImageCategory = (props) => {
4818
4808
  );
4819
4809
  };
4820
4810
 
4821
- const ImagePickerInner = ({ categories, clearImageIndex, handleChange, images, isDisabled = false, name, preselectedImage, acceptedMimeTypes = ["image/svg+xml"], type = "normal", isUploadButtonVisible = false, isClearButtonVisible = false, localisation, innerRef }) => {
4811
+ const ImagePickerInner = ({ categories, clearImageIndex, handleChange, images, isDisabled = false, name, preselectedImage, preselectedMediaType = "image", acceptedMimeTypes = ["image/svg+xml"], type = "normal", isUploadButtonVisible = false, isClearButtonVisible = false, isUploading = false, isErrorUploading = false, localisation, innerRef }) => {
4822
4812
  var _a;
4823
4813
  const placeholderImage = clearImageIndex !== void 0 && images[clearImageIndex] ? images[clearImageIndex] : img;
4824
4814
  const isTypeCompact = type === "compact";
@@ -4833,8 +4823,16 @@ const ImagePickerInner = ({ categories, clearImageIndex, handleChange, images, i
4833
4823
  React.useEffect(() => {
4834
4824
  if (preselectedImage && typeof preselectedImage === "string") {
4835
4825
  setSelectedMedia(preselectedImage);
4826
+ setSelectedMediaType(preselectedMediaType);
4827
+ }
4828
+ }, [preselectedImage, preselectedMediaType]);
4829
+ React.useEffect(() => {
4830
+ if (isErrorUploading) {
4831
+ setSelectedMedia(placeholderImage);
4832
+ setSelectedMediaType("image");
4833
+ setHighlightedImage(null);
4836
4834
  }
4837
- }, [preselectedImage]);
4835
+ }, [isErrorUploading]);
4838
4836
  const handleSearchChange = (event) => {
4839
4837
  const searchQuery = event.target.value;
4840
4838
  setSearch(searchQuery);
@@ -4849,15 +4847,17 @@ const ImagePickerInner = ({ categories, clearImageIndex, handleChange, images, i
4849
4847
  setHighlightedImage(event.currentTarget.src);
4850
4848
  };
4851
4849
  const handleDeleteClick = () => {
4850
+ handleChange({ target: { name, value: "" } });
4852
4851
  if (selectedMedia) {
4853
4852
  URL.revokeObjectURL(selectedMedia);
4854
4853
  }
4855
- handleChange({ target: { name, value: "" } });
4856
4854
  setSelectedMedia(placeholderImage);
4855
+ setSelectedMediaType("image");
4857
4856
  setHighlightedImage(null);
4858
4857
  };
4859
4858
  function handleConfirmSelection() {
4860
4859
  setSelectedMedia(highlightedImage !== null && highlightedImage !== void 0 ? highlightedImage : placeholderImage);
4860
+ setSelectedMediaType("image");
4861
4861
  handleChange({
4862
4862
  target: { name, value: highlightedImage }
4863
4863
  });
@@ -4866,16 +4866,20 @@ const ImagePickerInner = ({ categories, clearImageIndex, handleChange, images, i
4866
4866
  const handleCloseModal = () => {
4867
4867
  setIsImagesPanelOpen(false);
4868
4868
  };
4869
+ const mediaType = (mimeType) => {
4870
+ return mimeType.split("/")[0];
4871
+ };
4869
4872
  const uploadImage = (event) => __awaiter(void 0, void 0, void 0, function* () {
4870
4873
  var _b;
4871
4874
  if (event.currentTarget.files) {
4872
4875
  const file = event.currentTarget.files[0];
4873
4876
  if (acceptedMimeTypes.includes(file.type)) {
4874
4877
  setSelectedMedia(URL.createObjectURL(file));
4875
- setSelectedMediaType(file.type.split("/")[0]);
4876
- const base64String = yield getBase64(file);
4878
+ setSelectedMediaType(mediaType(file.type));
4879
+ const data = new FormData();
4880
+ data.append("file", file, file.name);
4877
4881
  handleChange({
4878
- target: { name, value: { imageData: base64String } }
4882
+ target: { name, value: { imageData: data } }
4879
4883
  });
4880
4884
  } else {
4881
4885
  setError((_b = localisation.error) !== null && _b !== void 0 ? _b : null);
@@ -4891,15 +4895,18 @@ const ImagePickerInner = ({ categories, clearImageIndex, handleChange, images, i
4891
4895
  selectedMedia && React__namespace.default.createElement(
4892
4896
  "div",
4893
4897
  { className: classNames__default.default({
4894
- "ui-h-11 ui-w-11 ui-rounded-full ui-bg-secondary ui-p-3": isTypeCompact,
4895
- "ui-h-44 ": !isTypeCompact
4898
+ "ui-h-11 ui-w-11 ui-flex-shrink-0 ui-rounded-full ui-bg-secondary ui-p-3": isTypeCompact,
4899
+ "ui-h-44 ui-w-full": !isTypeCompact
4896
4900
  }) },
4897
- selectedMediaType === "image" && React__namespace.default.createElement(Image$1, { className: classNames__default.default("ui-h-full", {
4898
- "ui-rounded ui-bg-secondary ui-p-3": !isTypeCompact
4899
- }), src: selectedMedia }),
4900
- selectedMediaType === "video" && React__namespace.default.createElement("video", { className: classNames__default.default("ui-h-full", {
4901
- "ui-rounded ui-bg-secondary ui-p-3": !isTypeCompact
4901
+ isUploading && React__namespace.default.createElement(LoadingIndicator, { className: "ui-my-16" }),
4902
+ !isUploading && selectedMediaType === "image" && React__namespace.default.createElement(Image$1, { className: classNames__default.default("ui-h-full", {
4903
+ "ui-w-full ui-rounded ui-bg-secondary ui-object-contain ui-p-3": !isTypeCompact,
4904
+ "ui-w-full": isTypeCompact
4902
4905
  }), src: selectedMedia }),
4906
+ !isUploading && !isErrorUploading && selectedMediaType === "video" && React__namespace.default.createElement("video", { className: classNames__default.default("ui-h-full", {
4907
+ "ui-w-full ui-rounded ui-bg-secondary ui-object-contain ui-p-3": !isTypeCompact,
4908
+ "ui-w-full": isTypeCompact
4909
+ }), src: selectedMedia, playsInline: true, controls: true, disablePictureInPicture: true, preload: "none", poster: placeholderImage }, "There is a video, but your browser doesn't support showing the video."),
4903
4910
  selectedMediaType !== "image" && selectedMediaType !== "video" && React__namespace.default.createElement(InfoBlock, { message: (_a = localisation.unsupportedMediaType) !== null && _a !== void 0 ? _a : "File uploaded successfully but preview not supported." })
4904
4911
  ),
4905
4912
  error && React__namespace.default.createElement(ErrorBlock, { className: "cweb-no-action-error", message: error }),
@@ -4940,13 +4947,7 @@ const ImagePickerInner = ({ categories, clearImageIndex, handleChange, images, i
4940
4947
  const ImagePicker = React__namespace.default.forwardRef((props, ref) => React__namespace.default.createElement(ImagePickerInner, Object.assign({}, props, { innerRef: ref })));
4941
4948
  const MediaPicker = (props) => {
4942
4949
  var _a, _b;
4943
- let preselectedImage = void 0;
4944
- if (props.preselectedMedia && typeof props.preselectedMedia === "string") {
4945
- preselectedImage = props.preselectedMedia;
4946
- } else if (props.preselectedMedia && typeof props.preselectedMedia !== "string" && props.preselectedMedia.mediaData) {
4947
- preselectedImage = { imageData: props.preselectedMedia.mediaData };
4948
- }
4949
- const mappedProps = Object.assign(Object.assign({}, props), { preselectedImage, images: props.media, handleChange: props.onChange, clearImageIndex: props.clearIndex, localisation: Object.assign(Object.assign({}, props.localisation), { uploadImage: (_b = (_a = props.localisation) === null || _a === void 0 ? void 0 : _a.upload) !== null && _b !== void 0 ? _b : "Upload file" }) });
4950
+ const mappedProps = Object.assign(Object.assign({}, props), { preselectedImage: props.preselectedMedia, images: props.media, handleChange: props.onChange, clearImageIndex: props.clearIndex, localisation: Object.assign(Object.assign({}, props.localisation), { uploadImage: (_b = (_a = props.localisation) === null || _a === void 0 ? void 0 : _a.upload) !== null && _b !== void 0 ? _b : "Upload file" }) });
4950
4951
  return React__namespace.default.createElement(ImagePicker, Object.assign({}, mappedProps));
4951
4952
  };
4952
4953