@luscii-healthtech/web-ui 2.3.1 → 2.5.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.
Files changed (32) hide show
  1. package/dist/components/Form/Form.d.ts +15 -5
  2. package/dist/components/Form/form.types.d.ts +6 -2
  3. package/dist/components/Icons/WarningIcon.d.ts +3 -0
  4. package/dist/components/Icons/types/IconProps.type.d.ts +1 -0
  5. package/dist/components/Toaster/Toaster.d.ts +17 -0
  6. package/dist/components/Toaster/toast-elements-getter.d.ts +22 -0
  7. package/dist/components/Toaster/toast-progress-animator.d.ts +12 -0
  8. package/dist/components/Toaster/toast.d.ts +12 -0
  9. package/dist/index.d.ts +7 -3
  10. package/dist/web-ui-tailwind.css +29 -0
  11. package/dist/web-ui.cjs.development.js +700 -482
  12. package/dist/web-ui.cjs.development.js.map +1 -1
  13. package/dist/web-ui.cjs.production.min.js +1 -1
  14. package/dist/web-ui.cjs.production.min.js.map +1 -1
  15. package/dist/web-ui.esm.js +694 -481
  16. package/dist/web-ui.esm.js.map +1 -1
  17. package/package.json +1 -1
  18. package/src/components/Form/Form.tsx +100 -68
  19. package/src/components/Form/form.types.ts +8 -1
  20. package/src/components/Icons/CheckIcon.tsx +1 -0
  21. package/src/components/Icons/CrossIcon.tsx +1 -0
  22. package/src/components/Icons/WarningIcon.tsx +24 -0
  23. package/src/components/Icons/types/IconProps.type.ts +1 -0
  24. package/src/components/Toaster/Toaster.scss +53 -0
  25. package/src/components/Toaster/Toaster.tsx +100 -0
  26. package/src/components/Toaster/toast-elements-getter.ts +72 -0
  27. package/src/components/Toaster/toast-progress-animator.ts +53 -0
  28. package/src/components/Toaster/toast.ts +112 -0
  29. package/src/index.tsx +14 -6
  30. package/dist/components/Acknowledgement/Acknowledgement.d.ts +0 -22
  31. package/src/components/Acknowledgement/Acknowledgement.js +0 -61
  32. package/src/components/Acknowledgement/Acknowledgement.scss +0 -49
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.3.1",
2
+ "version": "2.5.0",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { Control, useForm } from "react-hook-form";
2
+ import { Control, useForm, UseFormReturn } from "react-hook-form";
3
3
 
4
4
  import { PrimaryButton } from "../ButtonV2/PrimaryButton";
5
5
  import { InputProps } from "../Input/Input";
@@ -7,85 +7,56 @@ import { RadioGroupProps } from "../RadioGroup/RadioGroupV2";
7
7
  import { SelectProps } from "../Select/SelectV2";
8
8
 
9
9
  import { FormInput } from "./FormInput";
10
- import { FormFieldProps, FormFieldRowProps, FormProps } from "./form.types";
10
+ import {
11
+ FormFieldProps,
12
+ FormFieldRowProps,
13
+ FormProps,
14
+ GenericFormProps,
15
+ } from "./form.types";
11
16
  import { isRequired } from "./form.transformer";
12
17
  import { FormRadioGroup } from "./FormRadioGroup";
13
18
  import { FormSelect } from "./FormSelect";
14
19
 
15
20
  /**
16
- * Create a straight forward Form.
21
+ * Create a straight forward Form, which takes away the 'overhead' of react-hook-form.
17
22
  *
18
- * TODO: wrap this in some Page component to style the div and buttons
19
- * WARNING: don't use this component before some styling errors are resolved.
23
+ * You will probably don't want to use this component for now, given that the button is hardcoded.
24
+ * This is an example for the Form for now, though it could be improved to enforce further unification.
25
+ *
26
+ * TODO: make the buttons configurable.
20
27
  */
