mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
added SortableAudioGrid
This commit is contained in:
parent
586ebb7480
commit
47a6e02c21
14 changed files with 175 additions and 131 deletions
|
@ -1,14 +1,10 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
|
||||
import { SortEnd } from 'react-sortable-hoc';
|
||||
|
||||
import { SortableAudioGrid } from '~/components/editors/SortableAudioGrid';
|
||||
import { SortableImageGrid } from '~/components/sortable';
|
||||
import { SortableAudioGrid, SortableImageGrid } from '~/components/sortable';
|
||||
import { COMMENT_FILE_TYPES } from '~/constants/uploads';
|
||||
import { useFileDropZone } from '~/hooks';
|
||||
import { IFile } from '~/types';
|
||||
import { useUploaderContext } from '~/utils/context/UploaderContextProvider';
|
||||
import { moveArrItem } from '~/utils/fn';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
@ -37,15 +33,8 @@ const CommentFormAttaches: FC = () => {
|
|||
);
|
||||
|
||||
const onAudioMove = useCallback(
|
||||
({ oldIndex, newIndex }: SortEnd) => {
|
||||
setFiles([
|
||||
...filesImages,
|
||||
...(moveArrItem(
|
||||
oldIndex,
|
||||
newIndex,
|
||||
filesAudios.filter(file => !!file)
|
||||
) as IFile[]),
|
||||
]);
|
||||
(newFiles: IFile[]) => {
|
||||
setFiles([...filesImages, ...newFiles]);
|
||||
},
|
||||
[setFiles, filesImages, filesAudios]
|
||||
);
|
||||
|
@ -88,10 +77,7 @@ const CommentFormAttaches: FC = () => {
|
|||
onDelete={onFileDelete}
|
||||
onTitleChange={onAudioTitleChange}
|
||||
onSortEnd={onAudioMove}
|
||||
axis="y"
|
||||
locked={pendingAudios}
|
||||
pressDelay={50}
|
||||
helperClass={styles.helper}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,3 @@
|
|||
.attaches {
|
||||
@include outer_shadow();
|
||||
}
|
||||
|
||||
.helper {
|
||||
z-index: 10000 !important;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,8 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
|
||||
import { SortEnd } from 'react-sortable-hoc';
|
||||
|
||||
import { SortableAudioGrid } from '~/components/editors/SortableAudioGrid';
|
||||
import { useWindowSize } from '~/hooks/dom/useWindowSize';
|
||||
import { SortableAudioGrid } from '~/components/sortable';
|
||||
import { UploadStatus } from '~/store/uploader/UploaderStore';
|
||||
import { IFile } from '~/types';
|
||||
import { moveArrItem } from '~/utils/fn';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
||||
interface IProps {
|
||||
files: IFile[];
|
||||
|
@ -18,17 +11,9 @@ interface IProps {
|
|||
}
|
||||
|
||||
const AudioGrid: FC<IProps> = ({ files, setFiles, locked }) => {
|
||||
const { innerWidth } = useWindowSize();
|
||||
|
||||
const onMove = useCallback(
|
||||
({ oldIndex, newIndex }: SortEnd) => {
|
||||
setFiles(
|
||||
moveArrItem(
|
||||
oldIndex,
|
||||
newIndex,
|
||||
files.filter(file => !!file)
|
||||
) as IFile[]
|
||||
);
|
||||
(newFiles: IFile[]) => {
|
||||
setFiles(newFiles);
|
||||
},
|
||||
[setFiles, files]
|
||||
);
|
||||
|
@ -56,11 +41,8 @@ const AudioGrid: FC<IProps> = ({ files, setFiles, locked }) => {
|
|||
onDelete={onDrop}
|
||||
onTitleChange={onTitleChange}
|
||||
onSortEnd={onMove}
|
||||
axis="xy"
|
||||
items={files}
|
||||
locked={locked}
|
||||
pressDelay={innerWidth < 768 ? 200 : 0}
|
||||
helperClass={styles.helper}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
@import "src/styles/variables";
|
||||
|
||||
.helper {
|
||||
opacity: 0.5;
|
||||
z-index: 10 !important;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import { SortableContainer } from 'react-sortable-hoc';
|
||||
|
||||
import { SortableAudioGridItem } from '~/components/editors/SortableAudioGridItem';
|
||||
import { AudioPlayer } from '~/components/media/AudioPlayer';
|
||||
import { AudioUpload } from '~/components/upload/AudioUpload';
|
||||
import { UploadStatus } from '~/store/uploader/UploaderStore';
|
||||
import { IFile } from '~/types';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
const SortableAudioGrid = SortableContainer(
|
||||
({
|
||||
items,
|
||||
locked,
|
||||
onDelete,
|
||||
onTitleChange,
|
||||
}: {
|
||||
items: IFile[];
|
||||
locked: UploadStatus[];
|
||||
onDelete: (file_id: IFile['id']) => void;
|
||||
onTitleChange: (file_id: IFile['id'], title: string) => void;
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles.grid}>
|
||||
{items
|
||||
.filter(file => file && file.id)
|
||||
.map((file, index) => (
|
||||
<SortableAudioGridItem key={file.id} index={index} collection={0}>
|
||||
<AudioPlayer
|
||||
file={file}
|
||||
onDelete={onDelete}
|
||||
onTitleChange={onTitleChange}
|
||||
isEditing
|
||||
/>
|
||||
</SortableAudioGridItem>
|
||||
))}
|
||||
|
||||
{locked.map((item, index) => (
|
||||
<SortableAudioGridItem key={item.id} index={index} collection={1} disabled>
|
||||
<AudioUpload title={item.name} progress={item.progress} is_uploading />
|
||||
</SortableAudioGridItem>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export { SortableAudioGrid };
|
57
src/components/sortable/SortableAudioGrid/index.tsx
Normal file
57
src/components/sortable/SortableAudioGrid/index.tsx
Normal file
|
@ -0,0 +1,57 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
|
||||
import { AudioPlayer } from '~/components/media/AudioPlayer';
|
||||
import { AudioUpload } from '~/components/upload/AudioUpload';
|
||||
import { UploadStatus } from '~/store/uploader/UploaderStore';
|
||||
import { IFile } from '~/types';
|
||||
|
||||
import { SortableList } from '../SortableList';
|
||||
|
||||
type OnSortEnd = (newValue: IFile[]) => void;
|
||||
|
||||
interface SortableAudioGridProps {
|
||||
onSortEnd: OnSortEnd;
|
||||
items: IFile[];
|
||||
locked: UploadStatus[];
|
||||
className?: string;
|
||||
onDelete: (file_id: IFile['id']) => void;
|
||||
onTitleChange: (file_id: IFile['id'], title: string) => void;
|
||||
}
|
||||
|
||||
const SortableAudioGrid: FC<SortableAudioGridProps> = ({
|
||||
items,
|
||||
locked,
|
||||
onDelete,
|
||||
className,
|
||||
onSortEnd,
|
||||
onTitleChange,
|
||||
}) => {
|
||||
const renderItem = useCallback<FC<{ item: IFile }>>(
|
||||
({ item }) => (
|
||||
<AudioPlayer file={item} onDelete={onDelete} isEditing onTitleChange={onTitleChange} />
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
const renderLocked = useCallback<FC<{ locked: UploadStatus }>>(
|
||||
({ locked }) => (
|
||||
<AudioUpload id={locked.id} is_uploading title={locked.name} progress={locked.progress} />
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<SortableList
|
||||
items={items}
|
||||
locked={locked}
|
||||
getID={it => it.id}
|
||||
getLockedID={it => it.id}
|
||||
renderItem={renderItem}
|
||||
renderLocked={renderLocked}
|
||||
onSortEnd={onSortEnd}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export { SortableAudioGrid };
|
|
@ -1,27 +1,16 @@
|
|||
import React, { createElement, FC, useCallback, useMemo, useState } from 'react';
|
||||
import React, { createElement, FC, useMemo } from "react";
|
||||
|
||||
import {
|
||||
closestCenter,
|
||||
DndContext,
|
||||
DragOverlay,
|
||||
DragStartEvent,
|
||||
MouseSensor,
|
||||
TouchSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
} from '@dnd-kit/core';
|
||||
import { DragEndEvent } from '@dnd-kit/core/dist/types';
|
||||
import { rectSortingStrategy, SortableContext } from '@dnd-kit/sortable';
|
||||
import classNames from 'classnames';
|
||||
import { closestCenter, DndContext, DragOverlay } from "@dnd-kit/core";
|
||||
import { rectSortingStrategy, SortableContext } from "@dnd-kit/sortable";
|
||||
import classNames from "classnames";
|
||||
|
||||
import { DragOverlayItem } from '~/components/sortable/DragOverlayItem';
|
||||
import { useSortableActions } from '~/hooks/sortable';
|
||||
import { moveArrItem } from '~/utils/fn';
|
||||
import { DivProps } from '~/utils/types';
|
||||
import { DragOverlayItem } from "~/components/sortable/DragOverlayItem";
|
||||
import { useSortableActions } from "~/hooks/sortable";
|
||||
import { DivProps } from "~/utils/types";
|
||||
|
||||
import { SortableImageGridItem } from '../SortableGridItem';
|
||||
import { SortableItem } from "../SortableItem";
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
import styles from "./styles.module.scss";
|
||||
|
||||
interface SortableGridProps<T extends {}, R extends {}> {
|
||||
items: T[];
|
||||
|
@ -70,7 +59,7 @@ const SortableGrid = <T, R>({
|
|||
<SortableContext items={ids} strategy={rectSortingStrategy}>
|
||||
<div className={classNames(styles.grid, className)} style={gridStyle}>
|
||||
{items.map(item => (
|
||||
<SortableImageGridItem
|
||||
<SortableItem
|
||||
key={getID(item)}
|
||||
id={getID(item)}
|
||||
className={
|
||||
|
@ -78,7 +67,7 @@ const SortableGrid = <T, R>({
|
|||
}
|
||||
>
|
||||
{createElement(renderItem, { item })}
|
||||
</SortableImageGridItem>
|
||||
</SortableItem>
|
||||
))}
|
||||
|
||||
{locked.map(item =>
|
||||
|
|
|
@ -12,7 +12,7 @@ interface SortableImageGridItemProps {
|
|||
className?: string;
|
||||
}
|
||||
|
||||
const SortableImageGridItem: FC<SortableImageGridItemProps> = ({
|
||||
const SortableItem: FC<SortableImageGridItemProps> = ({
|
||||
children,
|
||||
id,
|
||||
disabled = false,
|
||||
|
@ -41,4 +41,4 @@ const SortableImageGridItem: FC<SortableImageGridItemProps> = ({
|
|||
);
|
||||
};
|
||||
|
||||
export { SortableImageGridItem };
|
||||
export { SortableItem };
|
76
src/components/sortable/SortableList/index.tsx
Normal file
76
src/components/sortable/SortableList/index.tsx
Normal file
|
@ -0,0 +1,76 @@
|
|||
import React, { createElement, FC } from 'react';
|
||||
|
||||
import { closestCenter, DndContext, DragOverlay } from '@dnd-kit/core';
|
||||
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { DragOverlayItem } from '~/components/sortable/DragOverlayItem';
|
||||
import { SortableItem } from '~/components/sortable/SortableItem';
|
||||
import { useSortableActions } from '~/hooks/sortable';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface SortableListProps<T extends {}, R extends {}> {
|
||||
items: T[];
|
||||
locked: R[];
|
||||
getID: (item: T) => number | string;
|
||||
getLockedID: (locked: R) => number | string;
|
||||
renderItem: FC<{ item: T }>;
|
||||
renderLocked: FC<{ locked: R }>;
|
||||
onSortEnd: (newVal: T[]) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const SortableList = <T, R>({
|
||||
items,
|
||||
locked,
|
||||
getID,
|
||||
getLockedID,
|
||||
className,
|
||||
renderItem,
|
||||
renderLocked,
|
||||
onSortEnd,
|
||||
}: SortableListProps<T, R>) => {
|
||||
const { sensors, onDragEnd, onDragStart, draggingItem, ids } = useSortableActions(
|
||||
items,
|
||||
getID,
|
||||
onSortEnd
|
||||
);
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={onDragEnd}
|
||||
onDragStart={onDragStart}
|
||||
>
|
||||
<SortableContext items={ids} strategy={verticalListSortingStrategy}>
|
||||
<div className={classNames(styles.grid, className)}>
|
||||
{items.map(item => (
|
||||
<SortableItem
|
||||
key={getID(item)}
|
||||
id={getID(item)}
|
||||
className={
|
||||
draggingItem && getID(item) === getID(draggingItem) ? styles.dragging : undefined
|
||||
}
|
||||
>
|
||||
{createElement(renderItem, { item })}
|
||||
</SortableItem>
|
||||
))}
|
||||
|
||||
{locked.map(item =>
|
||||
createElement(renderLocked, { locked: item, key: getLockedID(item) })
|
||||
)}
|
||||
|
||||
<DragOverlay>
|
||||
{draggingItem ? (
|
||||
<DragOverlayItem>{createElement(renderItem, { item: draggingItem })}</DragOverlayItem>
|
||||
) : null}
|
||||
</DragOverlay>
|
||||
</div>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
);
|
||||
};
|
||||
|
||||
export { SortableList };
|
15
src/components/sortable/SortableList/styles.module.scss
Normal file
15
src/components/sortable/SortableList/styles.module.scss
Normal file
|
@ -0,0 +1,15 @@
|
|||
@import "src/styles/variables";
|
||||
|
||||
.grid {
|
||||
box-sizing: border-box;
|
||||
|
||||
display: grid;
|
||||
grid-row-gap: $gap;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto;
|
||||
grid-auto-flow: row;
|
||||
}
|
||||
|
||||
.dragging {
|
||||
opacity: 0.1;
|
||||
}
|
|
@ -1,2 +1,2 @@
|
|||
export * from './SortableImageGrid';
|
||||
export * from './SortableGrid';
|
||||
export * from './SortableAudioGrid';
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import React, { FC, useCallback } from "react";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import { ArcProgress } from '~/components/input/ArcProgress';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { IFile } from '~/types';
|
||||
import { ArcProgress } from "~/components/input/ArcProgress";
|
||||
import { Icon } from "~/components/input/Icon";
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
import styles from "./styles.module.scss";
|
||||
|
||||
interface IProps {
|
||||
id?: IFile['id'];
|
||||
id?: string;
|
||||
title?: string;
|
||||
progress?: number;
|
||||
onDrop?: (file_id: IFile['id']) => void;
|
||||
onDrop?: (file_id: string) => void;
|
||||
|
||||
is_uploading?: boolean;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue