diff --git a/cspell.json b/cspell.json index 55343033878..2b38c484f68 100644 --- a/cspell.json +++ b/cspell.json @@ -123,6 +123,7 @@ "creditamount", "CREDS", "crios", + "csvg", "customairshipextender", "customfield", "customise", diff --git a/src/components/DropZone/DropZoneUI.tsx b/src/components/DropZone/DropZoneUI.tsx index ac5dcd56f91..734b1348093 100644 --- a/src/components/DropZone/DropZoneUI.tsx +++ b/src/components/DropZone/DropZoneUI.tsx @@ -20,19 +20,19 @@ type DropZoneUIProps = { dropTextStyles?: StyleProp; /** Custom styles for the inner wrapper of the drop zone */ - dropInnerWrapperStyles?: StyleProp; + dashedBorderStyles?: StyleProp; /** Custom styles for the drop wrapper */ dropWrapperStyles?: StyleProp; }; -function DropZoneUI({icon, dropTitle, dropStyles, dropTextStyles, dropWrapperStyles, dropInnerWrapperStyles}: DropZoneUIProps) { +function DropZoneUI({icon, dropTitle, dropStyles, dropTextStyles, dropWrapperStyles, dashedBorderStyles}: DropZoneUIProps) { const styles = useThemeStyles(); return ( - + {dropTitle} + diff --git a/src/components/DropZone/DualDropZone.tsx b/src/components/DropZone/DualDropZone.tsx index 1845b863c89..b09607cd1f4 100644 --- a/src/components/DropZone/DualDropZone.tsx +++ b/src/components/DropZone/DualDropZone.tsx @@ -4,6 +4,7 @@ import DragAndDropConsumer from '@components/DragAndDrop/Consumer'; import * as Expensicons from '@components/Icon/Expensicons'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DropZoneUI from './DropZoneUI'; import DropZoneWrapper from './DropZoneWrapper'; @@ -26,6 +27,7 @@ function DualDropZone({isEditing, onAttachmentDrop, onReceiptDrop, shouldAcceptS const styles = useThemeStyles(); const {translate} = useLocalize(); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); + const theme = useTheme(); const shouldStackVertically = shouldUseNarrowLayout || isMediumScreenWidth; const scanReceiptsText = shouldAcceptSingleReceipt ? 'dropzone.addReceipt' : 'dropzone.scanReceipts'; @@ -40,8 +42,8 @@ function DualDropZone({isEditing, onAttachmentDrop, onReceiptDrop, shouldAcceptS dropTitle={translate('dropzone.addAttachments')} dropStyles={styles.attachmentDropOverlay(isDraggingOver)} dropTextStyles={styles.attachmentDropText} - dropInnerWrapperStyles={styles.attachmentDropInnerWrapper(isDraggingOver)} dropWrapperStyles={shouldStackVertically ? styles.pb0 : styles.pr0} + dashedBorderStyles={styles.activeDropzoneDashedBorder(theme.attachmentDropBorderColorActive, isDraggingOver)} /> )} @@ -52,7 +54,7 @@ function DualDropZone({isEditing, onAttachmentDrop, onReceiptDrop, shouldAcceptS dropTitle={translate(isEditing ? 'dropzone.replaceReceipt' : scanReceiptsText)} dropStyles={styles.receiptDropOverlay(isDraggingOver)} dropTextStyles={styles.receiptDropText} - dropInnerWrapperStyles={styles.receiptDropInnerWrapper(isDraggingOver)} + dashedBorderStyles={styles.activeDropzoneDashedBorder(theme.receiptDropBorderColorActive, isDraggingOver)} /> )} diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index a78ed7089ec..01fdc3357b7 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -510,8 +510,8 @@ function SearchPage({route}: SearchPageProps) { dropTitle={translate('dropzone.scanReceipts')} dropStyles={styles.receiptDropOverlay(true)} dropTextStyles={styles.receiptDropText} - dropInnerWrapperStyles={styles.receiptDropInnerWrapper(true)} dropWrapperStyles={{marginBottom: variables.bottomTabHeight}} + dashedBorderStyles={styles.activeDropzoneDashedBorder(theme.receiptDropBorderColorActive, true)} /> {ErrorModal} @@ -599,7 +599,7 @@ function SearchPage({route}: SearchPageProps) { dropTitle={translate('dropzone.scanReceipts')} dropStyles={styles.receiptDropOverlay(true)} dropTextStyles={styles.receiptDropText} - dropInnerWrapperStyles={styles.receiptDropInnerWrapper(true)} + dashedBorderStyles={styles.activeDropzoneDashedBorder(theme.receiptDropBorderColorActive, true)} /> diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 3110b6fbd05..f4518511bf2 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -28,6 +28,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; @@ -137,6 +138,7 @@ function ReportActionCompose({ }: ReportActionComposeProps) { const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const styles = useThemeStyles(); + const theme = useTheme(); const {translate} = useLocalize(); // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth, isMediumScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); @@ -678,7 +680,7 @@ function ReportActionCompose({ dropTitle={translate('dropzone.addAttachments')} dropStyles={styles.attachmentDropOverlay(true)} dropTextStyles={styles.attachmentDropText} - dropInnerWrapperStyles={styles.attachmentDropInnerWrapper(true)} + dashedBorderStyles={styles.activeDropzoneDashedBorder(theme.attachmentDropBorderColorActive, true)} /> )} diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 8761c2eec79..c1f09ec4361 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -19,6 +19,7 @@ import useFilesValidation from '@hooks/useFilesValidation'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useThreeDotsAnchorPosition from '@hooks/useThreeDotsAnchorPosition'; import {completeTestDriveTask} from '@libs/actions/Task'; @@ -141,6 +142,7 @@ function IOURequestStepConfirmation({ const policyCategories = policyCategoriesReal ?? policyCategoriesDraft; const styles = useThemeStyles(); + const theme = useTheme(); const {translate} = useLocalize(); const threeDotsAnchorPosition = useThreeDotsAnchorPosition(styles.threeDotsPopoverOffsetNoCloseButton); const {isOffline} = useNetwork(); @@ -1055,7 +1057,7 @@ function IOURequestStepConfirmation({ dropStyles={styles.receiptDropOverlay(true)} dropTitle={translate(isEditingReceipt ? 'dropzone.replaceReceipt' : 'quickAction.scanReceipt')} dropTextStyles={styles.receiptDropText} - dropInnerWrapperStyles={styles.receiptDropInnerWrapper(true)} + dashedBorderStyles={styles.activeDropzoneDashedBorder(theme.receiptDropBorderColorActive, true)} /> {ErrorModal} diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 8f0cb77b248..f6896b0ecb6 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -1034,7 +1034,7 @@ function IOURequestStepScan({ dropStyles={styles.receiptDropOverlay(true)} dropTitle={isReplacingReceipt ? translate('dropzone.replaceReceipt') : translate(shouldAcceptMultipleFiles ? 'dropzone.scanReceipts' : 'quickAction.scanReceipt')} dropTextStyles={styles.receiptDropText} - dropInnerWrapperStyles={styles.receiptDropInnerWrapper(true)} + dashedBorderStyles={styles.activeDropzoneDashedBorder(theme.receiptDropBorderColorActive, true)} /> {/* We use isMobile() here to explicitly hide DownloadAppBanner component on both mobile web and native apps */} diff --git a/src/styles/index.ts b/src/styles/index.ts index 9c9e40df311..0ca88973234 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -13,6 +13,7 @@ import type {ValueOf} from 'type-fest'; import type DotLottieAnimation from '@components/LottieAnimations/types'; import {ACTIVE_LABEL_SCALE} from '@components/TextInput/styleConst'; import {getBrowser, isMobile, isMobileSafari, isSafari} from '@libs/Browser'; +import getPlatform from '@libs/getPlatform'; import CONST from '@src/CONST'; import {defaultTheme} from './theme'; import colors from './theme/colors'; @@ -3768,6 +3769,35 @@ const styles = (theme: ThemeColors) => backgroundColor: theme.fileDropUIBG, }, + activeDropzoneDashedBorder: (borderColor: string, isActive: boolean) => { + const browser = getBrowser(); + const isSafariOrChromeBrowser = getPlatform() === CONST.PLATFORM.WEB && (browser === CONST.BROWSER.SAFARI || browser === CONST.BROWSER.CHROME); + + return { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + width: '100%', + height: '100%', + opacity: isActive ? 1 : 0, + transition: 'opacity 0.2s ease-in', + ...(isSafariOrChromeBrowser && { + backgroundImage: `url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect x='1' y='1' width='calc(100%25 - 3px)' height='calc(100%25 - 3px)' fill='none' stroke='${encodeURIComponent(borderColor)}' stroke-width='2' stroke-dasharray='8' stroke-dashoffset='4 8' stroke-linecap='round' rx='8' ry='8' /%3e%3c/svg%3e")`, + backgroundSize: '100% 100%', + backgroundRepeat: 'no-repeat', + }), + // fallback for the desktop and other browsers that this svg doesn't work with + ...(!isSafariOrChromeBrowser && { + borderWidth: 2, + borderStyle: 'dashed', + borderColor, + borderRadius: variables.componentBorderRadiusNormal, + }), + }; + }, + attachmentDropOverlay: (isActive?: boolean) => ({ backgroundColor: isActive ? theme.attachmentDropUIBGActive : theme.attachmentDropUIBG, transition: 'background-color 0.2s ease-in', @@ -3777,11 +3807,6 @@ const styles = (theme: ThemeColors) => color: theme.textAttachmentDropZone, }, - attachmentDropInnerWrapper: (isActive?: boolean) => ({ - borderColor: isActive ? theme.attachmentDropBorderColorActive : theme.attachmentDropBorderColor, - transition: '0.2s ease-in', - }), - receiptDropOverlay: (isActive?: boolean) => ({ backgroundColor: isActive ? theme.receiptDropUIBGActive : theme.receiptDropUIBG, transition: 'background-color 0.2s ease-in', @@ -3791,11 +3816,6 @@ const styles = (theme: ThemeColors) => color: theme.textReceiptDropZone, }, - receiptDropInnerWrapper: (isActive?: boolean) => ({ - borderColor: isActive ? theme.receiptDropBorderColorActive : theme.receiptDropBorderColor, - transition: '0.2s ease-in', - }), - flashButtonContainer: { position: 'absolute', top: 20, @@ -5031,6 +5051,7 @@ const styles = (theme: ThemeColors) => paddingVertical: 20, flexGrow: 1, flexShrink: 1, + flexBasis: '35%', }, diff --git a/src/styles/theme/themes/dark.ts b/src/styles/theme/themes/dark.ts index b97189b6698..672f271e0ca 100644 --- a/src/styles/theme/themes/dark.ts +++ b/src/styles/theme/themes/dark.ts @@ -74,11 +74,9 @@ const darkTheme = { fileDropUIBG: 'rgba(3, 212, 124, 0.84)', attachmentDropUIBG: 'rgba(90, 176, 255, 0.8)', attachmentDropUIBGActive: 'rgba(90, 176, 255, 96)', - attachmentDropBorderColor: 'rgba(176, 217, 255, 0)', attachmentDropBorderColorActive: colors.blue100, receiptDropUIBG: 'rgba(3, 212, 124, 0.8)', receiptDropUIBGActive: 'rgba(3, 212, 124, 96)', - receiptDropBorderColor: 'rgba(177, 242, 214, 0)', receiptDropBorderColorActive: colors.green100, checkBox: colors.green400, imageCropBackgroundColor: colors.productDark700, diff --git a/src/styles/theme/themes/light.ts b/src/styles/theme/themes/light.ts index 9896a568ef3..359d09be456 100644 --- a/src/styles/theme/themes/light.ts +++ b/src/styles/theme/themes/light.ts @@ -74,11 +74,9 @@ const lightTheme = { fileDropUIBG: 'rgba(3, 212, 124, 0.84)', attachmentDropUIBG: 'rgba(90, 176, 255, 0.8)', attachmentDropUIBGActive: 'rgba(90, 176, 255, 0.96)', - attachmentDropBorderColor: 'rgba(176, 217, 255, 0)', attachmentDropBorderColorActive: colors.blue100, receiptDropUIBG: 'rgba(3, 212, 124, 0.8)', receiptDropUIBGActive: 'rgba(3, 212, 124, 0.96)', - receiptDropBorderColor: 'rgba(177, 242, 214, 0)', receiptDropBorderColorActive: colors.green100, checkBox: colors.green400, imageCropBackgroundColor: colors.productLight700, diff --git a/src/styles/theme/types.ts b/src/styles/theme/types.ts index f5153b88424..a7ffe98f6bd 100644 --- a/src/styles/theme/types.ts +++ b/src/styles/theme/types.ts @@ -79,11 +79,9 @@ type ThemeColors = { fileDropUIBG: Color; attachmentDropUIBG: Color; attachmentDropUIBGActive: Color; - attachmentDropBorderColor: Color; attachmentDropBorderColorActive: Color; receiptDropUIBG: Color; receiptDropUIBGActive: Color; - receiptDropBorderColor: Color; receiptDropBorderColorActive: Color; checkBox: Color; imageCropBackgroundColor: Color;