formik-form-components 2.0.2 → 2.0.3
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/README.md +19 -1
- package/dist/Form/AppAutoCompleter.d.ts +11 -0
- package/dist/Form/AppAutoCompleter.d.ts.map +1 -0
- package/dist/Form/AppCheckBox.d.ts +15 -0
- package/dist/Form/AppCheckBox.d.ts.map +1 -0
- package/dist/Form/AppDateAndTimePicker.d.ts +14 -0
- package/dist/Form/AppDateAndTimePicker.d.ts.map +1 -0
- package/dist/Form/AppDatePicker.d.ts +11 -0
- package/dist/Form/AppDatePicker.d.ts.map +1 -0
- package/dist/Form/AppFormErrorMessage.d.ts +9 -0
- package/dist/Form/AppFormErrorMessage.d.ts.map +1 -0
- package/dist/Form/AppInputField.d.ts +9 -0
- package/dist/Form/AppInputField.d.ts.map +1 -0
- package/dist/Form/AppMultiSelector.d.ts +20 -0
- package/dist/Form/AppMultiSelector.d.ts.map +1 -0
- package/dist/Form/AppPhoneNoInput.d.ts +16 -0
- package/dist/Form/AppPhoneNoInput.d.ts.map +1 -0
- package/dist/Form/AppRadioGroup.d.ts +17 -0
- package/dist/Form/AppRadioGroup.d.ts.map +1 -0
- package/dist/Form/AppRating.d.ts +12 -0
- package/dist/Form/AppRating.d.ts.map +1 -0
- package/dist/Form/AppSelectInput.d.ts +15 -0
- package/dist/Form/AppSelectInput.d.ts.map +1 -0
- package/dist/Form/AppSimpleUploadFile.d.ts +14 -0
- package/dist/Form/AppSimpleUploadFile.d.ts.map +1 -0
- package/dist/Form/AppSwitch.d.ts +10 -0
- package/dist/Form/AppSwitch.d.ts.map +1 -0
- package/dist/Form/AppTagsCreator.d.ts +11 -0
- package/dist/Form/AppTagsCreator.d.ts.map +1 -0
- package/dist/Form/AppTextArea.d.ts +10 -0
- package/dist/Form/AppTextArea.d.ts.map +1 -0
- package/dist/Form/AppUploadFile.d.ts +20 -0
- package/dist/Form/AppUploadFile.d.ts.map +1 -0
- package/dist/Form/SubmitButton.d.ts +10 -0
- package/dist/Form/SubmitButton.d.ts.map +1 -0
- package/dist/Form/index.d.ts +10 -0
- package/dist/Form/index.d.ts.map +1 -0
- package/dist/assets/illustrations/BackgroundIllustration.d.ts +7 -0
- package/dist/assets/illustrations/BackgroundIllustration.d.ts.map +1 -0
- package/dist/assets/illustrations/UploadIllustration.d.ts +5 -0
- package/dist/assets/illustrations/UploadIllustration.d.ts.map +1 -0
- package/dist/assets/illustrations/index.d.ts +2 -0
- package/dist/assets/illustrations/index.d.ts.map +1 -0
- package/dist/file-thumbnail/types.d.ts +6 -0
- package/dist/file-thumbnail/types.d.ts.map +1 -0
- package/dist/file-thumbnail/utils.d.ts +26 -0
- package/dist/file-thumbnail/utils.d.ts.map +1 -0
- package/dist/index.esm.js +1 -2
- package/dist/index.js +1 -2
- package/dist/lib/index.d.ts +29 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/optional-deps.d.ts +13 -0
- package/dist/lib/optional-deps.d.ts.map +1 -0
- package/dist/upload/Upload.d.ts +5 -0
- package/dist/upload/Upload.d.ts.map +1 -0
- package/dist/upload/errors/RejectionFiles.d.ts +8 -0
- package/dist/upload/errors/RejectionFiles.d.ts.map +1 -0
- package/dist/upload/index.d.ts +6 -0
- package/dist/upload/index.d.ts.map +1 -0
- package/dist/upload/preview/MultiFilePreview.d.ts +11 -0
- package/dist/upload/preview/MultiFilePreview.d.ts.map +1 -0
- package/dist/upload/preview/SingleFilePreview.d.ts +9 -0
- package/dist/upload/preview/SingleFilePreview.d.ts.map +1 -0
- package/dist/upload/types.d.ts +40 -0
- package/dist/upload/types.d.ts.map +1 -0
- package/package.json +22 -16
- package/src/App.css +38 -0
- package/src/App.test.tsx +9 -0
- package/src/App.tsx +166 -0
- package/src/Form/AppAutoCompleter.tsx +252 -0
- package/src/Form/AppCheckBox.tsx +101 -0
- package/src/Form/AppDateAndTimePicker.tsx +94 -0
- package/src/Form/AppDatePicker.tsx +69 -0
- package/src/Form/AppFormErrorMessage.tsx +34 -0
- package/src/Form/AppInputField.tsx +80 -0
- package/src/Form/AppMultiSelector.tsx +163 -0
- package/src/Form/AppPhoneNoInput.tsx +106 -0
- package/src/Form/AppRadioGroup.tsx +92 -0
- package/src/Form/AppRating.tsx +98 -0
- package/src/Form/AppSelectInput.tsx +249 -0
- package/src/Form/AppSimpleUploadFile.tsx +154 -0
- package/src/Form/AppSwitch.tsx +84 -0
- package/src/Form/AppTagsCreator.tsx +252 -0
- package/src/Form/AppTextArea.tsx +90 -0
- package/src/Form/AppUploadFile.tsx +167 -0
- package/src/Form/SubmitButton.tsx +122 -0
- package/src/Form/index.tsx +27 -0
- package/src/assets/illustrations/BackgroundIllustration.tsx +42 -0
- package/src/assets/illustrations/UploadIllustration.tsx +659 -0
- package/src/assets/illustrations/index.ts +1 -0
- package/src/file-thumbnail/types.ts +7 -0
- package/src/file-thumbnail/utils.ts +162 -0
- package/src/index.css +9 -0
- package/src/index.tsx +19 -0
- package/src/lib/index.ts +47 -0
- package/src/logo.svg +1 -0
- package/src/react-app-env.d.ts +1 -0
- package/src/reportWebVitals.ts +15 -0
- package/src/setupTests.ts +5 -0
- package/src/styles/PhoneInputCustom.css +238 -0
- package/src/styles/compiled-tailwind.css +1 -0
- package/src/upload/Upload.tsx +162 -0
- package/src/upload/errors/RejectionFiles.tsx +49 -0
- package/src/upload/index.ts +5 -0
- package/src/upload/preview/MultiFilePreview.tsx +297 -0
- package/src/upload/preview/SingleFilePreview.tsx +81 -0
- package/src/upload/types.ts +51 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { useFormikContext, FormikValues } from "formik";
|
|
5
|
+
import type { UploadProps } from "../upload";
|
|
6
|
+
import { Upload } from "../upload";
|
|
7
|
+
|
|
8
|
+
interface FileWithPreview {
|
|
9
|
+
file?: File;
|
|
10
|
+
preview?: string;
|
|
11
|
+
name: string;
|
|
12
|
+
size: number;
|
|
13
|
+
type: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const base64toBlob = ({
|
|
17
|
+
b64Data,
|
|
18
|
+
sliceSize = 512,
|
|
19
|
+
forcedMimeType,
|
|
20
|
+
}: {
|
|
21
|
+
b64Data: string;
|
|
22
|
+
sliceSize?: number;
|
|
23
|
+
forcedMimeType?: string;
|
|
24
|
+
}): Blob => {
|
|
25
|
+
const mimeType =
|
|
26
|
+
forcedMimeType ??
|
|
27
|
+
b64Data.match(/[^:]\w+\/[\w-+.]+(?=;|,)/)?.[0] ??
|
|
28
|
+
"application/octet-stream";
|
|
29
|
+
|
|
30
|
+
const base64 = b64Data.replace(/^data:.*,/, "");
|
|
31
|
+
const byteCharacters = atob(base64);
|
|
32
|
+
const byteArrays: Uint8Array[] = [];
|
|
33
|
+
|
|
34
|
+
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
|
35
|
+
const slice = byteCharacters.slice(offset, offset + sliceSize);
|
|
36
|
+
const byteNumbers = new Array(slice.length);
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < slice.length; i++) {
|
|
39
|
+
byteNumbers[i] = slice.charCodeAt(i);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
byteArrays.push(new Uint8Array(byteNumbers));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return new Blob(byteArrays as BlobPart[], { type: mimeType });
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const blobToBase64 = (file: File): Promise<string> => {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const reader = new FileReader();
|
|
51
|
+
reader.onloadend = () => resolve(reader.result as string);
|
|
52
|
+
reader.onerror = reject;
|
|
53
|
+
reader.readAsDataURL(file);
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
interface AppUploadFileProps
|
|
58
|
+
extends Omit<UploadProps, "file" | "files" | "disabled"> {
|
|
59
|
+
name: string;
|
|
60
|
+
label?: string;
|
|
61
|
+
className?: string;
|
|
62
|
+
multiple?: boolean;
|
|
63
|
+
maxFiles?: number;
|
|
64
|
+
disabled?: boolean;
|
|
65
|
+
onFilesChange?: (files: File[] | File | null) => void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const AppUploadFile: React.FC<AppUploadFileProps> = ({
|
|
69
|
+
name,
|
|
70
|
+
className = "",
|
|
71
|
+
disabled: propDisabled,
|
|
72
|
+
onFilesChange,
|
|
73
|
+
...rest
|
|
74
|
+
}) => {
|
|
75
|
+
const { errors, touched, setFieldValue, values } =
|
|
76
|
+
useFormikContext<FormikValues>();
|
|
77
|
+
|
|
78
|
+
const maxSizeInBytes = rest.maxSize ? rest.maxSize * 1024 : undefined;
|
|
79
|
+
|
|
80
|
+
const fieldError = (errors as any)[name];
|
|
81
|
+
const isTouched = (touched as any)[name];
|
|
82
|
+
const val = (values as any)[name];
|
|
83
|
+
|
|
84
|
+
const isMaxFilesReached =
|
|
85
|
+
rest.maxFiles != null &&
|
|
86
|
+
(Array.isArray(val) ? val.length : val ? 1 : 0) >= (rest.maxFiles || 0);
|
|
87
|
+
|
|
88
|
+
const isDisabled = propDisabled || isMaxFilesReached;
|
|
89
|
+
|
|
90
|
+
const handleFiles = async (files: File[]) => {
|
|
91
|
+
const processed = await Promise.all(
|
|
92
|
+
files.map(async (file) => ({
|
|
93
|
+
file,
|
|
94
|
+
name: file.name,
|
|
95
|
+
size: file.size,
|
|
96
|
+
type: file.type,
|
|
97
|
+
preview: await blobToBase64(file),
|
|
98
|
+
}))
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
if (rest.multiple) {
|
|
102
|
+
const current = Array.isArray(val) ? val : [];
|
|
103
|
+
if (current.length + processed.length > (rest.maxFiles || Infinity)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const newFiles = [...current, ...processed];
|
|
107
|
+
setFieldValue(name, newFiles, true);
|
|
108
|
+
onFilesChange?.(newFiles);
|
|
109
|
+
} else {
|
|
110
|
+
const newFile = processed[0] || null; // <-- Changed from processed[0]?.file to processed[0]
|
|
111
|
+
setFieldValue(name, newFile, true);
|
|
112
|
+
onFilesChange?.(newFile as any);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const handleDelete = () => {
|
|
117
|
+
const newValue = rest.multiple ? [] : null;
|
|
118
|
+
setFieldValue(name, newValue, true);
|
|
119
|
+
onFilesChange?.(newValue);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const handleRemove = (fileToRemove: File | string | FileWithPreview) => {
|
|
123
|
+
if (rest.multiple) {
|
|
124
|
+
const current = Array.isArray(val) ? val : [];
|
|
125
|
+
const updated = current.filter((f: any) => {
|
|
126
|
+
if (f?.file && f.file === fileToRemove) return false;
|
|
127
|
+
if (f === fileToRemove) return false;
|
|
128
|
+
if (f?.preview === fileToRemove) return false;
|
|
129
|
+
return true;
|
|
130
|
+
});
|
|
131
|
+
setFieldValue(name, updated, true);
|
|
132
|
+
onFilesChange?.(updated);
|
|
133
|
+
} else {
|
|
134
|
+
setFieldValue(name, null, true);
|
|
135
|
+
onFilesChange?.(null);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<div className={`w-full space-y-1 ${className}`}>
|
|
141
|
+
<div className={isDisabled ? "opacity-50" : ""}>
|
|
142
|
+
<Upload
|
|
143
|
+
{...rest}
|
|
144
|
+
maxSize={maxSizeInBytes}
|
|
145
|
+
file={!rest.multiple ? val || undefined : undefined}
|
|
146
|
+
files={rest.multiple ? (Array.isArray(val) ? val : []) : undefined}
|
|
147
|
+
error={Boolean(fieldError) && Boolean(isTouched)}
|
|
148
|
+
onDrop={handleFiles}
|
|
149
|
+
onDelete={handleDelete}
|
|
150
|
+
onRemove={handleRemove}
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
{Boolean(fieldError) && Boolean(isTouched) && (
|
|
155
|
+
<p className="text-xs text-red-500">{fieldError}</p>
|
|
156
|
+
)}
|
|
157
|
+
|
|
158
|
+
{rest.maxFiles != null && (
|
|
159
|
+
<p className="text-xs text-gray-500">
|
|
160
|
+
Maximum {rest.maxFiles} file{rest.maxFiles > 1 ? "s" : ""} allowed
|
|
161
|
+
</p>
|
|
162
|
+
)}
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export default AppUploadFile;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { useFormikContext } from "formik";
|
|
5
|
+
import clsx from "clsx";
|
|
6
|
+
|
|
7
|
+
interface SubmitButtonProps
|
|
8
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
icon?: React.ReactNode;
|
|
11
|
+
variant?:
|
|
12
|
+
| "default"
|
|
13
|
+
| "primary"
|
|
14
|
+
| "secondary"
|
|
15
|
+
| "danger"
|
|
16
|
+
| "ghost"
|
|
17
|
+
| "outline";
|
|
18
|
+
size?: "sm" | "md" | "lg";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const SubmitButton = React.forwardRef<HTMLButtonElement, SubmitButtonProps>(
|
|
22
|
+
(
|
|
23
|
+
{
|
|
24
|
+
loading = false,
|
|
25
|
+
icon,
|
|
26
|
+
variant = "primary",
|
|
27
|
+
size = "md",
|
|
28
|
+
className = "",
|
|
29
|
+
children,
|
|
30
|
+
disabled,
|
|
31
|
+
...props
|
|
32
|
+
},
|
|
33
|
+
ref
|
|
34
|
+
) => {
|
|
35
|
+
const { isSubmitting } = useFormikContext();
|
|
36
|
+
|
|
37
|
+
// Use manual loading prop OR Formik's isSubmitting
|
|
38
|
+
const showLoadingState = loading || isSubmitting;
|
|
39
|
+
const isButtonDisabled = disabled || showLoadingState;
|
|
40
|
+
|
|
41
|
+
const variantStyles = {
|
|
42
|
+
default:
|
|
43
|
+
"bg-gray-800 text-white hover:bg-gray-700 focus:ring-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600",
|
|
44
|
+
primary:
|
|
45
|
+
"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-300 dark:bg-blue-700 dark:hover:bg-blue-600",
|
|
46
|
+
secondary:
|
|
47
|
+
"bg-gray-200 text-gray-800 hover:bg-gray-300 focus:ring-gray-400 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600",
|
|
48
|
+
danger:
|
|
49
|
+
"bg-red-600 text-white hover:bg-red-700 focus:ring-red-300 dark:bg-red-700 dark:hover:bg-red-600",
|
|
50
|
+
ghost:
|
|
51
|
+
"bg-transparent text-gray-700 hover:bg-gray-100 focus:ring-gray-300 dark:text-gray-300 dark:hover:bg-gray-800",
|
|
52
|
+
outline:
|
|
53
|
+
"border-2 border-blue-600 text-blue-600 hover:bg-blue-50 focus:ring-blue-300 dark:border-blue-500 dark:text-blue-500 dark:hover:bg-blue-900/30",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const sizeStyles = {
|
|
57
|
+
sm: "px-3 py-1.5 text-sm h-9",
|
|
58
|
+
md: "px-4 py-2.5 text-base h-11",
|
|
59
|
+
lg: "px-6 py-3 text-lg h-14",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const defaultLoadingIcon = (
|
|
63
|
+
<svg
|
|
64
|
+
className="animate-spin h-5 w-5"
|
|
65
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
66
|
+
fill="none"
|
|
67
|
+
viewBox="0 0 24 24"
|
|
68
|
+
>
|
|
69
|
+
<circle
|
|
70
|
+
className="opacity-25"
|
|
71
|
+
cx="12"
|
|
72
|
+
cy="12"
|
|
73
|
+
r="10"
|
|
74
|
+
stroke="currentColor"
|
|
75
|
+
strokeWidth="4"
|
|
76
|
+
/>
|
|
77
|
+
<path
|
|
78
|
+
className="opacity-75"
|
|
79
|
+
fill="currentColor"
|
|
80
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
81
|
+
/>
|
|
82
|
+
</svg>
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<button
|
|
87
|
+
ref={ref}
|
|
88
|
+
type="submit" // This is IMPORTANT - let Formik handle submission
|
|
89
|
+
disabled={isButtonDisabled}
|
|
90
|
+
className={clsx(
|
|
91
|
+
"inline-flex items-center justify-center font-medium rounded-lg",
|
|
92
|
+
"transition-all duration-200 ease-in-out",
|
|
93
|
+
"focus:outline-none focus:ring-2 focus:ring-offset-2 dark:focus:ring-offset-gray-900",
|
|
94
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
95
|
+
"active:scale-[0.98]",
|
|
96
|
+
"whitespace-nowrap",
|
|
97
|
+
variantStyles[variant],
|
|
98
|
+
sizeStyles[size],
|
|
99
|
+
showLoadingState && "cursor-wait",
|
|
100
|
+
className
|
|
101
|
+
)}
|
|
102
|
+
aria-busy={showLoadingState}
|
|
103
|
+
aria-disabled={isButtonDisabled}
|
|
104
|
+
{...props}
|
|
105
|
+
>
|
|
106
|
+
<span className="flex items-center justify-center gap-2">
|
|
107
|
+
{showLoadingState && (
|
|
108
|
+
<span className="flex-shrink-0">{defaultLoadingIcon}</span>
|
|
109
|
+
)}
|
|
110
|
+
{!showLoadingState && icon && (
|
|
111
|
+
<span className="flex-shrink-0">{icon}</span>
|
|
112
|
+
)}
|
|
113
|
+
<span>{children}</span>
|
|
114
|
+
</span>
|
|
115
|
+
</button>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
SubmitButton.displayName = "SubmitButton";
|
|
121
|
+
|
|
122
|
+
export default SubmitButton;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import type { ReactNode, ReactElement } from "react";
|
|
3
|
+
import type { FormikValues, FormikProps } from "formik";
|
|
4
|
+
import { Formik, Form as FormikForm, FormikConfig } from "formik";
|
|
5
|
+
|
|
6
|
+
export interface FormProps<T> extends FormikConfig<T> {
|
|
7
|
+
children?: ((props: FormikProps<T>) => ReactNode) | ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const Form = <T extends FormikValues>({
|
|
12
|
+
children,
|
|
13
|
+
className,
|
|
14
|
+
...props
|
|
15
|
+
}: FormProps<T>): ReactElement => {
|
|
16
|
+
return (
|
|
17
|
+
<Formik<T> {...props}>
|
|
18
|
+
{(formikProps: FormikProps<T>) => (
|
|
19
|
+
<FormikForm className={className}>
|
|
20
|
+
{typeof children === "function" ? children(formikProps) : children}
|
|
21
|
+
</FormikForm>
|
|
22
|
+
)}
|
|
23
|
+
</Formik>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default Form;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React, { memo } from "react";
|
|
2
|
+
|
|
3
|
+
interface BackgroundIllustrationProps {
|
|
4
|
+
primaryColor?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const BackgroundIllustration: React.FC<BackgroundIllustrationProps> = ({
|
|
8
|
+
primaryColor = "#6366F1",
|
|
9
|
+
}) => {
|
|
10
|
+
return (
|
|
11
|
+
<svg
|
|
12
|
+
width="100%"
|
|
13
|
+
height="100%"
|
|
14
|
+
viewBox="0 0 480 360"
|
|
15
|
+
fill="none"
|
|
16
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
17
|
+
className="absolute inset-0"
|
|
18
|
+
>
|
|
19
|
+
<defs>
|
|
20
|
+
<linearGradient
|
|
21
|
+
id="BG"
|
|
22
|
+
x1="19.496%"
|
|
23
|
+
x2="77.479%"
|
|
24
|
+
y1="71.822%"
|
|
25
|
+
y2="16.69%"
|
|
26
|
+
>
|
|
27
|
+
<stop offset="0%" stopColor={primaryColor} />
|
|
28
|
+
<stop offset="100%" stopColor={primaryColor} stopOpacity="0" />
|
|
29
|
+
</linearGradient>
|
|
30
|
+
</defs>
|
|
31
|
+
|
|
32
|
+
<path
|
|
33
|
+
fill="url(#BG)"
|
|
34
|
+
fillRule="nonzero"
|
|
35
|
+
d="M0 198.78c0 41.458 14.945 79.236 39.539 107.786 28.214 32.765 69.128 53.365 114.734 53.434a148.44 148.44 0 0056.495-11.036c9.051-3.699 19.182-3.274 27.948 1.107a75.779 75.779 0 0033.957 8.01c5.023 0 9.942-.494 14.7-1.433 13.58-2.67 25.94-8.99 36.09-17.94 6.378-5.627 14.547-8.456 22.897-8.446h.142c27.589 0 53.215-8.732 74.492-23.696 19.021-13.36 34.554-31.696 44.904-53.224C474.92 234.58 480 213.388 480 190.958c0-76.93-59.774-139.305-133.498-139.305-7.516 0-14.88.663-22.063 1.899C305.418 21.42 271.355 0 232.499 0a103.651 103.651 0 00-45.88 10.661c-13.24 6.487-25.011 15.705-34.64 26.939-32.698.544-62.931 11.69-87.676 30.291C25.351 97.155 0 144.882 0 198.781z"
|
|
36
|
+
opacity="0.2"
|
|
37
|
+
/>
|
|
38
|
+
</svg>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default memo(BackgroundIllustration);
|