21
- export function Form<TFieldValues>({
28
+ export function GenericForm<TFieldValues>({
22
29
  fields,
23
30
  onValid,
24
31
  onError,
25
32
  defaultValues,
26
- }: FormProps<TFieldValues>): JSX.Element {
27
- const {
28
- register,
29
- handleSubmit,
30
- control,
31
- formState: { errors },
32
- } = useForm<TFieldValues>({
33
+ }: GenericFormProps<TFieldValues>): JSX.Element {
34
+ const useFormReturn = useForm<TFieldValues>({
33
35
  criteriaMode: "all",
34
36
  defaultValues: defaultValues,
35
37
  });
36
38
 
37
- const fieldMapper = ({
38
- type,
39
- name,
40
- options,
41
- fieldProps = {},
42
- ...decoratorProps
43
- }: FormFieldProps<TFieldValues>) => {
44
- switch (type) {
45
- case "text":
46
- case "number":
47
- case "email":
48
- case "password":
49
- return (
50
- <FormInput
51
- key={name}
52
- {...decoratorProps}
53
- fieldRequired={isRequired(options)}
54
- fieldErrors={errors}
55
- {...(fieldProps as InputProps)}
56
- {...register(name, options)}
57
- type={type || "text"}
58
- />
59
- );
60
- case "select":
61
- return (
62
- <FormSelect
63
- key={name}
64
- {...decoratorProps}
65
- fieldRequired={isRequired(options)}
66
- fieldErrors={errors}
67
- {...(fieldProps as SelectProps)}
68
- control={control as Control}
69
- rules={options}
70
- name={name}
71
- />
72
- );
73
- case "radioGroup":
74
- return (
75
- <FormRadioGroup
76
- key={name}
77
- {...decoratorProps}
78
- fieldRequired={isRequired(options)}
79
- fieldErrors={errors}
80
- {...(fieldProps as RadioGroupProps)}
81
- {...register(name, options)}
82
- />
83
- );
84
- default:
85
- return undefined;
86
- }
87
- };
39
+ const { handleSubmit } = useFormReturn;
88
40
 
41
+ return (
42
+ <div className="space-y-4">
43
+ <Form fields={fields} useFormReturn={useFormReturn} />
44
+ <PrimaryButton onClick={handleSubmit(onValid, onError)} text={"submit"} />
45
+ </div>
46
+ );
47
+ }
48
+
49
+ /**
50
+ * Creates a Form based on the fields input.
51
+ *
52
+ * Expects the results of the useForm hook to be injected into the useFormReturn parameter.
53
+ *
54
+ * This allows you to use and modify the useFormReturn before injecting it here.
55
+ */
56
+ export function Form<TFieldValues, TContext>({
57
+ fields,
58
+ useFormReturn,
59
+ }: FormProps<TFieldValues, TContext>): JSX.Element {
89
60
  return (
90
61
  <div className="space-y-4">
91
62
  {fields.map((props) => {
@@ -93,14 +64,75 @@ export function Form<TFieldValues>({
93
64
  const { fields: rowFields, key } =
94
65
  props as FormFieldRowProps<TFieldValues>;
95
66
  return (
96
- <div className={"flex flex-row"} key={key}>
97
- {rowFields.map(fieldMapper)}
67
+ <div className={"flex flex-row space-x-4"} key={key}>
68
+ {rowFields.map((field) => FormFieldMapper(field, useFormReturn))}
98
69
  </div>
99
70
  );
100
71
  }
101
- return fieldMapper(props);
72
+ return FormFieldMapper(props, useFormReturn);
102
73
  })}
103
- <PrimaryButton onClick={handleSubmit(onValid, onError)} text={"submit"} />
104
74
  </div>
105
75
  );
106
76
  }
77
+
78
+ function FormFieldMapper<TFieldValues, TContext>(
79
+ formFieldProps: FormFieldProps<TFieldValues>,
80
+ useFormReturn: UseFormReturn<TFieldValues, TContext>
81
+ ): JSX.Element {
82
+ const {
83
+ type,
84
+ name,
85
+ options,
86
+ fieldProps = {},
87
+ ...decoratorProps
88
+ } = formFieldProps;
89
+ const {
90
+ register,
91
+ control,
92
+ formState: { errors },
93
+ } = useFormReturn;
94
+
95
+ switch (type) {
96
+ case "text":
97
+ case "number":
98
+ case "email":
99
+ case "password":
100
+ return (
101
+ <FormInput
102
+ key={name}
103
+ {...decoratorProps}
104
+ fieldRequired={isRequired(options)}
105
+ fieldErrors={errors}
106
+ {...(fieldProps as InputProps)}
107
+ {...register(name, options)}
108
+ type={type || "text"}
109
+ />
110
+ );
111
+ case "select":
112
+ return (
113
+ <FormSelect
114
+ key={name}
115
+ {...decoratorProps}
116
+ fieldRequired={isRequired(options)}
117
+ fieldErrors={errors}
118
+ {...(fieldProps as SelectProps)}
119
+ control={control as Control}
120
+ rules={options}
121
+ name={name}
122
+ />
123
+ );
124
+ case "radioGroup":
125
+ return (
126
+ <FormRadioGroup
127
+ key={name}
128
+ {...decoratorProps}
129
+ fieldRequired={isRequired(options)}
130
+ fieldErrors={errors}
131
+ {...(fieldProps as RadioGroupProps)}
132
+ {...register(name, options)}
133
+ />
134
+ );
135
+ default:
136
+ return <></>;
137
+ }
138
+ }
@@ -7,6 +7,7 @@ import {
7
7
  RegisterOptions,
8
8
  SubmitErrorHandler,
9
9
  SubmitHandler,
10
+ UseFormReturn,
10
11
  } from "react-hook-form";
11
12
  import React, { HTMLInputTypeAttribute } from "react";
12
13
 
@@ -27,7 +28,7 @@ export type AllowedTextInputTypes = Extract<
27
28
  // --------------------------------------------
28
29
 
29
30
  // the input for the 'out-of-the-box' Form
30
- export interface FormProps<TFieldValues extends FieldValues> {
31
+ export interface GenericFormProps<TFieldValues extends FieldValues> {
31
32
  // the fields to be rendered
32
33
  fields: (FormFieldProps<TFieldValues> | FormFieldRowProps<TFieldValues>)[];
33
34
  onValid: SubmitHandler<TFieldValues>;
@@ -36,6 +37,12 @@ export interface FormProps<TFieldValues extends FieldValues> {
36
37
  defaultValues?: DeepPartial<TFieldValues>;
37
38
  }
38
39
 
40
+ export interface FormProps<TFieldValues extends FieldValues, TContext> {
41
+ // the fields to be rendered
42
+ fields: (FormFieldProps<TFieldValues> | FormFieldRowProps<TFieldValues>)[];
43
+ useFormReturn: UseFormReturn<TFieldValues, TContext>;
44
+ }
45
+
39
46
  interface FormFieldGenericProps<TFieldType, TFieldValues>
40
47
  extends FormFieldDecoratorProps {
41
48
  // the name of the field, which is registered within react-hook-form
@@ -8,6 +8,7 @@ export const CheckIcon = (props: IconProps): JSX.Element => {
8
8
  className={props.className}
9
9
  onClick={props.onClick}
10
10
  role={props.onClick ? "button" : undefined}
11
+ data-test-id={props["data-test-id"]}
11
12
  width="24"
12
13
  height="24"
13
14
  viewBox="0 0 24 24"
@@ -8,6 +8,7 @@ export const CrossIcon = (props: IconProps): JSX.Element => {
8
8
  className={props.className}
9
9
  onClick={props.onClick}
10
10
  role={props.onClick ? "button" : undefined}
11
+ data-test-id={props["data-test-id"]}
11
12
  width="24"
12
13
  height="24"
13
14
  viewBox="0 0 24 24"
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+
3
+ import { IconProps } from "./types/IconProps.type";
4
+
5
+ export const WarningIcon: React.VoidFunctionComponent<IconProps> = (props) => {
6
+ return (
7
+ <svg
8
+ className={props.className}
9
+ onClick={props.onClick}
10
+ role={props.onClick ? "button" : undefined}
11
+ data-test-id={props["data-test-id"]}
12
+ width="20"
13
+ height="20"
14
+ viewBox="0 0 20 20"
15
+ fill="none"
16
+ xmlns="http://www.w3.org/2000/svg"
17
+ >
18
+ <path
19
+ d="M10 0C4.48 0 0 4.48 0 10C0 15.52 4.48 20 10 20C15.52 20 20 15.52 20 10C20 4.48 15.52 0 10 0ZM10 11C9.45 11 9 10.55 9 10V6C9 5.45 9.45 5 10 5C10.55 5 11 5.45 11 6V10C11 10.55 10.55 11 10 11ZM11 15H9V13H11V15Z"
20
+ fill="currentColor"
21
+ />
22
+ </svg>
23
+ );
24
+ };
@@ -1,4 +1,5 @@
1
1
  export interface IconProps {
2
2
  className?: string;
3
3
  onClick?: (event: React.MouseEvent<SVGSVGElement> | undefined) => void;
4
+ "data-test-id"?: string;
4
5
  }
@@ -0,0 +1,53 @@
1
+ #application-toaster {
2
+ position: fixed;
3
+ left: 50%;
4
+ transform: translate(-50%, 150%);
5
+ bottom: 48px;
6
+
7
+ // https://easings.net/#easeInOutBack
8
+ transition: transform 0.3s cubic-bezier(0.68, -0.6, 0.32, 1.6);
9
+
10
+ &.shelved {
11
+ transform: translate(-50%, 150%);
12
+ }
13
+
14
+ &.expanded {
15
+ transform: translate(-50%, 0);
16
+ }
17
+
18
+ &.type-success {
19
+ [data-test-id="toaster-title"] {
20
+ @apply text-green-700;
21
+ }
22
+
23
+ .failure-icon {
24
+ @apply hidden;
25
+ }
26
+
27
+ [data-test-id="toaster-progress-bar-container"] {
28
+ @apply bg-green-50;
29
+
30
+ [data-test-id="toaster-progress-bar"] {
31
+ @apply bg-green-400;
32
+ }
33
+ }
34
+ }
35
+
36
+ &.type-failure {
37
+ [data-test-id="toaster-title"] {
38
+ @apply text-red-700;
39
+ }
40
+
41
+ .success-icon {
42
+ @apply hidden;
43
+ }
44
+
45
+ [data-test-id="toaster-progress-bar-container"] {
46
+ @apply bg-red-50;
47
+
48
+ [data-test-id="toaster-progress-bar"] {
49
+ @apply bg-red-400;
50
+ }
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,100 @@
1
+ import React from "react";
2
+ import classNames from "classnames";
3
+
4
+ import { Text } from "../Text/Text";
5
+ import { CheckIcon } from "../Icons/CheckIcon";
6
+ import { CrossIcon } from "../Icons/CrossIcon";
7
+ import { WarningIcon } from "../Icons/WarningIcon";
8
+
9
+ import "./Toaster.scss";
10
+
11
+ export interface ToasterProps {
12
+ type: "success" | "failure";
13
+ message: string;
14
+ title: string;
15
+ isVisible?: boolean;
16
+ /** if you need to reposition the toaster for whatever reason you can use these props */
17
+ styleOverwrite?: Pick<
18
+ React.CSSProperties,
19
+ "top" | "bottom" | "left" | "right"
20
+ >;
21
+ }
22
+
23
+ export const TOASTER_TYPE_OPTIONS = {
24
+ SUCCESS: "success",
25
+ FAILURE: "failure",
26
+ } as const;
27
+ export type ToasterType =
28
+ typeof TOASTER_TYPE_OPTIONS[keyof typeof TOASTER_TYPE_OPTIONS];
29
+
30
+ const Toaster: React.VoidFunctionComponent<ToasterProps> = ({
31
+ message = "",
32
+ title = "",
33
+ type = TOASTER_TYPE_OPTIONS.SUCCESS,
34
+ isVisible,
35
+ styleOverwrite,
36
+ }) => {
37
+ const isSuccess = type === TOASTER_TYPE_OPTIONS.SUCCESS;
38
+ const isFailure = type === TOASTER_TYPE_OPTIONS.FAILURE;
39
+
40
+ return (
41
+ <div
42
+ style={styleOverwrite}
43
+ id="application-toaster"
44
+ data-test-id={`toaster-panel-${type}`}
45
+ className={classNames(
46
+ "bg-white cursor-pointer",
47
+ "rounded-md shadow-md",
48
+ "min-h-13 max-h-19 w-104 transition-transform",
49
+
50
+ {
51
+ shelved: !isVisible,
52
+ expanded: isVisible,
53
+ "type-success": isSuccess,
54
+ "type-failure": isFailure,
55
+ }
56
+ )}
57
+ >
58
+ <div className="relative p-4">
59
+ <div className="flex flex-row">
60
+ <div
61
+ data-test-id="success-toaster-icon"
62
+ className={classNames("success-icon text-green-700", {
63
+ hidden: isFailure,
64
+ })}
65
+ >
66
+ <CheckIcon />
67
+ </div>
68
+ <div
69
+ data-test-id="failure-toaster-icon"
70
+ className={classNames("failure-icon text-red-700", {
71
+ hidden: isSuccess,
72
+ })}
73
+ >
74
+ <WarningIcon />
75
+ </div>
76
+ <div className="ml-3">
77
+ <Text type="strong" data-test-id="toaster-title" text={title} />
78
+ <Text data-test-id="toaster-message" text={message} />
79
+ </div>
80
+ <CrossIcon
81
+ data-test-id="toaster-close-button"
82
+ className="ml-auto cursor-pointer text-slate-500"
83
+ />
84
+ </div>
85
+ </div>
86
+
87
+ <div
88
+ data-test-id="toaster-progress-bar-container"
89
+ className={classNames("h-1 absolute bottom-0 w-full rounded-b-md")}
90
+ >
91
+ <div
92
+ data-test-id="toaster-progress-bar"
93
+ className={classNames("h-full")}
94
+ />
95
+ </div>
96
+ </div>
97
+ );
98
+ };
99
+
100
+ export default Toaster;
@@ -0,0 +1,72 @@
1
+ interface GetToasterElementsParams {
2
+ timeoutId: NodeJS.Timeout;
3
+ progressBarAnimationFrameHandler: number;
4
+ }
5
+
6
+ interface GetToasterElementsReturn {
7
+ toasterElementMessage: HTMLElement;
8
+ toasterProgressBar: HTMLElement;
9
+ toasterElementTitle: HTMLElement;
10
+ toasterElementSuccessIcon: HTMLElement;
11
+ toasterElementFailureIcon: HTMLElement;
12
+ toasterElementContainer: HTMLElement;
13
+ toasterCloseButton: HTMLElement;
14
+ }
15
+
16
+ /**
17
+ *
18
+ * @param params.timeoutId - the timeoutId (that makes the toaster vanish), in case of a shortcut
19
+ * @param params.progressBarAnimationFrameHandler - animation frame id to be canceled in case of a shortcut
20
+ * @returns a collection of html elements to be manipulated by the animation
21
+ */
22
+ export const getToasterElements = (
23
+ params: GetToasterElementsParams
24
+ ): GetToasterElementsReturn => {
25
+ const { timeoutId, progressBarAnimationFrameHandler } = params;
26
+ const toasterElementContainer = document.querySelector<HTMLElement>(
27
+ "#application-toaster"
28
+ );
29
+
30
+ if (!toasterElementContainer) {
31
+ clearTimeout(timeoutId);
32
+ clearTimeout(progressBarAnimationFrameHandler);
33
+
34
+ throw new Error(
35
+ "Make sure that the Toaster component element is rendered with an id [application-toaster]"
36
+ );
37
+ }
38
+
39
+ const toasterElementMessage = toasterElementContainer?.querySelector(
40
+ '[data-test-id="toaster-message"]'
41
+ ) as HTMLElement;
42
+
43
+ const toasterElementTitle = toasterElementContainer?.querySelector(
44
+ '[data-test-id="toaster-title"]'
45
+ ) as HTMLElement;
46
+
47
+ const toasterElementSuccessIcon = toasterElementContainer?.querySelector(
48
+ '[data-test-id="success-toaster-icon"]'
49
+ ) as HTMLElement;
50
+
51
+ const toasterElementFailureIcon = toasterElementContainer?.querySelector(
52
+ '[data-test-id="failure-toaster-icon"]'
53
+ ) as HTMLElement;
54
+
55
+ const toasterCloseButton = toasterElementContainer?.querySelector(
56
+ '[data-test-id="toaster-close-button"]'
57
+ ) as HTMLElement;
58
+
59
+ const toasterProgressBar = toasterElementContainer?.querySelector(
60
+ '[data-test-id="toaster-progress-bar"]'
61
+ ) as HTMLElement;
62
+
63
+ return {
64
+ toasterElementContainer,
65
+ toasterElementMessage,
66
+ toasterElementTitle,
67
+ toasterProgressBar,
68
+ toasterElementSuccessIcon,
69
+ toasterElementFailureIcon,
70
+ toasterCloseButton,
71
+ };
72
+ };
@@ -0,0 +1,53 @@
1
+ interface AnimateProgressParams {
2
+ animationDuration: number;
3
+ progressBarElement: HTMLElement;
4
+ }
5
+
6
+ /**
7
+ * Reference: https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
8
+ * @param params.animationDuration - the amount of seconds the duration will take
9
+ * @param params.progressBarElement - the html element for the progress bar
10
+ * @returns the animation frame id
11
+ */
12
+ export const animateProgress = (params: AnimateProgressParams): number => {
13
+ const { animationDuration, progressBarElement } = params;
14
+
15
+ let progressBarAnimationFrameHandler: number;
16
+
17
+ let startingTimestamp = 0;
18
+ let previousTimestamp = 0;
19
+ let done = false;
20
+ const animationTick = 100 / animationDuration;
21
+
22
+ const animate = (animationTimestamp: number) => {
23
+ if (!startingTimestamp) {
24
+ startingTimestamp = animationTimestamp;
25
+ }
26
+
27
+ const elapsed = animationTimestamp - startingTimestamp;
28
+
29
+ if (previousTimestamp !== animationTimestamp) {
30
+ const count = 100 - animationTick * elapsed;
31
+ progressBarElement.style.width = `${count}%`;
32
+ if (count === 0) {
33
+ done = true;
34
+ }
35
+ }
36
+
37
+ if (elapsed < animationDuration) {
38
+ // Stop the animation after duration is finished
39
+ previousTimestamp = animationTimestamp;
40
+
41
+ if (!done) {
42
+ progressBarAnimationFrameHandler =
43
+ window.requestAnimationFrame(animate);
44
+ }
45
+ }
46
+ };
47
+
48
+ progressBarElement.style.width = "100%";
49
+
50
+ progressBarAnimationFrameHandler = requestAnimationFrame(animate);
51
+
52
+ return progressBarAnimationFrameHandler;
53
+ };
@@ -0,0 +1,112 @@
1
+ import { getToasterElements } from "./toast-elements-getter";
2
+ import { animateProgress } from "./toast-progress-animator";
3
+
4
+ let timeoutId: NodeJS.Timeout;
5
+ let progressBarAnimationFrameHandler: number;
6
+
7
+ const defaultDurationInSeconds = 5;
8
+
9
+ type ToastParams =
10
+ | {
11
+ message: string;
12
+ title?: string;
13
+ showIcon?: boolean;
14
+ type: "success" | "failure";
15
+ }
16
+ | string;
17
+
18
+ const showToaster = (params: ToastParams) => {
19
+ clearTimeout(timeoutId);
20
+ clearTimeout(progressBarAnimationFrameHandler);
21
+
22
+ const {
23
+ toasterElementContainer,
24
+ toasterElementMessage,
25
+ toasterElementTitle,
26
+ toasterProgressBar,
27
+ toasterElementSuccessIcon,
28
+ toasterElementFailureIcon,
29
+ toasterCloseButton,
30
+ } = getToasterElements({
31
+ timeoutId,
32
+ progressBarAnimationFrameHandler,
33
+ });
34
+
35
+ const animationDuration = defaultDurationInSeconds * 1000;
36
+
37
+ progressBarAnimationFrameHandler = animateProgress({
38
+ animationDuration,
39
+ progressBarElement: toasterProgressBar,
40
+ });
41
+
42
+ timeoutId = setTimeout(() => {
43
+ toasterElementContainer.classList.remove("expanded");
44
+ clearTimeout(timeoutId);
45
+ cancelAnimationFrame(progressBarAnimationFrameHandler);
46
+ }, animationDuration);
47
+
48
+ function shortcutAndClose() {
49
+ toasterElementContainer.classList.remove("expanded");
50
+ toasterElementContainer.classList.add("shelved");
51
+
52
+ clearTimeout(timeoutId);
53
+ cancelAnimationFrame(progressBarAnimationFrameHandler);
54
+ }
55
+
56
+ toasterCloseButton.removeEventListener("click", shortcutAndClose, true);
57
+ toasterCloseButton.addEventListener("click", shortcutAndClose);
58
+
59
+ toasterElementContainer.removeEventListener("click", shortcutAndClose, true);
60
+ toasterElementContainer.addEventListener("click", shortcutAndClose);
61
+
62
+ // Basic usage, no need to manipulate any other element;
63
+ if (typeof params === "string") {
64
+ toasterElementMessage.textContent = params;
65
+ toasterElementTitle.classList.add("hidden");
66
+
67
+ toasterElementSuccessIcon.classList.remove("hidden");
68
+ toasterElementFailureIcon.classList.add("hidden");
69
+
70
+ toasterElementContainer.classList.remove("type-failure");
71
+ toasterElementContainer.classList.add("type-success");
72
+ toasterElementContainer.classList.add("expanded");
73
+
74
+ return;
75
+ }
76
+
77
+ const { title = "", showIcon = true, message, type } = params;
78
+
79
+ toasterElementTitle.classList.toggle("hidden", !title);
80
+ toasterElementTitle.textContent = title;
81
+
82
+ toasterElementSuccessIcon.classList.toggle("hidden", !showIcon);
83
+ toasterElementFailureIcon.classList.toggle("hidden", !showIcon);
84
+
85
+ toasterElementMessage.textContent = message;
86
+ toasterElementContainer.classList.remove("type-success");
87
+ toasterElementContainer.classList.remove("type-failure");
88
+ toasterElementContainer.classList.add("expanded");
89
+ toasterElementContainer.classList.add(`type-${type}`);
90
+ };
91
+
92
+ export const toast = (params: ToastParams): void => {
93
+ showToaster(params);
94
+ };
95
+
96
+ toast.error = (message: string): void => {
97
+ showToaster({
98
+ message: message,
99
+ type: "failure",
100
+ showIcon: true,
101
+ title: "",
102
+ });
103
+ };
104
+
105
+ toast.info = (message: string): void => {
106
+ showToaster({
107
+ message: message,
108
+ type: "success",
109
+ showIcon: true,
110
+ title: "",
111
+ });
112
+ };