feat: drag and drop pictures
This commit is contained in:
parent
da63609101
commit
637c5f9cd5
|
|
@ -76,9 +76,9 @@ export class MediaController {
|
|||
const name = upload.Location.split('/').pop();
|
||||
|
||||
// @ts-ignore
|
||||
await this._mediaService.saveFile(org.id, name, upload.Location);
|
||||
const saveFile = await this._mediaService.saveFile(org.id, name, upload.Location);
|
||||
|
||||
res.status(200).json(upload);
|
||||
res.status(200).json({...upload, saved: saveFile});
|
||||
// const filePath =
|
||||
// file.path.indexOf('http') === 0
|
||||
// ? file.path
|
||||
|
|
|
|||
|
|
@ -1,7 +1,16 @@
|
|||
'use client';
|
||||
|
||||
import React, {
|
||||
FC, Fragment, MouseEventHandler, useCallback, useEffect, useMemo, useRef, useState
|
||||
ClipboardEventHandler,
|
||||
FC,
|
||||
Fragment,
|
||||
MouseEventHandler,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
ClipboardEvent,
|
||||
useState,
|
||||
} from 'react';
|
||||
import dayjs from 'dayjs';
|
||||
import { Integrations } from '@gitroom/frontend/components/launches/calendar.context';
|
||||
|
|
@ -47,6 +56,9 @@ import { weightedLength } from '@gitroom/helpers/utils/count.length';
|
|||
import { uniqBy } from 'lodash';
|
||||
import { Select } from '@gitroom/react/form/select';
|
||||
import { useClickOutside } from '@gitroom/frontend/components/layout/click.outside';
|
||||
import { useUppyUploader } from '@gitroom/frontend/components/media/new.uploader';
|
||||
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
|
||||
import { DropFiles } from '@gitroom/frontend/components/layout/drop.files';
|
||||
|
||||
function countCharacters(text: string, type: string): number {
|
||||
if (type !== 'x') {
|
||||
|
|
@ -69,6 +81,8 @@ export const AddEditModal: FC<{
|
|||
}> = (props) => {
|
||||
const { date, integrations: ints, reopenModal, mutate, onlyValues } = props;
|
||||
const [customer, setCustomer] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
// selected integrations to allow edit
|
||||
const [selectedIntegrations, setSelectedIntegrations] = useStateCallback<
|
||||
|
|
@ -265,12 +279,14 @@ export const AddEditModal: FC<{
|
|||
const schedule = useCallback(
|
||||
(type: 'draft' | 'now' | 'schedule' | 'delete') => async () => {
|
||||
if (type === 'delete') {
|
||||
setLoading(true);
|
||||
if (
|
||||
!(await deleteDialog(
|
||||
'Are you sure you want to delete this post?',
|
||||
'Yes, delete it!'
|
||||
))
|
||||
) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
await fetch(`/posts/${existingData.group}`, {
|
||||
|
|
@ -341,6 +357,7 @@ export const AddEditModal: FC<{
|
|||
}
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
await fetch('/posts', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
|
|
@ -377,6 +394,68 @@ export const AddEditModal: FC<{
|
|||
]
|
||||
);
|
||||
|
||||
const uppy = useUppyUploader({
|
||||
onUploadSuccess: () => {
|
||||
/**empty**/
|
||||
},
|
||||
allowedFileTypes: 'image/*,video/mp4',
|
||||
});
|
||||
|
||||
const pasteImages = useCallback(
|
||||
(index: number, currentValue: any[], isFile?: boolean) => {
|
||||
return async (event: ClipboardEvent<HTMLDivElement> | File[]) => {
|
||||
// @ts-ignore
|
||||
const clipboardItems = isFile
|
||||
? // @ts-ignore
|
||||
event.map((p) => ({ kind: 'file', getAsFile: () => p }))
|
||||
: // @ts-ignore
|
||||
event.clipboardData?.items; // Ensure clipboardData is available
|
||||
if (!clipboardItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
const files: File[] = [];
|
||||
|
||||
// @ts-ignore
|
||||
for (const item of clipboardItems) {
|
||||
console.log(item);
|
||||
if (item.kind === 'file') {
|
||||
const file = item.getAsFile();
|
||||
if (file) {
|
||||
const isImage = file.type.startsWith('image/');
|
||||
const isVideo = file.type.startsWith('video/');
|
||||
if (isImage || isVideo) {
|
||||
files.push(file); // Collect images or videos
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setUploading(true);
|
||||
const lastValues = [...currentValue];
|
||||
for (const file of files) {
|
||||
uppy.addFile(file);
|
||||
const upload = await uppy.upload();
|
||||
uppy.clear();
|
||||
if (upload?.successful?.length) {
|
||||
lastValues.push(upload?.successful[0]?.response?.body?.saved!);
|
||||
changeImage(index)({
|
||||
target: {
|
||||
name: 'image',
|
||||
value: [...lastValues],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
setUploading(false);
|
||||
};
|
||||
},
|
||||
[changeImage]
|
||||
);
|
||||
|
||||
const getPostsMarketplace = useCallback(async () => {
|
||||
return (
|
||||
await fetch(`/posts/marketplace/${existingData?.posts?.[0]?.id}`)
|
||||
|
|
@ -427,6 +506,11 @@ export const AddEditModal: FC<{
|
|||
'flex flex-col md:flex-row p-[10px] rounded-[4px] bg-primary gap-[20px]'
|
||||
)}
|
||||
>
|
||||
{uploading && (
|
||||
<div className="absolute left-0 top-0 w-full h-full bg-black/40 z-[600] flex justify-center items-center">
|
||||
<LoadingComponent width={100} height={100} />
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={clsx(
|
||||
'flex flex-col gap-[16px] transition-all duration-700 whitespace-nowrap',
|
||||
|
|
@ -534,23 +618,28 @@ export const AddEditModal: FC<{
|
|||
<div>
|
||||
<div className="flex gap-[4px]">
|
||||
<div className="flex-1 editor text-textColor">
|
||||
<Editor
|
||||
order={index}
|
||||
height={value.length > 1 ? 150 : 250}
|
||||
commands={
|
||||
[
|
||||
// ...commands
|
||||
// .getCommands()
|
||||
// .filter((f) => f.name === 'image'),
|
||||
// newImage,
|
||||
// postSelector(dateState),
|
||||
]
|
||||
}
|
||||
value={p.content}
|
||||
preview="edit"
|
||||
// @ts-ignore
|
||||
onChange={changeValue(index)}
|
||||
/>
|
||||
<DropFiles
|
||||
onDrop={pasteImages(index, p.image || [], true)}
|
||||
>
|
||||
<Editor
|
||||
order={index}
|
||||
height={value.length > 1 ? 150 : 250}
|
||||
commands={
|
||||
[
|
||||
// ...commands
|
||||
// .getCommands()
|
||||
// .filter((f) => f.name === 'image'),
|
||||
// newImage,
|
||||
// postSelector(dateState),
|
||||
]
|
||||
}
|
||||
value={p.content}
|
||||
preview="edit"
|
||||
onPaste={pasteImages(index, p.image || [])}
|
||||
// @ts-ignore
|
||||
onChange={changeValue(index)}
|
||||
/>
|
||||
</DropFiles>
|
||||
|
||||
{showError &&
|
||||
(!p.content || p.content.length < 6) && (
|
||||
|
|
@ -649,6 +738,7 @@ export const AddEditModal: FC<{
|
|||
className="rounded-[4px] relative group"
|
||||
disabled={
|
||||
selectedIntegrations.length === 0 ||
|
||||
loading ||
|
||||
!canSendForPublication
|
||||
}
|
||||
>
|
||||
|
|
@ -678,7 +768,11 @@ export const AddEditModal: FC<{
|
|||
</svg>
|
||||
<div
|
||||
onClick={postNow}
|
||||
className="hidden group-hover:flex hover:flex flex-col justify-center absolute left-0 top-[100%] w-full h-[40px] bg-customColor22 border border-tableBorder"
|
||||
className={clsx(
|
||||
'hidden group-hover:flex hover:flex flex-col justify-center absolute left-0 top-[100%] w-full h-[40px] bg-customColor22 border border-tableBorder',
|
||||
loading &&
|
||||
'cursor-not-allowed pointer-events-none opacity-50'
|
||||
)}
|
||||
>
|
||||
Post now
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ export const Editor = forwardRef<
|
|||
)}
|
||||
value={props.value}
|
||||
onChange={(e) => props?.onChange?.(e.target.value)}
|
||||
onPaste={props.onPaste}
|
||||
placeholder="Write your reply..."
|
||||
autosuggestionsConfig={{
|
||||
textareaPurpose: `Assist me in writing social media posts.`,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import React, {
|
|||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
ClipboardEvent,
|
||||
} from 'react';
|
||||
import { Button } from '@gitroom/react/form/button';
|
||||
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
|
||||
|
|
@ -38,6 +39,9 @@ import { AddPostButton } from '@gitroom/frontend/components/launches/add.post.bu
|
|||
import { GeneralPreviewComponent } from '@gitroom/frontend/components/launches/general.preview.component';
|
||||
import { capitalize } from 'lodash';
|
||||
import { useModals } from '@mantine/modals';
|
||||
import { useUppyUploader } from '@gitroom/frontend/components/media/new.uploader';
|
||||
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
|
||||
import { DropFiles } from '@gitroom/frontend/components/layout/drop.files';
|
||||
|
||||
// Simple component to change back to settings on after changing tab
|
||||
export const SetTab: FC<{ changeTab: () => void }> = (props) => {
|
||||
|
|
@ -96,6 +100,7 @@ export const withProvider = function <T extends object>(
|
|||
const existingData = useExistingData();
|
||||
const { integration, date } = useIntegration();
|
||||
const [showLinkedinPopUp, setShowLinkedinPopUp] = useState<any>(false);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
useCopilotReadable({
|
||||
description:
|
||||
|
|
@ -276,6 +281,68 @@ export const withProvider = function <T extends object>(
|
|||
[]
|
||||
);
|
||||
|
||||
const uppy = useUppyUploader({
|
||||
onUploadSuccess: () => {
|
||||
/**empty**/
|
||||
},
|
||||
allowedFileTypes: 'image/*,video/mp4',
|
||||
});
|
||||
|
||||
const pasteImages = useCallback(
|
||||
(index: number, currentValue: any[], isFile?: boolean) => {
|
||||
return async (event: ClipboardEvent<HTMLDivElement> | File[]) => {
|
||||
// @ts-ignore
|
||||
const clipboardItems = isFile
|
||||
? // @ts-ignore
|
||||
event.map((p) => ({ kind: 'file', getAsFile: () => p }))
|
||||
: // @ts-ignore
|
||||
event.clipboardData?.items; // Ensure clipboardData is available
|
||||
if (!clipboardItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
const files: File[] = [];
|
||||
|
||||
// @ts-ignore
|
||||
for (const item of clipboardItems) {
|
||||
console.log(item);
|
||||
if (item.kind === 'file') {
|
||||
const file = item.getAsFile();
|
||||
if (file) {
|
||||
const isImage = file.type.startsWith('image/');
|
||||
const isVideo = file.type.startsWith('video/');
|
||||
if (isImage || isVideo) {
|
||||
files.push(file); // Collect images or videos
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setUploading(true);
|
||||
const lastValues = [...currentValue];
|
||||
for (const file of files) {
|
||||
uppy.addFile(file);
|
||||
const upload = await uppy.upload();
|
||||
uppy.clear();
|
||||
if (upload?.successful?.length) {
|
||||
lastValues.push(upload?.successful[0]?.response?.body?.saved!);
|
||||
changeImage(index)({
|
||||
target: {
|
||||
name: 'image',
|
||||
value: [...lastValues],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
setUploading(false);
|
||||
};
|
||||
},
|
||||
[changeImage]
|
||||
);
|
||||
|
||||
// this is a trick to prevent the data from being deleted, yet we don't render the elements
|
||||
if (!props.show) {
|
||||
return null;
|
||||
|
|
@ -329,6 +396,11 @@ export const withProvider = function <T extends object>(
|
|||
{editInPlace &&
|
||||
createPortal(
|
||||
<EditorWrapper>
|
||||
{uploading && (
|
||||
<div className="absolute left-0 top-0 w-full h-full bg-black/40 z-[600] flex justify-center items-center">
|
||||
<LoadingComponent width={100} height={100} />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col gap-[20px]">
|
||||
{!existingData?.integration && (
|
||||
<div className="bg-red-800 text-white">
|
||||
|
|
@ -347,33 +419,36 @@ export const withProvider = function <T extends object>(
|
|||
onClick={tagPersonOrCompany(
|
||||
integration.id,
|
||||
(newValue: string) =>
|
||||
changeValue(index)(
|
||||
val.content + newValue
|
||||
)
|
||||
changeValue(index)(val.content + newValue)
|
||||
)}
|
||||
>
|
||||
Tag a company
|
||||
</Button>
|
||||
)}
|
||||
<Editor
|
||||
order={index}
|
||||
height={InPlaceValue.length > 1 ? 200 : 250}
|
||||
value={val.content}
|
||||
commands={[
|
||||
// ...commands
|
||||
// .getCommands()
|
||||
// .filter((f) => f.name !== 'image'),
|
||||
// newImage,
|
||||
postSelector(date),
|
||||
...linkedinCompany(
|
||||
integration?.identifier!,
|
||||
integration?.id!
|
||||
),
|
||||
]}
|
||||
preview="edit"
|
||||
// @ts-ignore
|
||||
onChange={changeValue(index)}
|
||||
/>
|
||||
<DropFiles
|
||||
onDrop={pasteImages(index, val.image || [], true)}
|
||||
>
|
||||
<Editor
|
||||
order={index}
|
||||
height={InPlaceValue.length > 1 ? 200 : 250}
|
||||
value={val.content}
|
||||
commands={[
|
||||
// ...commands
|
||||
// .getCommands()
|
||||
// .filter((f) => f.name !== 'image'),
|
||||
// newImage,
|
||||
postSelector(date),
|
||||
...linkedinCompany(
|
||||
integration?.identifier!,
|
||||
integration?.id!
|
||||
),
|
||||
]}
|
||||
preview="edit"
|
||||
onPaste={pasteImages(index, val.image || [])}
|
||||
// @ts-ignore
|
||||
onChange={changeValue(index)}
|
||||
/>
|
||||
</DropFiles>
|
||||
{(!val.content || val.content.length < 6) && (
|
||||
<div className="my-[5px] text-customColor19 text-[12px] font-[500]">
|
||||
The post should be at least 6 characters long
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import { useDropzone } from 'react-dropzone';
|
||||
import { FC, ReactNode } from 'react';
|
||||
|
||||
export const DropFiles: FC<{ children: ReactNode, onDrop: (files: File[]) => void }> = (props) => {
|
||||
const { getRootProps, isDragActive } = useDropzone({
|
||||
onDrop: props.onDrop
|
||||
});
|
||||
|
||||
return (
|
||||
<div {...getRootProps()} className="relative">
|
||||
{isDragActive && (
|
||||
<div className="absolute left-0 top-0 w-full h-full bg-black/90 flex items-center justify-center z-[200] animate-normalFadeIn">
|
||||
Drag n drop some files here
|
||||
</div>
|
||||
)}
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
'use client';
|
||||
import ReactLoading from 'react-loading';
|
||||
import { FC } from 'react';
|
||||
|
||||
export const LoadingComponent = () => {
|
||||
return <div className="flex-1 flex justify-center pt-[100px]"><ReactLoading type="spin" color="#fff" width={100} height={100} /></div>;
|
||||
export const LoadingComponent: FC<{width?: number, height?: number}> = (props) => {
|
||||
return (
|
||||
<div className="flex-1 flex justify-center pt-[100px]">
|
||||
<ReactLoading type="spin" color="#fff" width={props.width || 100} height={props.height || 100} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,11 @@ export const MediaBox: FC<{
|
|||
|
||||
const { data, mutate } = useSWR('get-media', loadMedia);
|
||||
|
||||
const finishUpload = useCallback(async () => {
|
||||
const newData = await mutate();
|
||||
setNewMedia(newData.results[0])();
|
||||
}, [mutate, setNewMedia]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.pages) {
|
||||
setPages(data.pages);
|
||||
|
|
@ -127,7 +132,7 @@ export const MediaBox: FC<{
|
|||
>
|
||||
<div className="relative flex gap-2 items-center justify-center">
|
||||
<MultipartFileUploader
|
||||
onUploadSuccess={mutate}
|
||||
onUploadSuccess={finishUpload}
|
||||
allowedFileTypes={
|
||||
type === 'video'
|
||||
? 'video/mp4'
|
||||
|
|
@ -152,7 +157,7 @@ export const MediaBox: FC<{
|
|||
<div>Click the button below to upload one</div>
|
||||
<div className="mt-[10px] justify-center items-center flex flex-col-reverse gap-[10px]">
|
||||
<MultipartFileUploader
|
||||
onUploadSuccess={mutate}
|
||||
onUploadSuccess={finishUpload}
|
||||
allowedFileTypes={
|
||||
type === 'video'
|
||||
? 'video/mp4'
|
||||
|
|
@ -185,7 +190,7 @@ export const MediaBox: FC<{
|
|||
<img
|
||||
className="w-full h-full object-cover"
|
||||
src={mediaDirectory.set(media.path)}
|
||||
alt='media'
|
||||
alt="media"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -215,11 +220,12 @@ export const MultiMediaComponent: FC<{
|
|||
}> = (props) => {
|
||||
const { name, label, error, description, onChange, value } = props;
|
||||
const user = useUser();
|
||||
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
setCurrentMedia(value);
|
||||
}
|
||||
}, []);
|
||||
}, [value]);
|
||||
|
||||
const [modal, setShowModal] = useState(false);
|
||||
const [mediaModal, setMediaModal] = useState(false);
|
||||
|
|
@ -261,7 +267,7 @@ export const MultiMediaComponent: FC<{
|
|||
<>
|
||||
<div className="flex flex-col gap-[8px] bg-input rounded-bl-[8px]">
|
||||
{modal && <MediaBox setMedia={changeMedia} closeModal={showModal} />}
|
||||
{mediaModal && !!user?.tier?.ai && (
|
||||
{mediaModal && !!user?.tier?.ai && (
|
||||
<Polonto setMedia={changeMedia} closeModal={closeDesignModal} />
|
||||
)}
|
||||
<div className="flex gap-[10px]">
|
||||
|
|
@ -285,7 +291,9 @@ export const MultiMediaComponent: FC<{
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-[12px] font-[500] text-primary">Insert Media</div>
|
||||
<div className="text-[12px] font-[500] text-primary">
|
||||
Insert Media
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
|
|
@ -306,7 +314,9 @@ export const MultiMediaComponent: FC<{
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-[12px] font-[500] !text-white">Design Media</div>
|
||||
<div className="text-[12px] font-[500] !text-white">
|
||||
Design Media
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
@ -354,7 +364,8 @@ export const MediaComponent: FC<{
|
|||
width?: number;
|
||||
height?: number;
|
||||
}> = (props) => {
|
||||
const { name, type, label, description, onChange, value, width, height } = props;
|
||||
const { name, type, label, description, onChange, value, width, height } =
|
||||
props;
|
||||
const { getValues } = useSettings();
|
||||
const user = useUser();
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -55,18 +55,16 @@ export function MultipartFileUploader({
|
|||
);
|
||||
}
|
||||
|
||||
export function MultipartFileUploaderAfter({
|
||||
onUploadSuccess,
|
||||
allowedFileTypes,
|
||||
}: {
|
||||
export function useUppyUploader(props: {
|
||||
// @ts-ignore
|
||||
onUploadSuccess: (result: UploadResult) => void;
|
||||
allowedFileTypes: string;
|
||||
}) {
|
||||
const { storageProvider, backendUrl } = useVariables();
|
||||
const { onUploadSuccess, allowedFileTypes } = props;
|
||||
const fetch = useFetch();
|
||||
|
||||
const uppy = useMemo(() => {
|
||||
return useMemo(() => {
|
||||
const uppy2 = new Uppy({
|
||||
autoProceed: true,
|
||||
restrictions: {
|
||||
|
|
@ -86,7 +84,7 @@ export function MultipartFileUploaderAfter({
|
|||
convertTypes: ['image/jpeg'],
|
||||
maxWidth: 1000,
|
||||
maxHeight: 1000,
|
||||
quality: 1
|
||||
quality: 1,
|
||||
});
|
||||
// Set additional metadata when a file is added
|
||||
uppy2.on('file-added', (file) => {
|
||||
|
|
@ -102,9 +100,9 @@ export function MultipartFileUploaderAfter({
|
|||
|
||||
uppy2.on('upload-success', (file, response) => {
|
||||
// @ts-ignore
|
||||
uppy.setFileState(file.id, {
|
||||
uppy2.setFileState(file.id, {
|
||||
// @ts-ignore
|
||||
progress: uppy.getState().files[file.id].progress,
|
||||
progress: uppy2.getState().files[file.id].progress,
|
||||
// @ts-ignore
|
||||
uploadURL: response.body.Location,
|
||||
response: response,
|
||||
|
|
@ -114,6 +112,17 @@ export function MultipartFileUploaderAfter({
|
|||
|
||||
return uppy2;
|
||||
}, []);
|
||||
}
|
||||
|
||||
export function MultipartFileUploaderAfter({
|
||||
onUploadSuccess,
|
||||
allowedFileTypes,
|
||||
}: {
|
||||
// @ts-ignore
|
||||
onUploadSuccess: (result: UploadResult) => void;
|
||||
allowedFileTypes: string;
|
||||
}) {
|
||||
const uppy = useUppyUploader({ onUploadSuccess, allowedFileTypes });
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ module.exports = {
|
|||
},
|
||||
animation: {
|
||||
fade: 'fadeOut 0.5s ease-in-out',
|
||||
normalFadeIn: 'normalFadeIn 0.5s ease-in-out',
|
||||
normalFadeOut: 'normalFadeOut 0.5s linear 5s forwards',
|
||||
overflow: 'overFlow 0.5s ease-in-out forwards',
|
||||
overflowReverse: 'overFlowReverse 0.5s ease-in-out forwards',
|
||||
|
|
@ -120,6 +121,10 @@ module.exports = {
|
|||
'0%': { opacity: 1 },
|
||||
'100%': { opacity: 0 },
|
||||
},
|
||||
normalFadeIn: {
|
||||
'0%': { opacity: 0 },
|
||||
'100%': { opacity: 1 },
|
||||
},
|
||||
overFlow: {
|
||||
'0%': { overflow: 'hidden' },
|
||||
'99%': { overflow: 'hidden' },
|
||||
|
|
@ -131,7 +136,7 @@ module.exports = {
|
|||
'100%': { overflow: 'hidden' },
|
||||
},
|
||||
fadeDown: {
|
||||
'0%': { opacity: 0, marginTop: -30},
|
||||
'0%': { opacity: 0, marginTop: -30 },
|
||||
'10%': { opacity: 1, marginTop: 0 },
|
||||
'85%': { opacity: 1, marginTop: 0 },
|
||||
'90%': { opacity: 1, marginTop: 10 },
|
||||
|
|
@ -144,12 +149,15 @@ module.exports = {
|
|||
newMessages: {
|
||||
'0%': { backgroundColor: 'var(--color-seventh)', fontWeight: 'bold' },
|
||||
'99%': { backgroundColor: 'var(--color-third)', fontWeight: 'bold' },
|
||||
'100%': { backgroundColor: 'var(--color-third)', fontWeight: 'normal' },
|
||||
'100%': {
|
||||
backgroundColor: 'var(--color-third)',
|
||||
fontWeight: 'normal',
|
||||
},
|
||||
},
|
||||
}),
|
||||
screens: {
|
||||
custom: { raw: '(max-height: 800px)' },
|
||||
xs: { max: '401px'} ,
|
||||
xs: { max: '401px' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -81,6 +81,7 @@
|
|||
"@types/mime-types": "^2.1.4",
|
||||
"@types/multer": "^1.4.11",
|
||||
"@types/nodemailer": "^6.4.16",
|
||||
"@types/react-dropzone": "^4.2.2",
|
||||
"@types/remove-markdown": "^0.3.4",
|
||||
"@types/sha256": "^0.2.2",
|
||||
"@types/stripe": "^8.0.417",
|
||||
|
|
@ -139,6 +140,7 @@
|
|||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-dropzone": "^14.3.5",
|
||||
"react-hook-form": "^7.50.1",
|
||||
"react-loading": "^2.0.3",
|
||||
"react-tag-autocomplete": "^7.2.0",
|
||||
|
|
|
|||
Loading…
Reference in New Issue