diff --git a/src/components/editors/EditorPanel/index.tsx b/src/components/editors/EditorPanel/index.tsx index 6932ef1d..475b0bb2 100644 --- a/src/components/editors/EditorPanel/index.tsx +++ b/src/components/editors/EditorPanel/index.tsx @@ -1,4 +1,4 @@ -import React, { FC, ChangeEventHandler } from 'react'; +import React, { FC } from 'react'; import * as styles from './styles.scss'; import { INode } from '~/redux/types'; import { EditorUploadButton } from '~/components/editors/EditorUploadButton'; @@ -6,12 +6,13 @@ import { EditorUploadButton } from '~/components/editors/EditorUploadButton'; interface IProps { data: INode; setData: (val: INode) => void; - onUpload: ChangeEventHandler; + temp: string[]; + setTemp: (val: string[]) => void; } -const EditorPanel: FC = ({ onUpload }) => ( +const EditorPanel: FC = ({ data, setData, temp, setTemp }) => (
- +
); diff --git a/src/components/editors/EditorUploadButton/index.tsx b/src/components/editors/EditorUploadButton/index.tsx index 358e938e..e29115d2 100644 --- a/src/components/editors/EditorUploadButton/index.tsx +++ b/src/components/editors/EditorUploadButton/index.tsx @@ -1,21 +1,127 @@ -import React, { FC, ChangeEventHandler } from 'react'; +import React, { FC, useCallback, useEffect, useState } from 'react'; import * as styles from './styles.scss'; import { Icon } from '~/components/input/Icon'; +import { IFileWithUUID, INode, IFile } from '~/redux/types'; +import uuid from 'uuid4'; +import { UPLOAD_SUBJECTS, UPLOAD_TARGETS, UPLOAD_TYPES } from '~/redux/uploads/constants'; +import * as UPLOAD_ACTIONS from '~/redux/uploads/actions'; +import assocPath from 'ramda/es/assocPath'; +import append from 'ramda/es/append'; +import { selectUploads } from '~/redux/uploads/selectors'; +import { connect } from 'react-redux'; -interface IProps { - onUpload?: ChangeEventHandler; -} +const mapStateToProps = state => { + const { statuses, files } = selectUploads(state); -const EditorUploadButton: FC = ({ - onUpload, -}) => ( -
- + return { statuses, files }; +}; -
- +const mapDispatchToProps = { + uploadUploadFiles: UPLOAD_ACTIONS.uploadUploadFiles, +}; + +type IProps = ReturnType & + typeof mapDispatchToProps & { + data: INode; + setData: (val: INode) => void; + temp: string[]; + setTemp: (val: string[]) => void; + }; + +const EditorUploadButtonUnconnected: FC = ({ + data, + setData, + temp, + setTemp, + statuses, + files, + uploadUploadFiles, +}) => { + const eventPreventer = useCallback(event => event.preventDefault(), []); + + const onUpload = useCallback( + (uploads: File[]) => { + const items: IFileWithUUID[] = Array.from(uploads).map( + (file: File): IFileWithUUID => ({ + file, + temp_id: uuid(), + subject: UPLOAD_SUBJECTS.EDITOR, + target: UPLOAD_TARGETS.NODES, + type: UPLOAD_TYPES.IMAGE, + }) + ); + + const temps = items.map(file => file.temp_id); + + setTemp([...temp, ...temps]); + uploadUploadFiles(items); + }, + [setTemp, uploadUploadFiles, temp] + ); + + const onFileAdd = useCallback( + (file: IFile) => { + setData(assocPath(['files'], append(file, data.files), data)); + }, + [data, setData] + ); + + // const onDrop = useCallback( + // (event: React.DragEvent) => { + // event.preventDefault(); + + // if (!event.dataTransfer || !event.dataTransfer.files || !event.dataTransfer.files.length) + // return; + + // onUpload(Array.from(event.dataTransfer.files)); + // }, + // [onUpload] + // ); + + useEffect(() => { + window.addEventListener('dragover', eventPreventer, false); + window.addEventListener('drop', eventPreventer, false); + + return () => { + window.removeEventListener('dragover', eventPreventer, false); + window.removeEventListener('drop', eventPreventer, false); + }; + }, [eventPreventer]); + + useEffect(() => { + Object.entries(statuses).forEach(([id, status]) => { + if (temp.includes(id) && !!status.uuid && files[status.uuid]) { + onFileAdd(files[status.uuid]); + setTemp(temp.filter(el => el !== id)); + } + }); + }, [statuses, files, temp, onFileAdd]); + + const onInputChange = useCallback( + event => { + event.preventDefault(); + + if (!event.target.files || !event.target.files.length) return; + + onUpload(Array.from(event.target.files)); + }, + [onUpload] + ); + + return ( +
+ + +
+ +
-
-); + ); +}; + +const EditorUploadButton = connect( + mapStateToProps, + mapDispatchToProps +)(EditorUploadButtonUnconnected); export { EditorUploadButton }; diff --git a/src/components/editors/ImageEditor/index.tsx b/src/components/editors/ImageEditor/index.tsx index 5eb13eed..aecf0a90 100644 --- a/src/components/editors/ImageEditor/index.tsx +++ b/src/components/editors/ImageEditor/index.tsx @@ -1,10 +1,12 @@ -import React, { FC, ChangeEventHandler, DragEventHandler } from 'react'; +import React, { FC, ChangeEventHandler, DragEventHandler, useCallback } from 'react'; import { connect } from 'react-redux'; import { INode } from '~/redux/types'; import * as UPLOAD_ACTIONS from '~/redux/uploads/actions'; import { selectUploads } from '~/redux/uploads/selectors'; import { ImageGrid } from '~/components/editors/ImageGrid'; import { IUploadStatus } from '~/redux/uploads/reducer'; +import { moveArrItem } from '~/utils/fn'; +import assocPath from 'ramda/es/assocPath'; const mapStateToProps = selectUploads; const mapDispatchToProps = { @@ -21,19 +23,9 @@ type IProps = ReturnType & onInputChange: ChangeEventHandler; }; -const ImageEditorUnconnected: FC = ({ - data, - onFileMove, - onInputChange, - pending_files, -}) => ( - -); +const ImageEditorUnconnected: FC = ({ data, setData, pending_files }) => { + return ; +}; const ImageEditor = connect( mapStateToProps, diff --git a/src/components/editors/ImageGrid/index.tsx b/src/components/editors/ImageGrid/index.tsx index 8b7f5052..7a2ef947 100644 --- a/src/components/editors/ImageGrid/index.tsx +++ b/src/components/editors/ImageGrid/index.tsx @@ -1,16 +1,19 @@ -import React, { FC, useCallback, ChangeEventHandler, DragEventHandler } from 'react'; -import { SortableContainer, SortableElement } from 'react-sortable-hoc'; +import React, { FC, useCallback } from 'react'; +import { SortableContainer, SortableElement, SortEvent, SortEnd } from 'react-sortable-hoc'; import * as styles from './styles.scss'; import { ImageUpload } from '~/components/upload/ImageUpload'; -import { IFile } from '~/redux/types'; +import { IFile, INode } from '~/redux/types'; import { IUploadStatus } from '~/redux/uploads/reducer'; import { getURL } from '~/utils/dom'; +import assocPath from 'ramda/es/assocPath'; +import { moveArrItem } from '~/utils/fn'; interface IProps { - items: IFile[]; + data: INode; + setData: (val: INode) => void; locked: IUploadStatus[]; - onFileMove: (o: number, n: number) => void; - onUpload?: ChangeEventHandler; + // items: IFile[]; + // onFileMove: (o: number, n: number) => void; } const SortableItem = SortableElement(({ children }) => ( @@ -18,14 +21,7 @@ const SortableItem = SortableElement(({ children }) => ( )); const SortableList = SortableContainer( - ({ - items, - locked, - }: { - items: IFile[]; - locked: IUploadStatus[]; - onUpload: ChangeEventHandler; - }) => ( + ({ items, locked }: { items: IFile[]; locked: IUploadStatus[] }) => (
{items.map((file, index) => ( @@ -42,18 +38,20 @@ const SortableList = SortableContainer( ) ); -const ImageGrid: FC = ({ items, locked, onFileMove, onUpload }) => { - const onMove = useCallback(({ oldIndex, newIndex }) => onFileMove(oldIndex, newIndex), [ - onFileMove, - ]); +const ImageGrid: FC = ({ data, setData, locked }) => { + const onMove = useCallback( + ({ oldIndex, newIndex }: SortEnd) => { + setData(assocPath(['files'], moveArrItem(oldIndex, newIndex, data.files), data)); + }, + [data, setData] + ); return ( diff --git a/src/containers/dialogs/EditorDialog/index.tsx b/src/containers/dialogs/EditorDialog/index.tsx index c9332066..8b0a8a53 100644 --- a/src/containers/dialogs/EditorDialog/index.tsx +++ b/src/containers/dialogs/EditorDialog/index.tsx @@ -14,12 +14,8 @@ import * as styles from './styles.scss'; import { selectNode } from '~/redux/node/selectors'; import { ImageEditor } from '~/components/editors/ImageEditor'; import { EditorPanel } from '~/components/editors/EditorPanel'; -import { moveArrItem } from '~/utils/fn'; -import { IFile, IFileWithUUID } from '~/redux/types'; -import * as UPLOAD_ACTIONS from '~/redux/uploads/actions'; import * as NODE_ACTIONS from '~/redux/node/actions'; import { selectUploads } from '~/redux/uploads/selectors'; -import { UPLOAD_TARGETS, UPLOAD_TYPES, UPLOAD_SUBJECTS } from '~/redux/uploads/constants'; const mapStateToProps = state => { const { editor } = selectNode(state); @@ -29,101 +25,15 @@ const mapStateToProps = state => { }; const mapDispatchToProps = { - uploadUploadFiles: UPLOAD_ACTIONS.uploadUploadFiles, nodeSave: NODE_ACTIONS.nodeSave, }; type IProps = IDialogProps & ReturnType & typeof mapDispatchToProps & {}; -const EditorDialogUnconnected: FC = ({ - onRequestClose, - editor, - files, - statuses, - - uploadUploadFiles, - nodeSave, -}) => { +const EditorDialogUnconnected: FC = ({ onRequestClose, editor, statuses, nodeSave }) => { const [data, setData] = useState(editor); - const eventPreventer = useCallback(event => event.preventDefault(), []); const [temp, setTemp] = useState([]); - const onUpload = useCallback( - (uploads: File[]) => { - const items: IFileWithUUID[] = Array.from(uploads).map( - (file: File): IFileWithUUID => ({ - file, - temp_id: uuid(), - subject: UPLOAD_SUBJECTS.EDITOR, - target: UPLOAD_TARGETS.NODES, - type: UPLOAD_TYPES.IMAGE, - }) - ); - - const temps = items.map(file => file.temp_id); - - setTemp([...temp, ...temps]); - uploadUploadFiles(items); - }, - [setTemp, uploadUploadFiles, temp] - ); - - const onFileMove = useCallback( - (old_index: number, new_index: number) => { - setData(assocPath(['files'], moveArrItem(old_index, new_index, data.files), data)); - }, - [data, setData] - ); - - const onFileAdd = useCallback( - (file: IFile) => { - setData(assocPath(['files'], append(file, data.files), data)); - }, - [data, setData] - ); - - const onDrop = useCallback( - (event: React.DragEvent) => { - event.preventDefault(); - - if (!event.dataTransfer || !event.dataTransfer.files || !event.dataTransfer.files.length) - return; - - onUpload(Array.from(event.dataTransfer.files)); - }, - [onUpload] - ); - - const onInputChange = useCallback( - event => { - event.preventDefault(); - - if (!event.target.files || !event.target.files.length) return; - - onUpload(Array.from(event.target.files)); - }, - [onUpload] - ); - - useEffect(() => { - window.addEventListener('dragover', eventPreventer, false); - window.addEventListener('drop', eventPreventer, false); - - return () => { - window.removeEventListener('dragover', eventPreventer, false); - window.removeEventListener('drop', eventPreventer, false); - }; - }, [eventPreventer]); - - useEffect(() => { - Object.entries(statuses).forEach(([id, status]) => { - if (temp.includes(id) && !!status.uuid && files[status.uuid]) { - onFileAdd(files[status.uuid]); - setTemp(temp.filter(el => el !== id)); - } - }); - }, [statuses, files, temp, onFileAdd]); - const setTitle = useCallback( title => { setData({ ...data, title }); @@ -141,7 +51,7 @@ const EditorDialogUnconnected: FC = ({ const buttons = ( - + @@ -156,14 +66,11 @@ const EditorDialogUnconnected: FC = ({ return (
-
+
!!statuses[id]).map(id => statuses[id])} setData={setData} - onUpload={onInputChange} - onFileMove={onFileMove} - onInputChange={onInputChange} />