From 7d1524fbcab5cf48036b1c9d1817098c4bdb99b2 Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Thu, 14 Sep 2023 12:50:28 -0300 Subject: [PATCH] refactor: FilesDropTarget -> TS (#30254) Co-authored-by: Aleksander Nicacio da Silva <6494543+aleksandernsilva@users.noreply.github.com> --- .../src/components/FilesDropTarget/index.js | 107 --------------- .../src/components/FilesDropTarget/index.tsx | 122 ++++++++++++++++++ .../components/FilesDropTarget/stories.tsx | 6 +- .../livechat/src/routes/Chat/component.js | 8 +- 4 files changed, 130 insertions(+), 113 deletions(-) delete mode 100644 packages/livechat/src/components/FilesDropTarget/index.js create mode 100644 packages/livechat/src/components/FilesDropTarget/index.tsx diff --git a/packages/livechat/src/components/FilesDropTarget/index.js b/packages/livechat/src/components/FilesDropTarget/index.js deleted file mode 100644 index 589cdd8ecde9..000000000000 --- a/packages/livechat/src/components/FilesDropTarget/index.js +++ /dev/null @@ -1,107 +0,0 @@ -import { Component } from 'preact'; - -import { createClassName } from '../../helpers/createClassName'; -import styles from './styles.scss'; - -const escapeForRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - -export class FilesDropTarget extends Component { - state = { - dragLevel: 0, - }; - - handleInputRef = (ref) => { - this.input = ref; - }; - - handleDragOver = (event) => { - event.preventDefault(); - }; - - handleDragEnter = (event) => { - event.preventDefault(); - this.setState({ dragLevel: this.state.dragLevel + 1 }); - }; - - handleDragLeave = (event) => { - event.preventDefault(); - this.setState({ dragLevel: this.state.dragLevel - 1 }); - }; - - handleDrop = (event) => { - event.preventDefault(); - - let { dragLevel } = this.state; - if (dragLevel === 0) { - return; - } - - dragLevel = 0; - this.setState({ dragLevel }); - - this.handleUpload(event.dataTransfer.files); - }; - - handleInputChange = (event) => { - this.handleUpload(event.currentTarget.files); - }; - - handleUpload = (files) => { - const { accept, multiple, onUpload } = this.props; - - if (!onUpload) { - return; - } - - let filteredFiles = Array.from(files); - - if (accept) { - const acceptMatchers = accept.split(',').map((acceptString) => { - if (acceptString.charAt(0) === '.') { - return ({ name }) => new RegExp(`${escapeForRegExp(acceptString)}$`, 'i').test(name); - } - - const matchTypeOnly = /^(.+)\/\*$/i.exec(acceptString); - if (matchTypeOnly) { - return ({ type }) => new RegExp(`^${escapeForRegExp(matchTypeOnly[1])}/.*$`, 'i').test(type); - } - - return ({ type }) => new RegExp(`^s${escapeForRegExp(acceptString)}$`, 'i').test(type); - }); - - filteredFiles = filteredFiles.filter((file) => acceptMatchers.some((acceptMatcher) => acceptMatcher(file))); - } - - if (!multiple) { - filteredFiles = filteredFiles.slice(0, 1); - } - - filteredFiles.length && onUpload(filteredFiles); - }; - - browse = () => { - this.input.click(); - }; - - render = ({ overlayed, overlayText, accept, multiple, className, style = {}, children }, { dragLevel }) => ( -
0 }, [className])} - style={style} - > - - {children} -
- ); -} diff --git a/packages/livechat/src/components/FilesDropTarget/index.tsx b/packages/livechat/src/components/FilesDropTarget/index.tsx new file mode 100644 index 000000000000..3e9935c37565 --- /dev/null +++ b/packages/livechat/src/components/FilesDropTarget/index.tsx @@ -0,0 +1,122 @@ +import type { ComponentChildren, Ref } from 'preact'; +import type { TargetedEvent } from 'preact/compat'; +import { useState } from 'preact/hooks'; +import type { JSXInternal } from 'preact/src/jsx'; + +import { createClassName } from '../../helpers/createClassName'; +import styles from './styles.scss'; + +const escapeForRegExp = (string: string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + +type FilesDropTargetProps = { + overlayed?: boolean; + overlayText?: string; + accept?: string; + multiple?: boolean; + className?: string; + style?: JSXInternal.CSSProperties; + children?: ComponentChildren; + inputRef?: Ref; + onUpload?: (files: File[]) => void; +}; + +export const FilesDropTarget = ({ + overlayed, + overlayText, + accept, + multiple, + className, + style = {}, + children, + inputRef, + onUpload, +}: FilesDropTargetProps) => { + const [dragLevel, setDragLevel] = useState(0); + + const handleDragOver = (event: DragEvent) => { + event.preventDefault(); + }; + + const handleDragEnter = (event: DragEvent) => { + event.preventDefault(); + setDragLevel(dragLevel + 1); + }; + + const handleDragLeave = (event: DragEvent) => { + event.preventDefault(); + setDragLevel(dragLevel - 1); + }; + + const handleDrop = (event: DragEvent) => { + event.preventDefault(); + + if (dragLevel === 0 || !event?.dataTransfer?.files?.length) { + return; + } + + setDragLevel(0); + + handleUpload(event?.dataTransfer?.files); + }; + + const handleInputChange = (event: TargetedEvent) => { + if (!event?.currentTarget?.files?.length) { + return; + } + + handleUpload(event.currentTarget.files); + }; + + const handleUpload = (files: FileList) => { + if (!onUpload) { + return; + } + + let filteredFiles = Array.from(files); + + if (accept) { + const acceptMatchers = accept.split(',').map((acceptString) => { + if (acceptString.charAt(0) === '.') { + return ({ name }: { name: string }) => new RegExp(`${escapeForRegExp(acceptString)}$`, 'i').test(name); + } + + const matchTypeOnly = /^(.+)\/\*$/i.exec(acceptString); + if (matchTypeOnly) { + return ({ type }: { type: string }) => new RegExp(`^${escapeForRegExp(matchTypeOnly[1])}/.*$`, 'i').test(type); + } + + return ({ type }: { type: string }) => new RegExp(`^s${escapeForRegExp(acceptString)}$`, 'i').test(type); + }); + + filteredFiles = filteredFiles.filter((file) => acceptMatchers.some((acceptMatcher) => acceptMatcher(file))); + } + + if (!multiple) { + filteredFiles = filteredFiles.slice(0, 1); + } + + filteredFiles.length && onUpload(filteredFiles); + }; + + return ( +
0 }, [className])} + style={style} + > + + {children} +
+ ); +}; diff --git a/packages/livechat/src/components/FilesDropTarget/stories.tsx b/packages/livechat/src/components/FilesDropTarget/stories.tsx index e1a01cbaea09..d687fbaad492 100644 --- a/packages/livechat/src/components/FilesDropTarget/stories.tsx +++ b/packages/livechat/src/components/FilesDropTarget/stories.tsx @@ -71,7 +71,7 @@ AcceptingMultipleFiles.args = { export const TriggeringBrowseAction = Template.bind({}); TriggeringBrowseAction.storyName = 'triggering browse action'; -const ref = createRef(); +const inputRef = createRef(); TriggeringBrowseAction.args = { children: (
- +
), - ref, + inputRef, }; diff --git a/packages/livechat/src/routes/Chat/component.js b/packages/livechat/src/routes/Chat/component.js index 98991ab72b53..8bd9ac468c6e 100644 --- a/packages/livechat/src/routes/Chat/component.js +++ b/packages/livechat/src/routes/Chat/component.js @@ -1,4 +1,4 @@ -import { Component } from 'preact'; +import { Component, createRef } from 'preact'; import { Suspense, lazy } from 'preact/compat'; import { withTranslation } from 'react-i18next'; @@ -35,6 +35,8 @@ class Chat extends Component { emojiPickerActive: false, }; + inputRef = createRef(null); + handleFilesDropTargetRef = (ref) => { this.filesDropTarget = ref; }; @@ -61,7 +63,7 @@ class Chat extends Component { handleUploadClick = (event) => { event.preventDefault(); - this.filesDropTarget.browse(); + this.inputRef?.current?.click(); }; handleSendClick = (event) => { @@ -151,7 +153,7 @@ class Chat extends Component { handleEmojiClick={this.handleEmojiClick} {...props} > - + {incomingCallAlert && !!incomingCallAlert.show && } {incomingCallAlert?.show && ongoingCall && ongoingCall.callStatus === CallStatus.IN_PROGRESS_SAME_TAB ? (