diff --git a/.github/scripts/findUnusedKeys.sh b/.github/scripts/findUnusedKeys.sh index 77c3ea25326b..1411fffc8389 100755 --- a/.github/scripts/findUnusedKeys.sh +++ b/.github/scripts/findUnusedKeys.sh @@ -6,7 +6,7 @@ LIB_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd ../../ && pwd)" readonly SRC_DIR="${LIB_PATH}/src" readonly STYLES_DIR="${LIB_PATH}/src/styles" -readonly STYLES_FILE="${LIB_PATH}/src/styles/styles.js" +readonly STYLES_FILE="${LIB_PATH}/src/styles/styles.ts" readonly UTILITIES_STYLES_FILE="${LIB_PATH}/src/styles/utilities" readonly STYLES_KEYS_FILE="${LIB_PATH}/scripts/style_keys_list_temp.txt" readonly UTILITY_STYLES_KEYS_FILE="${LIB_PATH}/scripts/utility_keys_list_temp.txt" @@ -210,7 +210,12 @@ find_theme_style_and_store_keys() { fi # Check if we are inside an arrow function - if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ || "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\(.*\)[[:space:]]*'=>' ]]; then + if [[ "$line" =~ ^[[:space:]]*([a-zA-Zgv 0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ || "$line" =~ ^[[:space:]]*([a-zA-Zgv 0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>' ]]; then + inside_arrow_function=true + continue + fi + + if [[ "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\(.*\)[[:space:]]*'=>' ]]; then inside_arrow_function=true continue fi @@ -348,7 +353,7 @@ echo "🔍 Looking for styles." find_utility_styles_store_prefix find_utility_usage_as_styles -# Find and store keys from styles.js +# Find and store keys from styles.ts find_styles_object_and_store_keys "$STYLES_FILE" find_styles_functions_and_store_keys "$STYLES_FILE" collect_theme_keys_from_styles "$STYLES_FILE" diff --git a/src/components/SelectionList/BaseSelectionList.js b/src/components/SelectionList/BaseSelectionList.js index 86ed932ab0d6..fdb1f92ca73b 100644 --- a/src/components/SelectionList/BaseSelectionList.js +++ b/src/components/SelectionList/BaseSelectionList.js @@ -22,8 +22,8 @@ import Log from '../../libs/Log'; import OptionsListSkeletonView from '../OptionsListSkeletonView'; import useActiveElement from '../../hooks/useActiveElement'; import BaseListItem from './BaseListItem'; -import themeColors from '../../styles/themes/default'; import ArrowKeyFocusManager from '../ArrowKeyFocusManager'; +import themeColors from '../../styles/themes/default'; const propTypes = { ...keyboardStatePropTypes, @@ -426,7 +426,7 @@ function BaseSelectionList({ onScrollBeginDrag={onScrollBeginDrag} keyExtractor={(item) => item.keyForList} extraData={focusedIndex} - indicatorStyle={themeColors.selectionListIndicatorColor} + indicatorStyle={themeColors.white} keyboardShouldPersistTaps="always" showsVerticalScrollIndicator={showScrollIndicator} initialNumToRender={12} diff --git a/src/libs/ComposerUtils/updateNumberOfLines/index.native.ts b/src/libs/ComposerUtils/updateNumberOfLines/index.native.ts index b22135b4f767..b5c28cfc79e8 100644 --- a/src/libs/ComposerUtils/updateNumberOfLines/index.native.ts +++ b/src/libs/ComposerUtils/updateNumberOfLines/index.native.ts @@ -8,7 +8,7 @@ import UpdateNumberOfLines from './types'; * divide by line height to get the total number of rows for the textarea. */ const updateNumberOfLines: UpdateNumberOfLines = (props, event) => { - const lineHeight = styles.textInputCompose.lineHeight; + const lineHeight = styles.textInputCompose.lineHeight ?? 0; const paddingTopAndBottom = styles.textInputComposeSpacing.paddingVertical * 2; const inputHeight = event?.nativeEvent?.contentSize?.height ?? null; if (!inputHeight) { diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 3cd89dcf349f..62da2bf3be4b 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -1,20 +1,23 @@ -import {EdgeInsets} from 'react-native-safe-area-context'; -import {Animated, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; import {CSSProperties} from 'react'; +import {Animated, DimensionValue, ImageStyle, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; +import {EdgeInsets} from 'react-native-safe-area-context'; import {ValueOf} from 'type-fest'; import CONST from '../CONST'; +import * as Browser from '../libs/Browser'; +import * as UserUtils from '../libs/UserUtils'; +import colors from './colors'; import fontFamily from './fontFamily'; +import styles from './styles'; import themeColors from './themes/default'; -import variables from './variables'; -import colors from './colors'; +import cursor from './utilities/cursor'; import positioning from './utilities/positioning'; -import styles from './styles'; import spacing from './utilities/spacing'; -import * as UserUtils from '../libs/UserUtils'; -import * as Browser from '../libs/Browser'; -import cursor from './utilities/cursor'; +import variables from './variables'; import {Transaction} from '../types/onyx'; +type AllStyles = ViewStyle | TextStyle | ImageStyle; +type ParsableStyle = AllStyles | ((state: PressableStateCallbackType) => AllStyles); + type ColorValue = ValueOf; type AvatarSizeName = ValueOf; type EReceiptColorName = ValueOf; @@ -40,12 +43,11 @@ type ButtonSizeValue = ValueOf; type EmptyAvatarSizeName = ValueOf>; type ButtonStateName = ValueOf; type AvatarSize = {width: number}; -type ParsableStyle = ViewStyle | CSSProperties | ((state: PressableStateCallbackType) => ViewStyle | CSSProperties); type WorkspaceColorStyle = {backgroundColor: ColorValue; fill: ColorValue}; type EreceiptColorStyle = {backgroundColor: ColorValue; color: ColorValue}; -type ModalPaddingStylesArgs = { +type ModalPaddingStylesParams = { shouldAddBottomSafeAreaMargin: boolean; shouldAddTopSafeAreaMargin: boolean; shouldAddBottomSafeAreaPadding: boolean; @@ -61,13 +63,19 @@ type ModalPaddingStylesArgs = { insets: EdgeInsets; }; -type AvatarBorderStyleArgs = { +type AvatarBorderStyleParams = { isHovered: boolean; isPressed: boolean; isInReportAction: boolean; shouldUseCardBackground: boolean; }; +type GetBaseAutoCompleteSuggestionContainerStyleParams = { + left: number; + bottom: number; + width: number; +}; + const workspaceColorOptions: WorkspaceColorStyle[] = [ {backgroundColor: colors.blue200, fill: colors.blue700}, {backgroundColor: colors.blue400, fill: colors.blue800}, @@ -138,7 +146,7 @@ const avatarSizes: Record = { [CONST.AVATAR_SIZE.SMALL_NORMAL]: variables.avatarSizeSmallNormal, }; -const emptyAvatarStyles: Record = { +const emptyAvatarStyles: Record = { [CONST.AVATAR_SIZE.SMALL]: styles.emptyAvatarSmall, [CONST.AVATAR_SIZE.MEDIUM]: styles.emptyAvatarMedium, [CONST.AVATAR_SIZE.LARGE]: styles.emptyAvatarLarge, @@ -178,21 +186,21 @@ function getAvatarSize(size: AvatarSizeName): number { /** * Return the height of magic code input container */ -function getHeightOfMagicCodeInput(): ViewStyle | CSSProperties { +function getHeightOfMagicCodeInput(): ViewStyle { return {height: styles.magicCodeInputContainer.minHeight - styles.textInputContainer.borderBottomWidth}; } /** * Return the style from an empty avatar size constant */ -function getEmptyAvatarStyle(size: EmptyAvatarSizeName): ViewStyle | CSSProperties | undefined { +function getEmptyAvatarStyle(size: EmptyAvatarSizeName): ViewStyle | undefined { return emptyAvatarStyles[size]; } /** * Return the width style from an avatar size constant */ -function getAvatarWidthStyle(size: AvatarSizeName): ViewStyle | CSSProperties { +function getAvatarWidthStyle(size: AvatarSizeName): ViewStyle { const avatarSize = getAvatarSize(size); return { width: avatarSize, @@ -202,7 +210,7 @@ function getAvatarWidthStyle(size: AvatarSizeName): ViewStyle | CSSProperties { /** * Return the style from an avatar size constant */ -function getAvatarStyle(size: AvatarSizeName): ViewStyle | CSSProperties { +function getAvatarStyle(size: AvatarSizeName): ViewStyle { const avatarSize = getAvatarSize(size); return { height: avatarSize, @@ -215,7 +223,7 @@ function getAvatarStyle(size: AvatarSizeName): ViewStyle | CSSProperties { /** * Get Font size of '+1' text on avatar overlay */ -function getAvatarExtraFontSizeStyle(size: AvatarSizeName): TextStyle | CSSProperties { +function getAvatarExtraFontSizeStyle(size: AvatarSizeName): TextStyle { return { fontSize: avatarFontSizes[size], }; @@ -224,7 +232,7 @@ function getAvatarExtraFontSizeStyle(size: AvatarSizeName): TextStyle | CSSPrope /** * Get Bordersize of Avatar based on avatar size */ -function getAvatarBorderWidth(size: AvatarSizeName): ViewStyle | CSSProperties { +function getAvatarBorderWidth(size: AvatarSizeName): ViewStyle { return { borderWidth: avatarBorderWidths[size], }; @@ -233,7 +241,7 @@ function getAvatarBorderWidth(size: AvatarSizeName): ViewStyle | CSSProperties { /** * Return the border radius for an avatar */ -function getAvatarBorderRadius(size: AvatarSizeName, type: string): ViewStyle | CSSProperties { +function getAvatarBorderRadius(size: AvatarSizeName, type: string): ViewStyle { if (type === CONST.ICON_TYPE_WORKSPACE) { return {borderRadius: avatarBorderSizes[size]}; } @@ -245,7 +253,7 @@ function getAvatarBorderRadius(size: AvatarSizeName, type: string): ViewStyle | /** * Return the border style for an avatar */ -function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle | CSSProperties { +function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle { return { overflow: 'hidden', ...getAvatarBorderRadius(size, type), @@ -255,7 +263,7 @@ function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle | C /** * Helper method to return workspace avatar color styles */ -function getDefaultWorkspaceAvatarColor(workspaceName: string): ViewStyle | CSSProperties { +function getDefaultWorkspaceAvatarColor(workspaceName: string): ViewStyle { const colorHash = UserUtils.hashText(workspaceName.trim(), workspaceColorOptions.length); return workspaceColorOptions[colorHash]; @@ -282,7 +290,7 @@ function getEReceiptColorStyles(colorCode: EReceiptColorName): EreceiptColorStyl /** * Takes safe area insets and returns padding to use for a View */ -function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = variables.safeInsertPercentage): ViewStyle | CSSProperties { +function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = variables.safeInsertPercentage): ViewStyle { return { paddingTop: insets?.top, paddingBottom: (insets?.bottom ?? 0) * insetsPercentage, @@ -294,11 +302,11 @@ function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = vari /** * Takes safe area insets and returns margin to use for a View */ -function getSafeAreaMargins(insets?: EdgeInsets): ViewStyle | CSSProperties { +function getSafeAreaMargins(insets?: EdgeInsets): ViewStyle { return {marginBottom: (insets?.bottom ?? 0) * variables.safeInsertPercentage}; } -function getZoomCursorStyle(isZoomed: boolean, isDragging: boolean): ViewStyle | CSSProperties { +function getZoomCursorStyle(isZoomed: boolean, isDragging: boolean): ViewStyle { if (!isZoomed) { // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) // eslint-disable-next-line @typescript-eslint/no-unsafe-return @@ -309,6 +317,7 @@ function getZoomCursorStyle(isZoomed: boolean, isDragging: boolean): ViewStyle | return isDragging ? styles.cursorGrabbing : styles.cursorZoomOut; } +// NOTE: asserting some web style properties to a valid type, because it isn't possible to augment them. function getZoomSizingStyle( isZoomed: boolean, imgWidth: number, @@ -317,28 +326,28 @@ function getZoomSizingStyle( containerHeight: number, containerWidth: number, isLoading: boolean, -): ViewStyle | CSSProperties | undefined { +): ViewStyle | undefined { // Hide image until finished loading to prevent showing preview with wrong dimensions if (isLoading || imgWidth === 0 || imgHeight === 0) { return undefined; } - const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px`; - const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px`; + const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px` as DimensionValue; + const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px` as DimensionValue; // Return different size and offset style based on zoomScale and isZoom. if (isZoomed) { // When both width and height are smaller than container(modal) size, set the height by multiplying zoomScale if it is zoomed in. if (zoomScale >= 1) { return { - height: `${imgHeight * zoomScale}px`, - width: `${imgWidth * zoomScale}px`, + height: `${imgHeight * zoomScale}px` as DimensionValue, + width: `${imgWidth * zoomScale}px` as DimensionValue, }; } // If image height and width are bigger than container size, display image with original size because original size is bigger and position absolute. return { - height: `${imgHeight}px`, - width: `${imgWidth}px`, + height: `${imgHeight}px` as DimensionValue, + width: `${imgWidth}px` as DimensionValue, top, left, }; @@ -347,8 +356,8 @@ function getZoomSizingStyle( // If image is not zoomed in and image size is smaller than container size, display with original size based on offset and position absolute. if (zoomScale > 1) { return { - height: `${imgHeight}px`, - width: `${imgWidth}px`, + height: `${imgHeight}px` as DimensionValue, + width: `${imgWidth}px` as DimensionValue, top, left, }; @@ -356,11 +365,11 @@ function getZoomSizingStyle( // If image is bigger than container size, display full image in the screen with scaled size (fit by container size) and position absolute. // top, left offset should be different when displaying long or wide image. - const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px`; - const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px`; + const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px` as DimensionValue; + const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px` as DimensionValue; return { - height: `${imgHeight * zoomScale}px`, - width: `${imgWidth * zoomScale}px`, + height: `${imgHeight * zoomScale}px` as DimensionValue, + width: `${imgWidth * zoomScale}px` as DimensionValue, top: scaledTop, left: scaledLeft, }; @@ -369,7 +378,7 @@ function getZoomSizingStyle( /** * Returns auto grow text input style */ -function getWidthStyle(width: number): ViewStyle | CSSProperties { +function getWidthStyle(width: number): ViewStyle { return { width, }; @@ -378,7 +387,7 @@ function getWidthStyle(width: number): ViewStyle | CSSProperties { /** * Returns auto grow height text input style */ -function getAutoGrowHeightInputStyle(textInputHeight: number, maxHeight: number): ViewStyle | CSSProperties { +function getAutoGrowHeightInputStyle(textInputHeight: number, maxHeight: number): ViewStyle { if (textInputHeight > maxHeight) { // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) // eslint-disable-next-line @typescript-eslint/no-unsafe-return @@ -402,7 +411,7 @@ function getAutoGrowHeightInputStyle(textInputHeight: number, maxHeight: number) /** * Returns a style with backgroundColor and borderColor set to the same color */ -function getBackgroundAndBorderStyle(backgroundColor: string): ViewStyle | CSSProperties { +function getBackgroundAndBorderStyle(backgroundColor: string): ViewStyle { return { backgroundColor, borderColor: backgroundColor, @@ -412,7 +421,7 @@ function getBackgroundAndBorderStyle(backgroundColor: string): ViewStyle | CSSPr /** * Returns a style with the specified backgroundColor */ -function getBackgroundColorStyle(backgroundColor: string): ViewStyle | CSSProperties { +function getBackgroundColorStyle(backgroundColor: string): ViewStyle { return { backgroundColor, }; @@ -421,7 +430,7 @@ function getBackgroundColorStyle(backgroundColor: string): ViewStyle | CSSProper /** * Returns a style for text color */ -function getTextColorStyle(color: string): TextStyle | CSSProperties { +function getTextColorStyle(color: string): TextStyle { return { color, }; @@ -430,7 +439,7 @@ function getTextColorStyle(color: string): TextStyle | CSSProperties { /** * Returns a style with the specified borderColor */ -function getBorderColorStyle(borderColor: string): ViewStyle | CSSProperties { +function getBorderColorStyle(borderColor: string): ViewStyle { return { borderColor, }; @@ -439,7 +448,7 @@ function getBorderColorStyle(borderColor: string): ViewStyle | CSSProperties { /** * Returns the width style for the wordmark logo on the sign in page */ -function getSignInWordmarkWidthStyle(environment: string, isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getSignInWordmarkWidthStyle(environment: string, isSmallScreenWidth: boolean): ViewStyle { if (environment === CONST.ENVIRONMENT.DEV) { return isSmallScreenWidth ? {width: variables.signInLogoWidthPill} : {width: variables.signInLogoWidthLargeScreenPill}; } @@ -471,7 +480,7 @@ function hexadecimalToRGBArray(hexadecimal: string): number[] | undefined { /** * Returns a background color with opacity style */ -function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: number): ViewStyle | CSSProperties { +function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: number): ViewStyle { const result = hexadecimalToRGBArray(backgroundColor); if (result !== undefined) { return { @@ -484,8 +493,8 @@ function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: nu /** * Generate a style for the background color of the Badge */ -function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, isAdHoc = false): ViewStyle | CSSProperties { - if (success) { +function getBadgeColorStyle(isSuccess: boolean, isError: boolean, isPressed = false, isAdHoc = false): ViewStyle { + if (isSuccess) { if (isAdHoc) { // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) // eslint-disable-next-line @typescript-eslint/no-unsafe-return @@ -495,7 +504,7 @@ function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, // eslint-disable-next-line @typescript-eslint/no-unsafe-return return isPressed ? styles.badgeSuccessPressed : styles.badgeSuccess; } - if (error) { + if (isError) { // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) // eslint-disable-next-line @typescript-eslint/no-unsafe-return return isPressed ? styles.badgeDangerPressed : styles.badgeDanger; @@ -509,7 +518,7 @@ function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, * @param buttonState - One of {'default', 'hovered', 'pressed'} * @param isMenuItem - whether this button is apart of a list */ -function getButtonBackgroundColorStyle(buttonState: ButtonStateName = CONST.BUTTON_STATES.DEFAULT, isMenuItem = false): ViewStyle | CSSProperties { +function getButtonBackgroundColorStyle(buttonState: ButtonStateName = CONST.BUTTON_STATES.DEFAULT, isMenuItem = false): ViewStyle { switch (buttonState) { case CONST.BUTTON_STATES.PRESSED: return {backgroundColor: themeColors.buttonPressedBG}; @@ -552,7 +561,7 @@ function getAnimatedFABStyle(rotate: Animated.Value, backgroundColor: Animated.V }; } -function getWidthAndHeightStyle(width: number, height: number | undefined = undefined): ViewStyle | CSSProperties { +function getWidthAndHeightStyle(width: number, height?: number): ViewStyle { return { width, height: height ?? width, @@ -573,17 +582,17 @@ function getModalPaddingStyles({ modalContainerStylePaddingTop, modalContainerStylePaddingBottom, insets, -}: ModalPaddingStylesArgs): ViewStyle | CSSProperties { +}: ModalPaddingStylesParams): ViewStyle { // use fallback value for safeAreaPaddingBottom to keep padding bottom consistent with padding top. // More info: issue #17376 - const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop || 0 : safeAreaPaddingBottom; + const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop ?? 0 : safeAreaPaddingBottom; return { - marginTop: (modalContainerStyleMarginTop || 0) + (shouldAddTopSafeAreaMargin ? safeAreaPaddingTop : 0), - marginBottom: (modalContainerStyleMarginBottom || 0) + (shouldAddBottomSafeAreaMargin ? safeAreaPaddingBottomWithFallback : 0), - paddingTop: shouldAddTopSafeAreaPadding ? (modalContainerStylePaddingTop || 0) + safeAreaPaddingTop : modalContainerStylePaddingTop || 0, - paddingBottom: shouldAddBottomSafeAreaPadding ? (modalContainerStylePaddingBottom || 0) + safeAreaPaddingBottomWithFallback : modalContainerStylePaddingBottom || 0, - paddingLeft: safeAreaPaddingLeft || 0, - paddingRight: safeAreaPaddingRight || 0, + marginTop: (modalContainerStyleMarginTop ?? 0) + (shouldAddTopSafeAreaMargin ? safeAreaPaddingTop : 0), + marginBottom: (modalContainerStyleMarginBottom ?? 0) + (shouldAddBottomSafeAreaMargin ? safeAreaPaddingBottomWithFallback : 0), + paddingTop: shouldAddTopSafeAreaPadding ? (modalContainerStylePaddingTop ?? 0) + safeAreaPaddingTop : modalContainerStylePaddingTop ?? 0, + paddingBottom: shouldAddBottomSafeAreaPadding ? (modalContainerStylePaddingBottom ?? 0) + safeAreaPaddingBottomWithFallback : modalContainerStylePaddingBottom ?? 0, + paddingLeft: safeAreaPaddingLeft ?? 0, + paddingRight: safeAreaPaddingRight ?? 0, }; } @@ -601,7 +610,7 @@ function getFontFamilyMonospace({fontStyle, fontWeight}: TextStyle): string { /** * Gives the width for Emoji picker Widget */ -function getEmojiPickerStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getEmojiPickerStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { width: CONST.SMALL_EMOJI_PICKER_SIZE.WIDTH, @@ -616,7 +625,7 @@ function getEmojiPickerStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProper /** * Generate the styles for the ReportActionItem wrapper view. */ -function getReportActionItemStyle(isHovered = false): ViewStyle | CSSProperties { +function getReportActionItemStyle(isHovered = false): ViewStyle { // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) // eslint-disable-next-line @typescript-eslint/no-unsafe-return return { @@ -634,7 +643,7 @@ function getReportActionItemStyle(isHovered = false): ViewStyle | CSSProperties /** * Generate the wrapper styles for the mini ReportActionContextMenu. */ -function getMiniReportActionContextMenuWrapperStyle(isReportActionItemGrouped: boolean): ViewStyle | CSSProperties { +function getMiniReportActionContextMenuWrapperStyle(isReportActionItemGrouped: boolean): ViewStyle { // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) // eslint-disable-next-line @typescript-eslint/no-unsafe-return return { @@ -646,7 +655,7 @@ function getMiniReportActionContextMenuWrapperStyle(isReportActionItemGrouped: b }; } -function getPaymentMethodMenuWidth(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getPaymentMethodMenuWidth(isSmallScreenWidth: boolean): ViewStyle { const margin = 20; return {width: !isSmallScreenWidth ? variables.sideBarWidth - margin * 2 : undefined}; } @@ -732,14 +741,14 @@ function getThemeBackgroundColor(bgColor: string = themeColors.appBG): string { /** * Parse styleParam and return Styles array */ -function parseStyleAsArray(styleParam: ViewStyle | CSSProperties | Array): Array { +function parseStyleAsArray(styleParam: T | T[]): T[] { return Array.isArray(styleParam) ? styleParam : [styleParam]; } /** * Parse style function and return Styles object */ -function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): Array { +function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): AllStyles[] { const functionAppliedStyle = typeof style === 'function' ? style(state) : style; return parseStyleAsArray(functionAppliedStyle); } @@ -747,8 +756,8 @@ function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallb /** * Receives any number of object or array style objects and returns them all as an array */ -function combineStyles(...allStyles: Array>) { - let finalStyles: Array> = []; +function combineStyles(...allStyles: Array): T[] { + let finalStyles: T[] = []; allStyles.forEach((style) => { finalStyles = finalStyles.concat(parseStyleAsArray(style)); }); @@ -758,7 +767,7 @@ function combineStyles(...allStyles: Array 0 ? -overlapSize : 0, zIndex: index + 2, @@ -853,7 +862,7 @@ function getHorizontalStackedAvatarStyle(index: number, overlapSize: number): Vi /** * Get computed avatar styles of '+1' overlay based on size */ -function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAvatarBorderWidth: number): ViewStyle | CSSProperties { +function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAvatarBorderWidth: number): ViewStyle { return { borderWidth: oneAvatarBorderWidth, borderRadius: oneAvatarSize.width, @@ -863,7 +872,7 @@ function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAv }; } -function getErrorPageContainerStyle(safeAreaPaddingBottom = 0): ViewStyle | CSSProperties { +function getErrorPageContainerStyle(safeAreaPaddingBottom = 0): ViewStyle { return { backgroundColor: themeColors.componentBG, paddingBottom: 40 + safeAreaPaddingBottom, @@ -873,7 +882,7 @@ function getErrorPageContainerStyle(safeAreaPaddingBottom = 0): ViewStyle | CSSP /** * Gets the correct size for the empty state background image based on screen dimensions */ -function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { height: CONST.EMPTY_STATE_BACKGROUND.SMALL_SCREEN.IMAGE_HEIGHT, @@ -892,7 +901,7 @@ function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean): View /** * Gets the correct top margin size for the chat welcome message based on screen dimensions */ -function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { marginTop: CONST.EMPTY_STATE_BACKGROUND.SMALL_SCREEN.VIEW_HEIGHT, @@ -907,7 +916,7 @@ function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean): ViewStyle /** * Returns fontSize style */ -function getFontSizeStyle(fontSize: number): TextStyle | CSSProperties { +function getFontSizeStyle(fontSize: number): TextStyle { return { fontSize, }; @@ -916,7 +925,7 @@ function getFontSizeStyle(fontSize: number): TextStyle | CSSProperties { /** * Returns lineHeight style */ -function getLineHeightStyle(lineHeight: number): TextStyle | CSSProperties { +function getLineHeightStyle(lineHeight: number): TextStyle { return { lineHeight, }; @@ -925,7 +934,7 @@ function getLineHeightStyle(lineHeight: number): TextStyle | CSSProperties { /** * Gets the correct size for the empty state container based on screen dimensions */ -function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { minHeight: CONST.EMPTY_STATE_BACKGROUND.SMALL_SCREEN.CONTAINER_MINHEIGHT, @@ -944,12 +953,12 @@ function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean): ViewStyle /** * Gets styles for AutoCompleteSuggestion row */ -function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHeight: number, hovered: boolean, currentEmojiIndex: number): Array { +function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHeight: number, isHovered: boolean, currentEmojiIndex: number): ViewStyle[] { let backgroundColor; if (currentEmojiIndex === highlightedEmojiIndex) { backgroundColor = themeColors.activeComponentBG; - } else if (hovered) { + } else if (isHovered) { backgroundColor = themeColors.hoverComponentBG; } @@ -969,14 +978,19 @@ function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHe /** * Gets the correct position for the base auto complete suggestion container */ -function getBaseAutoCompleteSuggestionContainerStyle({left, bottom, width}: {left: number; bottom: number; width: number}): ViewStyle | CSSProperties { - return {position: 'fixed', bottom, left, width}; +function getBaseAutoCompleteSuggestionContainerStyle({left, bottom, width}: GetBaseAutoCompleteSuggestionContainerStyleParams): ViewStyle { + return { + ...positioning.pFixed, + bottom, + left, + width, + }; } /** * Gets the correct position for auto complete suggestion container */ -function getAutoCompleteSuggestionContainerStyle(itemsHeight: number): ViewStyle | CSSProperties { +function getAutoCompleteSuggestionContainerStyle(itemsHeight: number): ViewStyle { 'worklet'; const borderWidth = 2; @@ -994,11 +1008,11 @@ function getAutoCompleteSuggestionContainerStyle(itemsHeight: number): ViewStyle /** * Select the correct color for text. */ -function getColoredBackgroundStyle(isColored: boolean): ViewStyle | CSSProperties { +function getColoredBackgroundStyle(isColored: boolean): ViewStyle { return {backgroundColor: isColored ? themeColors.link : undefined}; } -function getEmojiReactionBubbleStyle(isHovered: boolean, hasUserReacted: boolean, isContextMenu = false): ViewStyle | CSSProperties { +function getEmojiReactionBubbleStyle(isHovered: boolean, hasUserReacted: boolean, isContextMenu = false): ViewStyle { let backgroundColor = themeColors.border; if (isHovered) { @@ -1024,7 +1038,7 @@ function getEmojiReactionBubbleStyle(isHovered: boolean, hasUserReacted: boolean }; } -function getEmojiReactionBubbleTextStyle(isContextMenu = false): TextStyle | CSSProperties { +function getEmojiReactionBubbleTextStyle(isContextMenu = false): TextStyle { if (isContextMenu) { return { fontSize: 17, @@ -1038,7 +1052,7 @@ function getEmojiReactionBubbleTextStyle(isContextMenu = false): TextStyle | CSS }; } -function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle | CSSProperties { +function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle { if (hasUserReacted) { return {color: themeColors.reactionActiveText}; } @@ -1051,7 +1065,7 @@ function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle | * * @param direction - The direction of the rotation (CONST.DIRECTION.LEFT or CONST.DIRECTION.RIGHT). */ -function getDirectionStyle(direction: string): ViewStyle | CSSProperties { +function getDirectionStyle(direction: ValueOf): ViewStyle { if (direction === CONST.DIRECTION.LEFT) { return {transform: [{rotate: '180deg'}]}; } @@ -1062,11 +1076,11 @@ function getDirectionStyle(direction: string): ViewStyle | CSSProperties { /** * Returns a style object with display flex or none basing on the condition value. */ -function displayIfTrue(condition: boolean): ViewStyle | CSSProperties { +function displayIfTrue(condition: boolean): ViewStyle { return {display: condition ? 'flex' : 'none'}; } -function getGoogleListViewStyle(shouldDisplayBorder: boolean): ViewStyle | CSSProperties { +function getGoogleListViewStyle(shouldDisplayBorder: boolean): ViewStyle { if (shouldDisplayBorder) { // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) // eslint-disable-next-line @typescript-eslint/no-unsafe-return @@ -1086,7 +1100,7 @@ function getGoogleListViewStyle(shouldDisplayBorder: boolean): ViewStyle | CSSPr /** * Gets the correct height for emoji picker list based on screen dimensions */ -function getEmojiPickerListHeight(hasAdditionalSpace: boolean, windowHeight: number): ViewStyle | CSSProperties { +function getEmojiPickerListHeight(hasAdditionalSpace: boolean, windowHeight: number): ViewStyle { const style = { ...spacing.ph4, height: hasAdditionalSpace ? CONST.NON_NATIVE_EMOJI_PICKER_LIST_HEIGHT + CONST.CATEGORY_SHORTCUT_BAR_HEIGHT : CONST.NON_NATIVE_EMOJI_PICKER_LIST_HEIGHT, @@ -1106,7 +1120,7 @@ function getEmojiPickerListHeight(hasAdditionalSpace: boolean, windowHeight: num /** * Returns style object for the user mention component based on whether the mention is ours or not. */ -function getMentionStyle(isOurMention: boolean): ViewStyle | CSSProperties { +function getMentionStyle(isOurMention: boolean): ViewStyle { const backgroundColor = isOurMention ? themeColors.ourMentionBG : themeColors.mentionBG; return { backgroundColor, @@ -1125,7 +1139,7 @@ function getMentionTextColor(isOurMention: boolean): string { /** * Returns padding vertical based on number of lines */ -function getComposeTextAreaPadding(numberOfLines: number, isComposerFullSize: boolean): ViewStyle | CSSProperties { +function getComposeTextAreaPadding(numberOfLines: number, isComposerFullSize: boolean): ViewStyle { let paddingValue = 5; // Issue #26222: If isComposerFullSize paddingValue will always be 5 to prevent padding jumps when adding multiple lines. if (!isComposerFullSize) { @@ -1146,14 +1160,14 @@ function getComposeTextAreaPadding(numberOfLines: number, isComposerFullSize: bo /** * Returns style object for the mobile on WEB */ -function getOuterModalStyle(windowHeight: number, viewportOffsetTop: number): ViewStyle | CSSProperties { +function getOuterModalStyle(windowHeight: number, viewportOffsetTop: number): ViewStyle { return Browser.isMobile() ? {maxHeight: windowHeight, marginTop: viewportOffsetTop} : {}; } /** * Returns style object for flexWrap depending on the screen size */ -function getWrappingStyle(isExtraSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getWrappingStyle(isExtraSmallScreenWidth: boolean): ViewStyle { return { flexWrap: isExtraSmallScreenWidth ? 'wrap' : 'nowrap', }; @@ -1162,7 +1176,7 @@ function getWrappingStyle(isExtraSmallScreenWidth: boolean): ViewStyle | CSSProp /** * Returns the text container styles for menu items depending on if the menu item container a small avatar */ -function getMenuItemTextContainerStyle(isSmallAvatarSubscriptMenu: boolean): ViewStyle | CSSProperties { +function getMenuItemTextContainerStyle(isSmallAvatarSubscriptMenu: boolean): ViewStyle { return { minHeight: isSmallAvatarSubscriptMenu ? variables.avatarSizeSubscript : variables.componentSizeNormal, }; @@ -1171,7 +1185,7 @@ function getMenuItemTextContainerStyle(isSmallAvatarSubscriptMenu: boolean): Vie /** * Returns link styles based on whether the link is disabled or not */ -function getDisabledLinkStyles(isDisabled = false): ViewStyle | CSSProperties { +function getDisabledLinkStyles(isDisabled = false): ViewStyle { const disabledLinkStyles = { color: themeColors.textSupporting, ...cursor.cursorDisabled, @@ -1195,7 +1209,7 @@ function getColorStyle(color: string): ViewStyle | CSSProperties { /** * Returns the checkbox pressable style */ -function getCheckboxPressableStyle(borderRadius = 6): ViewStyle | CSSProperties { +function getCheckboxPressableStyle(borderRadius = 6): ViewStyle { return { padding: 2, justifyContent: 'center', @@ -1208,7 +1222,7 @@ function getCheckboxPressableStyle(borderRadius = 6): ViewStyle | CSSProperties /** * Returns the checkbox container style */ -function getCheckboxContainerStyle(size: number, borderRadius = 4): ViewStyle | CSSProperties { +function getCheckboxContainerStyle(size: number, borderRadius = 4): ViewStyle { return { backgroundColor: themeColors.componentBG, height: size, @@ -1225,7 +1239,7 @@ function getCheckboxContainerStyle(size: number, borderRadius = 4): ViewStyle | /** * Returns style object for the dropbutton height */ -function getDropDownButtonHeight(buttonSize: ButtonSizeValue): ViewStyle | CSSProperties { +function getDropDownButtonHeight(buttonSize: ButtonSizeValue): ViewStyle { if (buttonSize === CONST.DROPDOWN_BUTTON_SIZE.LARGE) { return { height: variables.componentSizeLarge, @@ -1239,7 +1253,7 @@ function getDropDownButtonHeight(buttonSize: ButtonSizeValue): ViewStyle | CSSPr /** * Returns fitting fontSize and lineHeight values in order to prevent large amounts from being cut off on small screen widths. */ -function getAmountFontSizeAndLineHeight(baseFontSize: number, baseLineHeight: number, isSmallScreenWidth: boolean, windowWidth: number): ViewStyle | CSSProperties { +function getAmountFontSizeAndLineHeight(baseFontSize: number, baseLineHeight: number, isSmallScreenWidth: boolean, windowWidth: number): TextStyle { let toSubtract = 0; if (isSmallScreenWidth) { @@ -1299,40 +1313,25 @@ function getTransparentColor(color: string) { } export { + combineStyles, + displayIfTrue, + getAmountFontSizeAndLineHeight, + getAnimatedFABStyle, + getAutoCompleteSuggestionContainerStyle, + getAutoCompleteSuggestionItemStyle, + getAutoGrowHeightInputStyle, + getAvatarBorderRadius, + getAvatarBorderStyle, + getAvatarBorderWidth, + getAvatarExtraFontSizeStyle, getAvatarSize, - getAvatarWidthStyle, getAvatarStyle, - getAvatarExtraFontSizeStyle, - getAvatarBorderWidth, - getAvatarBorderStyle, - getEmptyAvatarStyle, - getErrorPageContainerStyle, - getSafeAreaPadding, - getSafeAreaMargins, - getZoomCursorStyle, - getZoomSizingStyle, - getWidthStyle, - getAutoGrowHeightInputStyle, + getAvatarWidthStyle, getBackgroundAndBorderStyle, getBackgroundColorStyle, - getTextColorStyle, - getBorderColorStyle, getBackgroundColorWithOpacityStyle, getBadgeColorStyle, getButtonBackgroundColorStyle, - getIconFillColor, - getAnimatedFABStyle, - getWidthAndHeightStyle, - getModalPaddingStyles, - getFontFamilyMonospace, - getEmojiPickerStyle, - getReportActionItemStyle, - getMiniReportActionContextMenuWrapperStyle, - getPaymentMethodMenuWidth, - getThemeBackgroundColor, - parseStyleAsArray, - parseStyleFromFunction, - combineStyles, getPaddingLeft, hasSafeAreas, getHeight, @@ -1348,36 +1347,51 @@ export { getReportWelcomeTopMarginStyle, getReportWelcomeContainerStyle, getBaseAutoCompleteSuggestionContainerStyle, - getAutoCompleteSuggestionItemStyle, - getAutoCompleteSuggestionContainerStyle, + getBorderColorStyle, + getCheckboxContainerStyle, + getCheckboxPressableStyle, getColoredBackgroundStyle, + getComposeTextAreaPadding, getColorStyle, getDefaultWorkspaceAvatarColor, - getAvatarBorderRadius, + getDirectionStyle, + getDisabledLinkStyles, + getDropDownButtonHeight, + getEmojiPickerListHeight, + getEmojiPickerStyle, getEmojiReactionBubbleStyle, getEmojiReactionBubbleTextStyle, getEmojiReactionCounterTextStyle, - getDirectionStyle, - displayIfTrue, + getEmptyAvatarStyle, + getErrorPageContainerStyle, + getFontFamilyMonospace, getFontSizeStyle, - getLineHeightStyle, - getSignInWordmarkWidthStyle, getGoogleListViewStyle, - getEmojiPickerListHeight, + getHeightOfMagicCodeInput, + getIconFillColor, + getLineHeightStyle, getMentionStyle, getMentionTextColor, - getComposeTextAreaPadding, - getHeightOfMagicCodeInput, + getMenuItemTextContainerStyle, + getMiniReportActionContextMenuWrapperStyle, + getModalPaddingStyles, getOuterModalStyle, + getPaymentMethodMenuWidth, + getReportActionItemStyle, + getSafeAreaMargins, + getSafeAreaPadding, + getSignInWordmarkWidthStyle, + getTextColorStyle, + getThemeBackgroundColor, + getTransparentColor, + getWidthAndHeightStyle, + getWidthStyle, getWrappingStyle, - getMenuItemTextContainerStyle, - getDisabledLinkStyles, - getCheckboxPressableStyle, - getCheckboxContainerStyle, - getDropDownButtonHeight, - getAmountFontSizeAndLineHeight, + getZoomCursorStyle, + getZoomSizingStyle, + parseStyleAsArray, + parseStyleFromFunction, getContainerStyles, - getTransparentColor, getEReceiptColorStyles, getEReceiptColorCode, }; diff --git a/src/styles/ThemeStylesProvider.tsx b/src/styles/ThemeStylesProvider.tsx index d0db784ca8ca..4e6f91baf34a 100644 --- a/src/styles/ThemeStylesProvider.tsx +++ b/src/styles/ThemeStylesProvider.tsx @@ -5,7 +5,7 @@ import ThemeStylesContext from './ThemeStylesContext'; // TODO: Rename this to "styles" once the app is migrated to theme switching hooks and HOCs import {stylesGenerator as stylesUntyped} from './styles'; -const styles = stylesUntyped as (theme: Record) => Record; +const styles = stylesUntyped; type ThemeStylesProviderProps = { children: React.ReactNode; diff --git a/src/styles/addOutlineWidth/index.ts b/src/styles/addOutlineWidth/index.ts index 257a3de2b6cd..6fe2ecf85978 100644 --- a/src/styles/addOutlineWidth/index.ts +++ b/src/styles/addOutlineWidth/index.ts @@ -9,11 +9,11 @@ import AddOutlineWidth from './types'; /** * Adds the addOutlineWidth property to an object to be used when styling */ -const addOutlineWidth: AddOutlineWidth = (obj, val, error = false) => ({ +const addOutlineWidth: AddOutlineWidth = (obj, val, hasError = false) => ({ ...obj, outlineWidth: val, outlineStyle: val ? 'auto' : 'none', - boxShadow: val !== 0 ? `0px 0px 0px ${val}px ${error ? themeDefault.danger : themeDefault.borderFocus}` : 'none', + boxShadow: val !== 0 ? `0px 0px 0px ${val}px ${hasError ? themeDefault.danger : themeDefault.borderFocus}` : 'none', }); export default addOutlineWidth; diff --git a/src/styles/addOutlineWidth/types.ts b/src/styles/addOutlineWidth/types.ts index 422d135ef759..7a3a86506959 100644 --- a/src/styles/addOutlineWidth/types.ts +++ b/src/styles/addOutlineWidth/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type AddOutlineWidth = (obj: TextStyle | CSSProperties, val?: number, error?: boolean) => TextStyle | CSSProperties; +type AddOutlineWidth = (obj: TextStyle, val?: number, hasError?: boolean) => TextStyle; export default AddOutlineWidth; diff --git a/src/styles/cardStyles/index.ts b/src/styles/cardStyles/index.ts index 823081b62904..1f28ca4f4b78 100644 --- a/src/styles/cardStyles/index.ts +++ b/src/styles/cardStyles/index.ts @@ -1,10 +1,11 @@ +import positioning from '../utilities/positioning'; import GetCardStyles from './types'; /** * Get card style for cardStyleInterpolator */ const getCardStyles: GetCardStyles = (screenWidth) => ({ - position: 'fixed', + ...positioning.pFixed, width: screenWidth, height: '100%', }); diff --git a/src/styles/cardStyles/types.ts b/src/styles/cardStyles/types.ts index e1598b7696ff..134b93eae32f 100644 --- a/src/styles/cardStyles/types.ts +++ b/src/styles/cardStyles/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; -type GetCardStyles = (screenWidth: number) => Partial>; +type GetCardStyles = (screenWidth: number) => ViewStyle; export default GetCardStyles; diff --git a/src/styles/codeStyles/types.ts b/src/styles/codeStyles/types.ts index 7397b3c6543c..25e35f492089 100644 --- a/src/styles/codeStyles/types.ts +++ b/src/styles/codeStyles/types.ts @@ -1,8 +1,7 @@ -import {CSSProperties} from 'react'; import {TextStyle, ViewStyle} from 'react-native'; -type CodeWordWrapperStyles = ViewStyle | CSSProperties; -type CodeWordStyles = TextStyle | CSSProperties; -type CodeTextStyles = TextStyle | CSSProperties; +type CodeWordWrapperStyles = ViewStyle; +type CodeWordStyles = TextStyle; +type CodeTextStyles = TextStyle; -export type {CodeWordWrapperStyles, CodeWordStyles, CodeTextStyles}; +export type {CodeTextStyles, CodeWordStyles, CodeWordWrapperStyles}; diff --git a/src/styles/containerComposeStyles/index.native.ts b/src/styles/containerComposeStyles/index.native.ts index 6b6bcf71cfcf..ea525dc652cf 100644 --- a/src/styles/containerComposeStyles/index.native.ts +++ b/src/styles/containerComposeStyles/index.native.ts @@ -1,6 +1,6 @@ -import {StyleProp, ViewStyle} from 'react-native'; import styles from '../styles'; +import ContainerComposeStyles from './types'; -const containerComposeStyles: StyleProp = [styles.textInputComposeSpacing]; +const containerComposeStyles: ContainerComposeStyles = [styles.textInputComposeSpacing]; export default containerComposeStyles; diff --git a/src/styles/containerComposeStyles/index.ts b/src/styles/containerComposeStyles/index.ts index 6968e23a507e..fbbf35a20818 100644 --- a/src/styles/containerComposeStyles/index.ts +++ b/src/styles/containerComposeStyles/index.ts @@ -1,7 +1,7 @@ -import {StyleProp, ViewStyle} from 'react-native'; import styles from '../styles'; +import ContainerComposeStyles from './types'; // We need to set paddingVertical = 0 on web to avoid displaying a normal pointer on some parts of compose box when not in focus -const containerComposeStyles: StyleProp = [styles.textInputComposeSpacing, {paddingVertical: 0}]; +const containerComposeStyles: ContainerComposeStyles = [styles.textInputComposeSpacing, {paddingVertical: 0}]; export default containerComposeStyles; diff --git a/src/styles/containerComposeStyles/types.ts b/src/styles/containerComposeStyles/types.ts new file mode 100644 index 000000000000..278039691b8a --- /dev/null +++ b/src/styles/containerComposeStyles/types.ts @@ -0,0 +1,5 @@ +import {ViewStyle} from 'react-native'; + +type ContainerComposeStyles = ViewStyle[]; + +export default ContainerComposeStyles; diff --git a/src/styles/editedLabelStyles/index.ts b/src/styles/editedLabelStyles/index.ts index 6e6164810b52..5764735d0dea 100644 --- a/src/styles/editedLabelStyles/index.ts +++ b/src/styles/editedLabelStyles/index.ts @@ -1,7 +1,10 @@ -import EditedLabelStyles from './types'; import display from '../utilities/display'; import flex from '../utilities/flex'; +import EditedLabelStyles from './types'; -const editedLabelStyles: EditedLabelStyles = {...display.dInlineFlex, ...flex.alignItemsBaseline}; +const editedLabelStyles: EditedLabelStyles = { + ...display.dInlineFlex, + ...flex.alignItemsBaseline, +}; export default editedLabelStyles; diff --git a/src/styles/editedLabelStyles/types.ts b/src/styles/editedLabelStyles/types.ts index 8d96c9216ed2..20bcc8c55f15 100644 --- a/src/styles/editedLabelStyles/types.ts +++ b/src/styles/editedLabelStyles/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type EditedLabelStyles = CSSProperties | TextStyle; +type EditedLabelStyles = TextStyle; export default EditedLabelStyles; diff --git a/src/styles/fontFamily/bold/index.android.js b/src/styles/fontFamily/bold/index.android.js deleted file mode 100644 index 73ba56dc4262..000000000000 --- a/src/styles/fontFamily/bold/index.android.js +++ /dev/null @@ -1,4 +0,0 @@ -const singleBold = 'ExpensifyNeue-Bold'; -const multiBold = 'ExpensifyNeue-Bold'; - -export {singleBold, multiBold}; diff --git a/src/styles/fontFamily/bold/index.android.ts b/src/styles/fontFamily/bold/index.android.ts new file mode 100644 index 000000000000..828b9425e8d4 --- /dev/null +++ b/src/styles/fontFamily/bold/index.android.ts @@ -0,0 +1,6 @@ +import FontFamilyBoldStyles from './types'; + +const singleBold: FontFamilyBoldStyles = 'ExpensifyNeue-Bold'; +const multiBold: FontFamilyBoldStyles = 'ExpensifyNeue-Bold'; + +export {singleBold, multiBold}; diff --git a/src/styles/fontFamily/bold/index.ios.js b/src/styles/fontFamily/bold/index.ios.js deleted file mode 100644 index a0523831a058..000000000000 --- a/src/styles/fontFamily/bold/index.ios.js +++ /dev/null @@ -1,4 +0,0 @@ -const singleBold = 'ExpensifyNeue-Regular'; -const multiBold = 'ExpensifyNeue-Regular'; - -export {singleBold, multiBold}; diff --git a/src/styles/fontFamily/bold/index.ios.ts b/src/styles/fontFamily/bold/index.ios.ts new file mode 100644 index 000000000000..31b7e3d82c05 --- /dev/null +++ b/src/styles/fontFamily/bold/index.ios.ts @@ -0,0 +1,6 @@ +import FontFamilyBoldStyles from './types'; + +const singleBold: FontFamilyBoldStyles = 'ExpensifyNeue-Regular'; +const multiBold: FontFamilyBoldStyles = 'ExpensifyNeue-Regular'; + +export {singleBold, multiBold}; diff --git a/src/styles/fontFamily/bold/index.js b/src/styles/fontFamily/bold/index.js deleted file mode 100644 index 401a808d29d6..000000000000 --- a/src/styles/fontFamily/bold/index.js +++ /dev/null @@ -1,4 +0,0 @@ -const singleBold = 'ExpensifyNeue-Regular'; -const multiBold = 'ExpensifyNeue-Regular, Segoe UI Emoji, Noto Color Emoji'; - -export {singleBold, multiBold}; diff --git a/src/styles/fontFamily/bold/index.ts b/src/styles/fontFamily/bold/index.ts new file mode 100644 index 000000000000..c416bff3aadc --- /dev/null +++ b/src/styles/fontFamily/bold/index.ts @@ -0,0 +1,6 @@ +import FontFamilyBoldStyles from './types'; + +const singleBold: FontFamilyBoldStyles = 'ExpensifyNeue-Regular'; +const multiBold: FontFamilyBoldStyles = 'ExpensifyNeue-Regular, Segoe UI Emoji, Noto Color Emoji'; + +export {singleBold, multiBold}; diff --git a/src/styles/fontFamily/bold/types.ts b/src/styles/fontFamily/bold/types.ts new file mode 100644 index 000000000000..258b23de94a2 --- /dev/null +++ b/src/styles/fontFamily/bold/types.ts @@ -0,0 +1,5 @@ +import {TextStyle} from 'react-native'; + +type FontFamilyBoldStyles = NonNullable; + +export default FontFamilyBoldStyles; diff --git a/src/styles/fontFamily/types.ts b/src/styles/fontFamily/types.ts index 4c9a121e80d7..c688f40927be 100644 --- a/src/styles/fontFamily/types.ts +++ b/src/styles/fontFamily/types.ts @@ -1,3 +1,5 @@ +import {TextStyle} from 'react-native'; + type FontFamilyKeys = | 'EXP_NEUE_ITALIC' | 'EXP_NEUE_BOLD' @@ -10,6 +12,6 @@ type FontFamilyKeys = | 'MONOSPACE_BOLD' | 'MONOSPACE_BOLD_ITALIC'; -type FontFamilyStyles = Record; +type FontFamilyStyles = Record>; export default FontFamilyStyles; diff --git a/src/styles/fontWeight/bold/types.ts b/src/styles/fontWeight/bold/types.ts index 3c9930a63d87..00e72d0e879c 100644 --- a/src/styles/fontWeight/bold/types.ts +++ b/src/styles/fontWeight/bold/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type FontWeightBoldStyles = (TextStyle | CSSProperties)['fontWeight']; +type FontWeightBoldStyles = NonNullable; export default FontWeightBoldStyles; diff --git a/src/styles/getModalStyles.ts b/src/styles/getModalStyles.ts index 32e6cf6f98e7..d52d29568c2d 100644 --- a/src/styles/getModalStyles.ts +++ b/src/styles/getModalStyles.ts @@ -1,16 +1,17 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; -import {ValueOf} from 'type-fest'; import {ModalProps} from 'react-native-modal'; +import {ValueOf} from 'type-fest'; import CONST from '../CONST'; -import variables from './variables'; -import themeColors from './themes/default'; import styles from './styles'; +import themeColors from './themes/default'; +import variables from './variables'; function getCenteredModalStyles(windowWidth: number, isSmallScreenWidth: boolean, isFullScreenWhenSmall = false): ViewStyle { + const modalStyles = styles.centeredModalStyles(isSmallScreenWidth, isFullScreenWhenSmall); + return { - borderWidth: styles.centeredModalStyles(isSmallScreenWidth, isFullScreenWhenSmall).borderWidth, - width: isSmallScreenWidth ? '100%' : windowWidth - styles.centeredModalStyles(isSmallScreenWidth, isFullScreenWhenSmall).marginHorizontal * 2, + borderWidth: modalStyles.borderWidth, + width: isSmallScreenWidth ? '100%' : windowWidth - modalStyles.marginHorizontal * 2, }; } @@ -26,7 +27,7 @@ type WindowDimensions = { type GetModalStyles = { modalStyle: ViewStyle; - modalContainerStyle: ViewStyle | Pick; + modalContainerStyle: ViewStyle; swipeDirection: ModalProps['swipeDirection']; animationIn: ModalProps['animationIn']; animationOut: ModalProps['animationOut']; diff --git a/src/styles/getNavigationModalCardStyles/index.desktop.js b/src/styles/getNavigationModalCardStyles/index.desktop.ts similarity index 57% rename from src/styles/getNavigationModalCardStyles/index.desktop.js rename to src/styles/getNavigationModalCardStyles/index.desktop.ts index 54c9790253d6..75b5636f9bd8 100644 --- a/src/styles/getNavigationModalCardStyles/index.desktop.js +++ b/src/styles/getNavigationModalCardStyles/index.desktop.ts @@ -1,4 +1,7 @@ -export default () => ({ +import positioning from '../utilities/positioning'; +import GetNavigationModalCardStyles from './types'; + +const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ // position: fixed is set instead of position absolute to workaround Safari known issues of updating heights in DOM. // Safari issues: // https://github.com/Expensify/App/issues/12005 @@ -6,5 +9,8 @@ export default () => ({ // https://github.com/Expensify/App/issues/20709 width: '100%', height: '100%', - position: 'fixed', + + ...positioning.pFixed, }); + +export default getNavigationModalCardStyles; diff --git a/src/styles/getNavigationModalCardStyles/index.ts b/src/styles/getNavigationModalCardStyles/index.ts index 8e2bffc1ecfc..d3cf49c6c89c 100644 --- a/src/styles/getNavigationModalCardStyles/index.ts +++ b/src/styles/getNavigationModalCardStyles/index.ts @@ -1,3 +1,7 @@ -export default () => ({ +import GetNavigationModalCardStyles from './types'; + +const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ height: '100%', }); + +export default getNavigationModalCardStyles; diff --git a/src/styles/getNavigationModalCardStyles/index.website.ts b/src/styles/getNavigationModalCardStyles/index.website.ts index 9879e4c1567b..75b5636f9bd8 100644 --- a/src/styles/getNavigationModalCardStyles/index.website.ts +++ b/src/styles/getNavigationModalCardStyles/index.website.ts @@ -1,3 +1,4 @@ +import positioning from '../utilities/positioning'; import GetNavigationModalCardStyles from './types'; const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ @@ -8,7 +9,8 @@ const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ // https://github.com/Expensify/App/issues/20709 width: '100%', height: '100%', - position: 'fixed', + + ...positioning.pFixed, }); export default getNavigationModalCardStyles; diff --git a/src/styles/getNavigationModalCardStyles/types.ts b/src/styles/getNavigationModalCardStyles/types.ts index 504b659c87b7..877981dd4dd2 100644 --- a/src/styles/getNavigationModalCardStyles/types.ts +++ b/src/styles/getNavigationModalCardStyles/types.ts @@ -1,9 +1,7 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; -import {Merge} from 'type-fest'; type GetNavigationModalCardStylesParams = {isSmallScreenWidth: number}; -type GetNavigationModalCardStyles = (params: GetNavigationModalCardStylesParams) => Merge>; +type GetNavigationModalCardStyles = (params: GetNavigationModalCardStylesParams) => ViewStyle; export default GetNavigationModalCardStyles; diff --git a/src/styles/getReportActionContextMenuStyles.ts b/src/styles/getReportActionContextMenuStyles.ts index 9c0e159eb5fe..cd3843169a43 100644 --- a/src/styles/getReportActionContextMenuStyles.ts +++ b/src/styles/getReportActionContextMenuStyles.ts @@ -1,16 +1,13 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; import styles from './styles'; -import variables from './variables'; import themeColors from './themes/default'; +import variables from './variables'; -type StylesArray = Array; - -const defaultWrapperStyle: ViewStyle | CSSProperties = { +const defaultWrapperStyle: ViewStyle = { backgroundColor: themeColors.componentBG, }; -const miniWrapperStyle: StylesArray = [ +const miniWrapperStyle: ViewStyle[] = [ styles.flexRow, defaultWrapperStyle, { @@ -18,11 +15,12 @@ const miniWrapperStyle: StylesArray = [ borderWidth: 1, borderColor: themeColors.border, // In Safari, when welcome messages use a code block (triple backticks), they would overlap the context menu below when there is no scrollbar without the transform style. - transform: 'translateZ(0)', + // NOTE: asserting "transform" to a valid type, because it isn't possible to augment "transform". + transform: 'translateZ(0)' as unknown as ViewStyle['transform'], }, ]; -const bigWrapperStyle: StylesArray = [styles.flexColumn, defaultWrapperStyle]; +const bigWrapperStyle: ViewStyle[] = [styles.flexColumn, defaultWrapperStyle]; /** * Generate the wrapper styles for the ReportActionContextMenu. @@ -30,7 +28,7 @@ const bigWrapperStyle: StylesArray = [styles.flexColumn, defaultWrapperStyle]; * @param isMini * @param isSmallScreenWidth */ -function getReportActionContextMenuStyles(isMini: boolean, isSmallScreenWidth: boolean): StylesArray { +function getReportActionContextMenuStyles(isMini: boolean, isSmallScreenWidth: boolean): ViewStyle[] { if (isMini) { return miniWrapperStyle; } diff --git a/src/styles/getTooltipStyles.ts b/src/styles/getTooltipStyles.ts index be76002bd216..2613cb791688 100644 --- a/src/styles/getTooltipStyles.ts +++ b/src/styles/getTooltipStyles.ts @@ -1,11 +1,11 @@ -import {CSSProperties} from 'react'; import {TextStyle, View, ViewStyle} from 'react-native'; -import spacing from './utilities/spacing'; +import fontFamily from './fontFamily'; +import roundToNearestMultipleOfFour from './roundToNearestMultipleOfFour'; import styles from './styles'; import themeColors from './themes/default'; -import fontFamily from './fontFamily'; +import positioning from './utilities/positioning'; +import spacing from './utilities/spacing'; import variables from './variables'; -import roundToNearestMultipleOfFour from './roundToNearestMultipleOfFour'; /** This defines the proximity with the edge of the window in which tooltips should not be displayed. * If a tooltip is too close to the edge of the screen, we'll shift it towards the center. */ @@ -95,9 +95,9 @@ function isOverlappingAtTop(tooltip: View | HTMLDivElement, xOffset: number, yOf type TooltipStyles = { animationStyle: ViewStyle; - rootWrapperStyle: ViewStyle | CSSProperties; + rootWrapperStyle: ViewStyle; textStyle: TextStyle; - pointerWrapperStyle: ViewStyle | CSSProperties; + pointerWrapperStyle: ViewStyle; pointerStyle: ViewStyle; }; @@ -237,7 +237,7 @@ export default function getTooltipStyles( transform: [{scale}], }, rootWrapperStyle: { - position: 'fixed', + ...positioning.pFixed, backgroundColor: themeColors.heading, borderRadius: variables.componentBorderRadiusSmall, ...tooltipVerticalPadding, @@ -260,7 +260,7 @@ export default function getTooltipStyles( textAlign: 'center', }, pointerWrapperStyle: { - position: 'fixed', + ...positioning.pFixed, top: pointerWrapperTop, left: pointerWrapperLeft, }, diff --git a/src/styles/italic/types.ts b/src/styles/italic/types.ts index 61e0328e52b6..e9feedbdfac5 100644 --- a/src/styles/italic/types.ts +++ b/src/styles/italic/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type ItalicStyles = (TextStyle | CSSProperties)['fontStyle']; +type ItalicStyles = NonNullable; export default ItalicStyles; diff --git a/src/styles/optionAlternateTextPlatformStyles/types.ts b/src/styles/optionAlternateTextPlatformStyles/types.ts index b2e8e4745fff..aacdef7e3501 100644 --- a/src/styles/optionAlternateTextPlatformStyles/types.ts +++ b/src/styles/optionAlternateTextPlatformStyles/types.ts @@ -1,5 +1,5 @@ import {TextStyle} from 'react-native'; -type OptionAlternateTextPlatformStyles = Partial>; +type OptionAlternateTextPlatformStyles = Pick; export default OptionAlternateTextPlatformStyles; diff --git a/src/styles/optionRowStyles/index.native.ts b/src/styles/optionRowStyles/index.native.ts index 11371509ce73..9c13fdd082a4 100644 --- a/src/styles/optionRowStyles/index.native.ts +++ b/src/styles/optionRowStyles/index.native.ts @@ -1,5 +1,5 @@ -import OptionRowStyles from './types'; import styles from '../styles'; +import CompactContentContainerStyles from './types'; /** * On native platforms, alignItemsBaseline does not work correctly @@ -7,8 +7,7 @@ import styles from '../styles'; * keeping compactContentContainerStyles as it is. * https://github.com/Expensify/App/issues/14148 */ - -const compactContentContainerStyles: OptionRowStyles = styles.alignItemsCenter; +const compactContentContainerStyles: CompactContentContainerStyles = styles.alignItemsCenter; export { // eslint-disable-next-line import/prefer-default-export diff --git a/src/styles/optionRowStyles/index.ts b/src/styles/optionRowStyles/index.ts index fbeca3c702d9..975f4243842e 100644 --- a/src/styles/optionRowStyles/index.ts +++ b/src/styles/optionRowStyles/index.ts @@ -1,7 +1,7 @@ -import OptionRowStyles from './types'; +import CompactContentContainerStyles from './types'; import styles from '../styles'; -const compactContentContainerStyles: OptionRowStyles = styles.alignItemsBaseline; +const compactContentContainerStyles: CompactContentContainerStyles = styles.alignItemsBaseline; export { // eslint-disable-next-line import/prefer-default-export diff --git a/src/styles/optionRowStyles/types.ts b/src/styles/optionRowStyles/types.ts index c08174470701..fcce41f6bc28 100644 --- a/src/styles/optionRowStyles/types.ts +++ b/src/styles/optionRowStyles/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; -type OptionRowStyles = CSSProperties | ViewStyle; +type CompactContentContainerStyles = ViewStyle; -export default OptionRowStyles; +export default CompactContentContainerStyles; diff --git a/src/styles/overflowXHidden/types.ts b/src/styles/overflowXHidden/types.ts index 7ac572f0e651..0f13ba552c0c 100644 --- a/src/styles/overflowXHidden/types.ts +++ b/src/styles/overflowXHidden/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type OverflowXHiddenStyles = Partial>; +type OverflowXHiddenStyles = Pick; export default OverflowXHiddenStyles; diff --git a/src/styles/pointerEventsAuto/types.ts b/src/styles/pointerEventsAuto/types.ts index 7c9f0164c936..0ecbc851e000 100644 --- a/src/styles/pointerEventsAuto/types.ts +++ b/src/styles/pointerEventsAuto/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type PointerEventsAutoStyles = Partial>; +type PointerEventsAutoStyles = Pick; export default PointerEventsAutoStyles; diff --git a/src/styles/pointerEventsNone/types.ts b/src/styles/pointerEventsNone/types.ts index f2f5a0d88a41..766ac3f94349 100644 --- a/src/styles/pointerEventsNone/types.ts +++ b/src/styles/pointerEventsNone/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type PointerEventsNone = Partial>; +type PointerEventsNone = Pick; export default PointerEventsNone; diff --git a/src/styles/styles.js b/src/styles/styles.js deleted file mode 100644 index 3396bc83259b..000000000000 --- a/src/styles/styles.js +++ /dev/null @@ -1,3912 +0,0 @@ -import {defaultStyles as defaultPickerStyles} from 'react-native-picker-select/src/styles'; -import lodashClamp from 'lodash/clamp'; -import fontFamily from './fontFamily'; -import addOutlineWidth from './addOutlineWidth'; -import defaultTheme from './themes/default'; -import fontWeightBold from './fontWeight/bold'; -import variables from './variables'; -import spacing from './utilities/spacing'; -import sizing from './utilities/sizing'; -import flex from './utilities/flex'; -import display from './utilities/display'; -import overflow from './utilities/overflow'; -import whiteSpace from './utilities/whiteSpace'; -import wordBreak from './utilities/wordBreak'; -import positioning from './utilities/positioning'; -import codeStyles from './codeStyles'; -import visibility from './utilities/visibility'; -import writingDirection from './utilities/writingDirection'; -import optionAlternateTextPlatformStyles from './optionAlternateTextPlatformStyles'; -import pointerEventsNone from './pointerEventsNone'; -import pointerEventsAuto from './pointerEventsAuto'; -import getPopOverVerticalOffset from './getPopOverVerticalOffset'; -import overflowXHidden from './overflowXHidden'; -import CONST from '../CONST'; -import * as Browser from '../libs/Browser'; -import cursor from './utilities/cursor'; -import userSelect from './utilities/userSelect'; -import textUnderline from './utilities/textUnderline'; -import colors from './colors'; -import objectFit from './utilities/objectFit'; - -// touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target -const touchCalloutNone = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; -// to prevent vertical text offset in Safari for badges, new lineHeight values have been added -const lineHeightBadge = Browser.isSafari() ? {lineHeight: variables.lineHeightXSmall} : {lineHeight: variables.lineHeightNormal}; - -const picker = (theme) => ({ - backgroundColor: theme.transparent, - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - lineHeight: variables.fontSizeNormalHeight, - paddingBottom: 8, - paddingTop: 23, - paddingLeft: 0, - paddingRight: 25, - height: variables.inputHeight, - borderWidth: 0, - textAlign: 'left', -}); - -const link = (theme) => ({ - color: theme.link, - textDecorationColor: theme.link, - fontFamily: fontFamily.EXP_NEUE, -}); - -const baseCodeTagStyles = (theme) => ({ - borderWidth: 1, - borderRadius: 5, - borderColor: theme.border, - backgroundColor: theme.textBackground, -}); - -const headlineFont = { - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - fontWeight: '500', -}; - -const webViewStyles = (theme) => ({ - // As of react-native-render-html v6, don't declare distinct styles for - // custom renderers, the API for custom renderers has changed. Declare the - // styles in the below "tagStyles" instead. If you need to reuse those - // styles from the renderer, just pass the "style" prop to the underlying - // component. - tagStyles: { - em: { - fontFamily: fontFamily.EXP_NEUE, - fontStyle: 'italic', - }, - - del: { - textDecorationLine: 'line-through', - textDecorationStyle: 'solid', - }, - - strong: { - fontFamily: fontFamily.EXP_NEUE, - fontWeight: 'bold', - }, - - a: link(theme), - - ul: { - maxWidth: '100%', - }, - - ol: { - maxWidth: '100%', - }, - - li: { - flexShrink: 1, - }, - - blockquote: { - borderLeftColor: theme.border, - borderLeftWidth: 4, - paddingLeft: 12, - marginTop: 4, - marginBottom: 4, - - // Overwrite default HTML margin for blockquotes - marginLeft: 0, - }, - - pre: { - ...baseCodeTagStyles(theme), - paddingTop: 12, - paddingBottom: 12, - paddingRight: 8, - paddingLeft: 8, - fontFamily: fontFamily.MONOSPACE, - marginTop: 0, - marginBottom: 0, - }, - - code: { - ...baseCodeTagStyles(theme), - ...codeStyles.codeTextStyle, - paddingLeft: 5, - paddingRight: 5, - fontFamily: fontFamily.MONOSPACE, - fontSize: 13, - }, - - img: { - borderColor: theme.border, - borderRadius: variables.componentBorderRadiusNormal, - borderWidth: 1, - ...touchCalloutNone, - }, - - p: { - marginTop: 0, - marginBottom: 0, - }, - h1: { - fontSize: variables.fontSizeLarge, - marginBottom: 8, - }, - }, - - baseFontStyle: { - color: theme.text, - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE, - flex: 1, - lineHeight: variables.fontSizeNormalHeight, - ...writingDirection.ltr, - }, -}); - -const styles = (theme) => ({ - // Add all of our utility and helper styles - ...spacing, - ...sizing, - ...flex, - ...display, - ...overflow, - ...positioning, - ...wordBreak, - ...whiteSpace, - ...writingDirection, - ...cursor, - ...userSelect, - ...textUnderline, - ...theme, // TODO: Should we do this? - ...objectFit, - - autoCompleteSuggestionsContainer: { - backgroundColor: theme.appBG, - borderRadius: 8, - borderWidth: 1, - borderColor: theme.border, - justifyContent: 'center', - boxShadow: variables.popoverMenuShadow, - position: 'absolute', - left: 0, - right: 0, - paddingVertical: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTER_INNER_PADDING, - }, - - autoCompleteSuggestionContainer: { - flexDirection: 'row', - alignItems: 'center', - }, - - emojiSuggestionsEmoji: { - fontSize: variables.fontSizeMedium, - width: 51, - textAlign: 'center', - }, - emojiSuggestionsText: { - fontSize: variables.fontSizeMedium, - flex: 1, - ...wordBreak.breakWord, - ...spacing.pr4, - }, - - mentionSuggestionsAvatarContainer: { - width: 24, - height: 24, - alignItems: 'center', - justifyContent: 'center', - }, - - mentionSuggestionsText: { - fontSize: variables.fontSizeMedium, - ...spacing.ml2, - }, - - mentionSuggestionsDisplayName: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - }, - - mentionSuggestionsHandle: { - color: theme.textSupporting, - }, - - webViewStyles: webViewStyles(theme), - - link: link(theme), - - linkMuted: { - color: theme.textSupporting, - textDecorationColor: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - }, - - linkMutedHovered: { - color: theme.textMutedReversed, - }, - - highlightBG: { - backgroundColor: theme.highlightBG, - }, - - appBG: { - backgroundColor: theme.appBG, - }, - - h4: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeLabel, - fontWeight: fontWeightBold, - }, - - textAlignCenter: { - textAlign: 'center', - }, - - textAlignRight: { - textAlign: 'right', - }, - - textAlignLeft: { - textAlign: 'left', - }, - - textUnderline: { - textDecorationLine: 'underline', - }, - - label: { - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - }, - - textLabel: { - color: theme.text, - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - }, - - mutedTextLabel: { - color: theme.textSupporting, - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - }, - - textMicro: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, - - textMicroBold: { - color: theme.text, - fontWeight: fontWeightBold, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, - - textMicroSupporting: { - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, - - textExtraSmallSupporting: { - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeExtraSmall, - }, - - textNormal: { - fontSize: variables.fontSizeNormal, - }, - - textLarge: { - fontSize: variables.fontSizeLarge, - }, - - textXLarge: { - fontSize: variables.fontSizeXLarge, - }, - - textXXLarge: { - fontSize: variables.fontSizeXXLarge, - }, - - textXXXLarge: { - fontSize: variables.fontSizeXXXLarge, - }, - - textHero: { - fontSize: variables.fontSizeHero, - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - lineHeight: variables.lineHeightHero, - }, - - textStrong: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - }, - - textItalic: { - fontFamily: fontFamily.EXP_NEUE_ITALIC, - fontStyle: 'italic', - }, - - textHeadline: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - }, - - textHeadlineH1: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - fontSize: variables.fontSizeh1, - lineHeight: variables.lineHeightSizeh1, - }, - - textDecorationNoLine: { - textDecorationLine: 'none', - }, - - textWhite: { - color: theme.textLight, - }, - - textBlue: { - color: theme.link, - }, - - textUppercase: { - textTransform: 'uppercase', - }, - - textNoWrap: { - ...whiteSpace.noWrap, - }, - - colorReversed: { - color: theme.textReversed, - }, - - colorMutedReversed: { - color: theme.textMutedReversed, - }, - - colorMuted: { - color: theme.textSupporting, - }, - - bgTransparent: { - backgroundColor: 'transparent', - }, - - bgDark: { - backgroundColor: theme.inverse, - }, - - opacity0: { - opacity: 0, - }, - - opacity1: { - opacity: 1, - }, - - textDanger: { - color: theme.danger, - }, - - borderRadiusNormal: { - borderRadius: variables.buttonBorderRadius, - }, - - button: { - backgroundColor: theme.buttonDefaultBG, - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeLarge, - justifyContent: 'center', - ...spacing.ph3, - }, - - buttonContainer: { - padding: 1, - borderRadius: variables.buttonBorderRadius, - }, - - buttonText: { - color: theme.text, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - textAlign: 'center', - flexShrink: 1, - - // It is needed to unset the Lineheight. We don't need it for buttons as button always contains single line of text. - // It allows to vertically center the text. - lineHeight: undefined, - - // Add 1px to the Button text to give optical vertical alignment. - paddingBottom: 1, - }, - - buttonSmall: { - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeSmall, - paddingTop: 4, - paddingHorizontal: 14, - paddingBottom: 4, - backgroundColor: theme.buttonDefaultBG, - }, - - buttonMedium: { - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeNormal, - paddingTop: 12, - paddingRight: 16, - paddingBottom: 12, - paddingLeft: 16, - backgroundColor: theme.buttonDefaultBG, - }, - - buttonLarge: { - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeLarge, - paddingTop: 8, - paddingRight: 10, - paddingBottom: 8, - paddingLeft: 18, - backgroundColor: theme.buttonDefaultBG, - }, - - buttonSmallText: { - fontSize: variables.fontSizeSmall, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - textAlign: 'center', - }, - - buttonMediumText: { - fontSize: variables.fontSizeLabel, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - textAlign: 'center', - }, - - buttonLargeText: { - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - textAlign: 'center', - }, - - buttonDefaultHovered: { - backgroundColor: theme.buttonHoveredBG, - borderWidth: 0, - }, - - buttonSuccess: { - backgroundColor: theme.success, - borderWidth: 0, - }, - - buttonOpacityDisabled: { - opacity: 0.5, - }, - - buttonSuccessHovered: { - backgroundColor: theme.successHover, - borderWidth: 0, - }, - - buttonDanger: { - backgroundColor: theme.danger, - borderWidth: 0, - }, - - buttonDangerHovered: { - backgroundColor: theme.dangerHover, - borderWidth: 0, - }, - - buttonDisabled: { - backgroundColor: theme.buttonDefaultBG, - borderWidth: 0, - }, - - buttonDivider: { - height: variables.dropDownButtonDividerHeight, - borderWidth: 0.7, - borderColor: theme.text, - }, - - noBorderRadius: { - borderRadius: 0, - }, - - noRightBorderRadius: { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }, - - noLeftBorderRadius: { - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - }, - - buttonCTA: { - paddingVertical: 6, - ...spacing.mh4, - }, - - buttonCTAIcon: { - marginRight: 22, - - // Align vertically with the Button text - paddingBottom: 1, - paddingTop: 1, - }, - - buttonConfirm: { - margin: 20, - }, - - attachmentButtonBigScreen: { - minWidth: 300, - alignSelf: 'center', - }, - - buttonConfirmText: { - paddingLeft: 20, - paddingRight: 20, - }, - - buttonSuccessText: { - color: theme.textLight, - }, - - buttonDangerText: { - color: theme.textLight, - }, - - hoveredComponentBG: { - backgroundColor: theme.hoverComponentBG, - }, - - activeComponentBG: { - backgroundColor: theme.activeComponentBG, - }, - - touchableButtonImage: { - alignItems: 'center', - height: variables.componentSizeNormal, - justifyContent: 'center', - width: variables.componentSizeNormal, - }, - - visuallyHidden: { - ...visibility.hidden, - overflow: 'hidden', - width: 0, - height: 0, - }, - - visibilityHidden: { - ...visibility.hidden, - }, - - loadingVBAAnimation: { - width: 140, - height: 140, - }, - - pickerSmall: (backgroundColor = theme.highlightBG) => ({ - inputIOS: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - paddingLeft: 0, - paddingRight: 17, - paddingTop: 6, - paddingBottom: 6, - borderWidth: 0, - color: theme.text, - height: 26, - opacity: 1, - backgroundColor: 'transparent', - }, - done: { - color: theme.text, - }, - doneDepressed: { - fontSize: defaultPickerStyles.done.fontSize, - }, - modalViewMiddle: { - backgroundColor: theme.border, - borderTopWidth: 0, - }, - modalViewBottom: { - backgroundColor: theme.highlightBG, - }, - inputWeb: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - paddingLeft: 0, - paddingRight: 17, - paddingTop: 6, - paddingBottom: 6, - borderWidth: 0, - color: theme.text, - appearance: 'none', - height: 26, - opacity: 1, - backgroundColor, - ...cursor.cursorPointer, - }, - inputAndroid: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - paddingLeft: 0, - paddingRight: 17, - paddingTop: 6, - paddingBottom: 6, - borderWidth: 0, - color: theme.text, - height: 26, - opacity: 1, - backgroundColor: 'transparent', - }, - iconContainer: { - top: 7, - ...pointerEventsNone, - }, - icon: { - width: variables.iconSizeExtraSmall, - height: variables.iconSizeExtraSmall, - }, - }), - - badge: { - backgroundColor: theme.border, - borderRadius: 14, - height: variables.iconSizeNormal, - flexDirection: 'row', - paddingHorizontal: 7, - alignItems: 'center', - }, - - badgeSuccess: { - backgroundColor: theme.success, - }, - - badgeSuccessPressed: { - backgroundColor: theme.successHover, - }, - - badgeAdHocSuccess: { - backgroundColor: theme.badgeAdHoc, - }, - - badgeAdHocSuccessPressed: { - backgroundColor: theme.badgeAdHocHover, - }, - - badgeDanger: { - backgroundColor: theme.danger, - }, - - badgeDangerPressed: { - backgroundColor: theme.dangerPressed, - }, - - badgeText: { - color: theme.text, - fontSize: variables.fontSizeSmall, - ...lineHeightBadge, - ...whiteSpace.noWrap, - }, - - border: { - borderWidth: 1, - borderRadius: variables.componentBorderRadius, - borderColor: theme.border, - }, - - borderColorFocus: { - borderColor: theme.borderFocus, - }, - - borderColorDanger: { - borderColor: theme.danger, - }, - - textInputDisabled: { - // Adding disabled color theme to indicate user that the field is not editable. - backgroundColor: theme.highlightBG, - borderBottomWidth: 2, - borderColor: theme.borderLighter, - // Adding browser specefic style to bring consistency between Safari and other platforms. - // Applying the Webkit styles only to browsers as it is not available in native. - ...(Browser.getBrowser() - ? { - WebkitTextFillColor: theme.textSupporting, - WebkitOpacity: 1, - } - : {}), - color: theme.textSupporting, - }, - - uploadReceiptView: (isSmallScreenWidth) => ({ - borderRadius: variables.componentBorderRadiusLarge, - borderWidth: isSmallScreenWidth ? 0 : 2, - borderColor: theme.borderFocus, - borderStyle: 'dotted', - marginBottom: 20, - marginLeft: 20, - marginRight: 20, - justifyContent: 'center', - alignItems: 'center', - paddingVertical: 40, - gap: 4, - flex: 1, - }), - - receiptViewTextContainer: { - paddingHorizontal: 40, - ...sizing.w100, - }, - - cameraView: { - flex: 1, - overflow: 'hidden', - borderRadius: 28, - borderStyle: 'solid', - borderWidth: 8, - backgroundColor: theme.highlightBG, - borderColor: theme.appBG, - display: 'flex', - justifyContent: 'center', - justifyItems: 'center', - }, - - permissionView: { - paddingVertical: 108, - paddingHorizontal: 61, - alignItems: 'center', - justifyContent: 'center', - }, - - headerAnonymousFooter: { - color: theme.heading, - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - }, - - headerText: { - color: theme.heading, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - }, - - headerGap: { - height: CONST.DESKTOP_HEADER_PADDING, - }, - - reportOptions: { - marginLeft: 8, - }, - - chatItemComposeSecondaryRow: { - height: CONST.CHAT_FOOTER_SECONDARY_ROW_HEIGHT, - marginBottom: CONST.CHAT_FOOTER_SECONDARY_ROW_PADDING, - marginTop: CONST.CHAT_FOOTER_SECONDARY_ROW_PADDING, - }, - - chatItemComposeSecondaryRowSubText: { - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, - - chatItemComposeSecondaryRowOffset: { - marginLeft: variables.chatInputSpacing, - }, - - offlineIndicator: { - marginLeft: variables.chatInputSpacing, - }, - - offlineIndicatorMobile: { - paddingLeft: 20, - paddingTop: 5, - paddingBottom: 30, - marginBottom: -25, - }, - - offlineIndicatorRow: { - height: 25, - }, - - // Actions - actionAvatar: { - borderRadius: 20, - }, - - componentHeightLarge: { - height: variables.inputHeight, - }, - - calendarHeader: { - height: 50, - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: 15, - paddingRight: 5, - ...userSelect.userSelectNone, - }, - - calendarDayRoot: { - flex: 1, - height: 45, - justifyContent: 'center', - alignItems: 'center', - ...userSelect.userSelectNone, - }, - - calendarDayContainer: { - width: 30, - height: 30, - justifyContent: 'center', - alignItems: 'center', - borderRadius: 15, - overflow: 'hidden', - }, - - calendarDayContainerSelected: { - backgroundColor: theme.buttonDefaultBG, - }, - - /** - * @param {number} textInputHeight - * @param {number} minHeight - * @param {number} maxHeight - * @returns {object} - */ - autoGrowHeightInputContainer: (textInputHeight, minHeight, maxHeight) => ({ - height: lodashClamp(textInputHeight, minHeight, maxHeight), - minHeight, - }), - - autoGrowHeightHiddenInput: (maxWidth, maxHeight) => ({ - maxWidth, - maxHeight: maxHeight && maxHeight + 1, - overflow: 'hidden', - }), - - textInputContainer: { - flex: 1, - justifyContent: 'center', - height: '100%', - backgroundColor: 'transparent', - borderBottomWidth: 2, - borderColor: theme.border, - overflow: 'hidden', - }, - - textInputLabel: { - position: 'absolute', - left: 0, - top: 0, - fontSize: variables.fontSizeNormal, - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - width: '100%', - }, - - textInputLabelBackground: { - position: 'absolute', - top: 0, - width: '100%', - height: 23, - backgroundColor: theme.componentBG, - }, - - textInputLabelDesktop: { - transformOrigin: 'left center', - }, - - textInputLabelTransformation: (translateY, translateX, scale) => ({ - transform: [{translateY}, {translateX}, {scale}], - }), - - baseTextInput: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - lineHeight: variables.lineHeightXLarge, - color: theme.text, - paddingTop: 23, - paddingBottom: 8, - paddingLeft: 0, - borderWidth: 0, - }, - - textInputMultiline: { - scrollPadding: '23px 0 0 0', - }, - - textInputMultilineContainer: { - paddingTop: 23, - }, - - textInputAndIconContainer: { - flex: 1, - height: '100%', - zIndex: -1, - flexDirection: 'row', - }, - - textInputDesktop: addOutlineWidth({}, 0), - - textInputIconContainer: { - paddingHorizontal: 11, - justifyContent: 'center', - margin: 1, - }, - - secureInput: { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }, - - textInput: { - backgroundColor: 'transparent', - borderRadius: variables.componentBorderRadiusNormal, - height: variables.inputComponentSizeNormal, - borderColor: theme.border, - borderWidth: 1, - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - paddingLeft: 12, - paddingRight: 12, - paddingTop: 10, - paddingBottom: 10, - textAlignVertical: 'center', - }, - - textInputPrefixWrapper: { - position: 'absolute', - left: 0, - top: 0, - height: variables.inputHeight, - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - paddingTop: 23, - paddingBottom: 8, - }, - - textInputPrefix: { - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - textAlignVertical: 'center', - }, - - pickerContainer: { - borderBottomWidth: 2, - paddingLeft: 0, - borderStyle: 'solid', - borderColor: theme.border, - justifyContent: 'center', - backgroundColor: 'transparent', - height: variables.inputHeight, - overflow: 'hidden', - }, - - pickerContainerSmall: { - height: variables.inputHeightSmall, - }, - - pickerLabel: { - position: 'absolute', - left: 0, - top: 6, - zIndex: 1, - }, - - picker: (disabled = false, backgroundColor = theme.appBG) => ({ - iconContainer: { - top: Math.round(variables.inputHeight * 0.5) - 11, - right: 0, - ...pointerEventsNone, - }, - - inputWeb: { - appearance: 'none', - ...(disabled ? cursor.cursorDisabled : cursor.cursorPointer), - ...picker(theme), - backgroundColor, - }, - - inputIOS: { - ...picker(theme), - }, - done: { - color: theme.text, - }, - doneDepressed: { - fontSize: defaultPickerStyles.done.fontSize, - }, - modalViewMiddle: { - backgroundColor: theme.border, - borderTopWidth: 0, - }, - modalViewBottom: { - backgroundColor: theme.highlightBG, - }, - - inputAndroid: { - ...picker(theme), - }, - }), - - disabledText: { - color: theme.icon, - }, - - inputDisabled: { - backgroundColor: theme.highlightBG, - color: theme.icon, - }, - - noOutline: addOutlineWidth({}, 0), - - textLabelSupporting: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.textSupporting, - }, - - textLabelError: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.textError, - }, - - textReceiptUpload: { - ...headlineFont, - fontSize: variables.fontSizeXLarge, - color: theme.textLight, - textAlign: 'center', - }, - - subTextReceiptUpload: { - fontFamily: fontFamily.EXP_NEUE, - lineHeight: variables.lineHeightLarge, - textAlign: 'center', - color: theme.textLight, - }, - - furtherDetailsText: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - color: theme.textSupporting, - }, - - lh16: { - lineHeight: 16, - }, - - lh20: { - lineHeight: 20, - }, - - lh140Percent: { - lineHeight: '140%', - }, - - formHelp: { - color: theme.textSupporting, - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - marginBottom: 4, - }, - - formError: { - color: theme.textError, - fontSize: variables.fontSizeLabel, - lineHeight: variables.formErrorLineHeight, - marginBottom: 4, - }, - - formSuccess: { - color: theme.success, - fontSize: variables.fontSizeLabel, - lineHeight: 18, - marginBottom: 4, - }, - - signInPage: { - backgroundColor: theme.highlightBG, - minHeight: '100%', - flex: 1, - }, - - signInPageHeroCenter: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'center', - alignItems: 'center', - }, - - signInPageGradient: { - height: '100%', - width: 540, - position: 'absolute', - top: 0, - left: 0, - }, - - signInPageGradientMobile: { - height: 300, - width: 800, - position: 'absolute', - top: 0, - left: 0, - }, - - signInBackground: { - position: 'absolute', - bottom: 0, - left: 0, - minHeight: 700, - }, - - signInPageInner: { - marginLeft: 'auto', - marginRight: 'auto', - height: '100%', - width: '100%', - }, - - signInPageContentTopSpacer: { - maxHeight: 132, - minHeight: 24, - }, - - signInPageContentTopSpacerSmallScreens: { - maxHeight: 132, - minHeight: 45, - }, - - signInPageLeftContainer: { - paddingLeft: 40, - paddingRight: 40, - }, - - signInPageLeftContainerWide: { - maxWidth: variables.sideBarWidth, - }, - - signInPageWelcomeFormContainer: { - maxWidth: CONST.SIGN_IN_FORM_WIDTH, - }, - - signInPageWelcomeTextContainer: { - width: CONST.SIGN_IN_FORM_WIDTH, - }, - - changeExpensifyLoginLinkContainer: { - flexDirection: 'row', - flexWrap: 'wrap', - ...wordBreak.breakWord, - }, - - // Sidebar Styles - sidebar: { - backgroundColor: theme.sidebar, - height: '100%', - }, - - sidebarHeaderContainer: { - flexDirection: 'row', - paddingHorizontal: 20, - paddingVertical: 19, - justifyContent: 'space-between', - alignItems: 'center', - }, - - subNavigationContainer: { - backgroundColor: theme.sidebar, - flex: 1, - borderTopLeftRadius: variables.componentBorderRadiusRounded, - }, - - sidebarAnimatedWrapperContainer: { - height: '100%', - position: 'absolute', - }, - - sidebarFooter: { - alignItems: 'center', - display: 'flex', - justifyContent: 'center', - paddingVertical: variables.lineHeightXLarge, - width: '100%', - }, - - sidebarAvatar: { - backgroundColor: theme.icon, - borderRadius: 20, - height: variables.componentSizeNormal, - width: variables.componentSizeNormal, - }, - - statusIndicator: (backgroundColor = theme.danger) => ({ - borderColor: theme.sidebar, - backgroundColor, - borderRadius: 8, - borderWidth: 2, - position: 'absolute', - right: -2, - top: -1, - height: 16, - width: 16, - zIndex: 10, - }), - - floatingActionButtonContainer: { - position: 'absolute', - left: 16, - // The bottom of the floating action button should align with the bottom of the compose box. - // The value should be equal to the height + marginBottom + marginTop of chatItemComposeSecondaryRow - bottom: 25, - }, - - floatingActionButton: { - backgroundColor: theme.success, - height: variables.componentSizeNormal, - width: variables.componentSizeNormal, - borderRadius: 999, - alignItems: 'center', - justifyContent: 'center', - }, - - sidebarFooterUsername: { - color: theme.heading, - fontSize: variables.fontSizeLabel, - fontWeight: '700', - width: 200, - textOverflow: 'ellipsis', - overflow: 'hidden', - ...whiteSpace.noWrap, - }, - - sidebarFooterLink: { - color: theme.textSupporting, - fontSize: variables.fontSizeSmall, - textDecorationLine: 'none', - fontFamily: fontFamily.EXP_NEUE, - lineHeight: 20, - }, - - sidebarListContainer: { - scrollbarWidth: 'none', - paddingBottom: 4, - }, - - sidebarListItem: { - justifyContent: 'center', - textDecorationLine: 'none', - }, - - RHPNavigatorContainer: (isSmallScreenWidth) => ({ - width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, - position: 'absolute', - right: 0, - height: '100%', - }), - - onlyEmojisText: { - fontSize: variables.fontSizeOnlyEmojis, - lineHeight: variables.fontSizeOnlyEmojisHeight, - }, - - onlyEmojisTextLineHeight: { - lineHeight: variables.fontSizeOnlyEmojisHeight, - }, - - createMenuPositionSidebar: (windowHeight) => ({ - horizontal: 18, - vertical: windowHeight - 75, - }), - - createMenuPositionProfile: (windowWidth) => ({ - horizontal: windowWidth - 355, - ...getPopOverVerticalOffset(162), - }), - - createMenuPositionReportActionCompose: (windowHeight) => ({ - horizontal: 18 + variables.sideBarWidth, - vertical: windowHeight - 83, - }), - - createMenuPositionRightSidepane: { - right: 18, - bottom: 75, - }, - - createMenuContainer: { - width: variables.sideBarWidth - 40, - paddingVertical: 12, - }, - - createMenuHeaderText: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.heading, - }, - - popoverMenuItem: { - flexDirection: 'row', - borderRadius: 0, - paddingHorizontal: 20, - paddingVertical: 12, - justifyContent: 'space-between', - width: '100%', - }, - - popoverMenuIcon: { - width: variables.componentSizeNormal, - justifyContent: 'center', - alignItems: 'center', - }, - - popoverMenuText: { - fontSize: variables.fontSizeNormal, - color: theme.heading, - }, - - popoverInnerContainer: { - paddingTop: 0, // adjusting this because the mobile modal adds additional padding that we don't need for our layout - maxHeight: '95%', - }, - - menuItemTextContainer: { - minHeight: variables.componentSizeNormal, - }, - - chatLinkRowPressable: { - minWidth: 0, - textDecorationLine: 'none', - flex: 1, - }, - - sidebarLink: { - textDecorationLine: 'none', - }, - - sidebarLinkLHN: { - textDecorationLine: 'none', - marginLeft: 12, - marginRight: 12, - borderRadius: 8, - }, - - sidebarLinkInner: { - alignItems: 'center', - flexDirection: 'row', - paddingLeft: 20, - paddingRight: 20, - }, - - sidebarLinkInnerLHN: { - alignItems: 'center', - flexDirection: 'row', - paddingLeft: 8, - paddingRight: 8, - }, - - sidebarLinkText: { - color: theme.textSupporting, - fontSize: variables.fontSizeNormal, - textDecorationLine: 'none', - overflow: 'hidden', - }, - - sidebarLinkHover: { - backgroundColor: theme.sidebarHover, - }, - - sidebarLinkHoverLHN: { - backgroundColor: theme.highlightBG, - }, - - sidebarLinkActive: { - backgroundColor: theme.border, - textDecorationLine: 'none', - }, - - sidebarLinkActiveLHN: { - backgroundColor: theme.highlightBG, - textDecorationLine: 'none', - }, - - sidebarLinkTextBold: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - color: theme.heading, - }, - - sidebarLinkActiveText: { - color: theme.textSupporting, - fontSize: variables.fontSizeNormal, - textDecorationLine: 'none', - overflow: 'hidden', - }, - - optionItemAvatarNameWrapper: { - minWidth: 0, - flex: 1, - }, - - optionDisplayName: { - fontFamily: fontFamily.EXP_NEUE, - minHeight: variables.alternateTextHeight, - lineHeight: variables.lineHeightXLarge, - ...whiteSpace.noWrap, - }, - - optionDisplayNameCompact: { - minWidth: 'auto', - flexBasis: 'auto', - flexGrow: 0, - flexShrink: 1, - }, - - displayNameTooltipEllipsis: { - position: 'absolute', - opacity: 0, - right: 0, - bottom: 0, - }, - - optionAlternateText: { - minHeight: variables.alternateTextHeight, - lineHeight: variables.lineHeightXLarge, - }, - - optionAlternateTextCompact: { - flexShrink: 1, - flexGrow: 1, - flexBasis: 'auto', - ...optionAlternateTextPlatformStyles, - }, - - optionRow: { - minHeight: variables.optionRowHeight, - paddingTop: 12, - paddingBottom: 12, - }, - - optionRowSelected: { - backgroundColor: theme.activeComponentBG, - }, - - optionRowDisabled: { - color: theme.textSupporting, - }, - - optionRowCompact: { - height: variables.optionRowHeightCompact, - paddingTop: 12, - paddingBottom: 12, - }, - - optionsListSectionHeader: { - marginTop: 8, - marginBottom: 4, - }, - - overlayStyles: (current) => ({ - position: 'fixed', - - // We need to stretch the overlay to cover the sidebar and the translate animation distance. - left: -2 * variables.sideBarWidth, - top: 0, - bottom: 0, - right: 0, - backgroundColor: theme.overlay, - opacity: current.progress.interpolate({ - inputRange: [0, 1], - outputRange: [0, variables.overlayOpacity], - extrapolate: 'clamp', - }), - }), - - appContent: { - backgroundColor: theme.appBG, - overflow: 'hidden', - }, - - appContentHeader: { - height: variables.contentHeaderHeight, - justifyContent: 'center', - display: 'flex', - paddingRight: 20, - }, - - appContentHeaderTitle: { - alignItems: 'center', - flexDirection: 'row', - }, - - LHNToggle: { - alignItems: 'center', - height: variables.contentHeaderHeight, - justifyContent: 'center', - paddingRight: 10, - paddingLeft: 20, - }, - - LHNToggleIcon: { - height: 15, - width: 18, - }, - - chatContentScrollView: { - flexGrow: 1, - justifyContent: 'flex-start', - paddingBottom: 16, - }, - - // Chat Item - chatItem: { - display: 'flex', - flexDirection: 'row', - paddingTop: 8, - paddingBottom: 8, - paddingLeft: 20, - paddingRight: 20, - }, - - chatItemRightGrouped: { - flexGrow: 1, - flexShrink: 1, - flexBasis: 0, - position: 'relative', - marginLeft: variables.chatInputSpacing, - }, - - chatItemRight: { - flexGrow: 1, - flexShrink: 1, - flexBasis: 0, - position: 'relative', - }, - - chatItemMessageHeader: { - alignItems: 'center', - display: 'flex', - flexDirection: 'row', - flexWrap: 'nowrap', - }, - - chatItemMessageHeaderSender: { - color: theme.heading, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - lineHeight: variables.lineHeightXLarge, - ...wordBreak.breakWord, - }, - - chatItemMessageHeaderTimestamp: { - flexShrink: 0, - color: theme.textSupporting, - fontSize: variables.fontSizeSmall, - paddingTop: 2, - }, - - chatItemMessage: { - color: theme.text, - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE, - lineHeight: variables.lineHeightXLarge, - maxWidth: '100%', - ...cursor.cursorAuto, - ...whiteSpace.preWrap, - ...wordBreak.breakWord, - }, - - renderHTMLTitle: { - color: theme.text, - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE, - lineHeight: variables.lineHeightXLarge, - maxWidth: '100%', - ...whiteSpace.preWrap, - ...wordBreak.breakWord, - }, - - chatItemComposeWithFirstRow: { - minHeight: 90, - }, - - chatItemFullComposeRow: { - ...sizing.h100, - }, - - chatItemComposeBoxColor: { - borderColor: theme.border, - }, - - chatItemComposeBoxFocusedColor: { - borderColor: theme.borderFocus, - }, - - chatItemComposeBox: { - backgroundColor: theme.componentBG, - borderWidth: 1, - borderRadius: variables.componentBorderRadiusRounded, - minHeight: variables.componentSizeMedium, - }, - - chatItemFullComposeBox: { - ...flex.flex1, - ...sizing.h100, - }, - - chatFooter: { - paddingLeft: 20, - paddingRight: 20, - display: 'flex', - backgroundColor: theme.appBG, - }, - - chatFooterFullCompose: { - flex: 1, - }, - - chatItemDraft: { - display: 'flex', - flexDirection: 'row', - paddingTop: 8, - paddingBottom: 8, - paddingLeft: 20, - paddingRight: 20, - }, - - chatItemReactionsDraftRight: { - marginLeft: 52, - }, - chatFooterAtTheTop: { - flexGrow: 1, - justifyContent: 'flex-start', - }, - - // Be extremely careful when editing the compose styles, as it is easy to introduce regressions. - // Make sure you run the following tests against any changes: #12669 - textInputCompose: addOutlineWidth( - { - backgroundColor: theme.componentBG, - borderColor: theme.border, - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - borderWidth: 0, - height: 'auto', - lineHeight: variables.lineHeightXLarge, - ...overflowXHidden, - - // On Android, multiline TextInput with height: 'auto' will show extra padding unless they are configured with - // paddingVertical: 0, alignSelf: 'center', and textAlignVertical: 'center' - - paddingHorizontal: variables.avatarChatSpacing, - paddingTop: 0, - paddingBottom: 0, - alignSelf: 'center', - textAlignVertical: 'center', - }, - 0, - ), - - textInputFullCompose: { - alignSelf: 'stretch', - flex: 1, - maxHeight: '100%', - textAlignVertical: 'top', - }, - - // composer padding should not be modified unless thoroughly tested against the cases in this PR: #12669 - textInputComposeSpacing: { - paddingVertical: 5, - ...flex.flexRow, - flex: 1, - }, - - textInputComposeBorder: { - borderLeftWidth: 1, - borderColor: theme.border, - }, - - chatItemSubmitButton: { - alignSelf: 'flex-end', - borderRadius: variables.componentBorderRadiusRounded, - backgroundColor: theme.transparent, - height: 40, - padding: 10, - margin: 3, - justifyContent: 'center', - }, - - emojiPickerContainer: { - backgroundColor: theme.componentBG, - }, - - emojiHeaderContainer: { - backgroundColor: theme.componentBG, - display: 'flex', - height: CONST.EMOJI_PICKER_HEADER_HEIGHT, - justifyContent: 'center', - width: '100%', - }, - - emojiSkinToneTitle: { - ...spacing.pv1, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - color: theme.heading, - fontSize: variables.fontSizeSmall, - }, - - // Emoji Picker Styles - emojiText: { - textAlign: 'center', - fontSize: variables.emojiSize, - ...spacing.pv0, - ...spacing.ph0, - lineHeight: variables.emojiLineHeight, - }, - - emojiItem: { - width: '12.5%', - textAlign: 'center', - borderRadius: 8, - paddingTop: 2, - paddingBottom: 2, - height: CONST.EMOJI_PICKER_ITEM_HEIGHT, - ...userSelect.userSelectNone, - }, - - emojiItemHighlighted: { - transition: '0.2s ease', - backgroundColor: theme.buttonDefaultBG, - }, - - emojiItemKeyboardHighlighted: { - transition: '0.2s ease', - borderWidth: 1, - borderColor: theme.link, - borderRadius: variables.buttonBorderRadius, - }, - - categoryShortcutButton: { - flex: 1, - borderRadius: 8, - height: CONST.EMOJI_PICKER_ITEM_HEIGHT, - alignItems: 'center', - justifyContent: 'center', - }, - - chatItemEmojiButton: { - alignSelf: 'flex-end', - borderRadius: variables.buttonBorderRadius, - height: 40, - marginVertical: 3, - paddingHorizontal: 10, - justifyContent: 'center', - }, - - editChatItemEmojiWrapper: { - marginRight: 3, - alignSelf: 'flex-end', - }, - - hoveredButton: { - backgroundColor: theme.buttonHoveredBG, - }, - - composerSizeButton: { - alignSelf: 'center', - height: 32, - width: 32, - padding: 6, - margin: 3, - borderRadius: variables.componentBorderRadiusRounded, - backgroundColor: theme.transparent, - justifyContent: 'center', - }, - - chatItemAttachmentPlaceholder: { - backgroundColor: theme.sidebar, - borderColor: theme.border, - borderWidth: 1, - borderRadius: variables.componentBorderRadiusNormal, - height: 150, - textAlign: 'center', - verticalAlign: 'middle', - width: 200, - }, - - sidebarVisible: { - borderRightWidth: 1, - }, - - sidebarHidden: { - width: 0, - borderRightWidth: 0, - }, - - exampleCheckImage: { - width: '100%', - height: 80, - borderColor: theme.border, - borderWidth: 1, - borderRadius: variables.componentBorderRadiusNormal, - }, - - singleAvatar: { - height: 24, - width: 24, - backgroundColor: theme.icon, - borderRadius: 24, - }, - - singleAvatarSmall: { - height: 18, - width: 18, - backgroundColor: theme.icon, - borderRadius: 18, - }, - - singleAvatarMedium: { - height: 52, - width: 52, - backgroundColor: theme.icon, - borderRadius: 52, - }, - - secondAvatar: { - position: 'absolute', - right: -18, - bottom: -18, - borderWidth: 3, - borderRadius: 30, - borderColor: 'transparent', - }, - - secondAvatarSmall: { - position: 'absolute', - right: -13, - bottom: -13, - borderWidth: 3, - borderRadius: 18, - borderColor: 'transparent', - }, - - secondAvatarMedium: { - position: 'absolute', - right: -36, - bottom: -36, - borderWidth: 3, - borderRadius: 52, - borderColor: 'transparent', - }, - - secondAvatarSubscript: { - position: 'absolute', - right: -6, - bottom: -6, - }, - - secondAvatarSubscriptCompact: { - position: 'absolute', - bottom: -1, - right: -1, - }, - - secondAvatarSubscriptSmallNormal: { - position: 'absolute', - bottom: 0, - right: 0, - }, - - secondAvatarInline: { - bottom: -3, - right: -25, - borderWidth: 3, - borderRadius: 18, - borderColor: theme.cardBorder, - backgroundColor: theme.appBG, - }, - - avatarLarge: { - width: variables.avatarSizeLarge, - height: variables.avatarSizeLarge, - }, - - avatarXLarge: { - width: variables.avatarSizeXLarge, - height: variables.avatarSizeXLarge, - }, - - avatarInnerText: { - color: theme.textLight, - fontSize: variables.fontSizeSmall, - lineHeight: undefined, - marginLeft: -3, - textAlign: 'center', - }, - - avatarInnerTextSmall: { - color: theme.textLight, - fontSize: variables.fontSizeExtraSmall, - lineHeight: undefined, - marginLeft: -2, - textAlign: 'center', - }, - - emptyAvatar: { - height: variables.avatarSizeNormal, - width: variables.avatarSizeNormal, - }, - - emptyAvatarSmallNormal: { - height: variables.avatarSizeSmallNormal, - width: variables.avatarSizeSmallNormal, - }, - - emptyAvatarSmall: { - height: variables.avatarSizeSmall, - width: variables.avatarSizeSmall, - }, - - emptyAvatarSmaller: { - height: variables.avatarSizeSmaller, - width: variables.avatarSizeSmaller, - }, - - emptyAvatarMedium: { - height: variables.avatarSizeMedium, - width: variables.avatarSizeMedium, - }, - - emptyAvatarLarge: { - height: variables.avatarSizeLarge, - width: variables.avatarSizeLarge, - }, - - emptyAvatarMargin: { - marginRight: variables.avatarChatSpacing, - }, - - emptyAvatarMarginChat: { - marginRight: variables.avatarChatSpacing - 12, - }, - - emptyAvatarMarginSmall: { - marginRight: variables.avatarChatSpacing - 4, - }, - - emptyAvatarMarginSmaller: { - marginRight: variables.avatarChatSpacing - 4, - }, - - borderTop: { - borderTopWidth: variables.borderTopWidth, - borderColor: theme.border, - }, - - borderTopRounded: { - borderTopWidth: 1, - borderColor: theme.border, - borderTopLeftRadius: variables.componentBorderRadiusNormal, - borderTopRightRadius: variables.componentBorderRadiusNormal, - }, - - borderBottomRounded: { - borderBottomWidth: 1, - borderColor: theme.border, - borderBottomLeftRadius: variables.componentBorderRadiusNormal, - borderBottomRightRadius: variables.componentBorderRadiusNormal, - }, - - borderBottom: { - borderBottomWidth: 1, - borderColor: theme.border, - }, - - borderNone: { - borderWidth: 0, - borderBottomWidth: 0, - }, - - borderRight: { - borderRightWidth: 1, - borderColor: theme.border, - }, - - borderLeft: { - borderLeftWidth: 1, - borderColor: theme.border, - }, - - pointerEventsNone, - - pointerEventsAuto, - - headerBar: { - overflow: 'hidden', - justifyContent: 'center', - display: 'flex', - paddingLeft: 20, - height: variables.contentHeaderHeight, - width: '100%', - }, - - imageViewContainer: { - width: '100%', - height: '100%', - alignItems: 'center', - justifyContent: 'center', - }, - - imageModalPDF: { - flex: 1, - backgroundColor: theme.modalBackground, - }, - - PDFView: { - // `display: grid` is not supported in native platforms! - // It's being used on Web/Desktop only to vertically center short PDFs, - // while preventing the overflow of the top of long PDF files. - display: 'grid', - backgroundColor: theme.modalBackground, - width: '100%', - height: '100%', - justifyContent: 'center', - overflow: 'hidden', - alignItems: 'center', - }, - - PDFViewList: { - overflowX: 'hidden', - // There properties disable "focus" effect on list - boxShadow: 'none', - outline: 'none', - }, - - getPDFPasswordFormStyle: (isSmallScreenWidth) => ({ - width: isSmallScreenWidth ? '100%' : 350, - ...(isSmallScreenWidth && flex.flex1), - }), - - centeredModalStyles: (isSmallScreenWidth, isFullScreenWhenSmall) => ({ - borderWidth: isSmallScreenWidth && !isFullScreenWhenSmall ? 1 : 0, - marginHorizontal: isSmallScreenWidth ? 0 : 20, - }), - - imageModalImageCenterContainer: { - alignItems: 'center', - flex: 1, - justifyContent: 'center', - width: '100%', - }, - - defaultAttachmentView: { - backgroundColor: theme.sidebar, - borderRadius: variables.componentBorderRadiusNormal, - borderWidth: 1, - borderColor: theme.border, - flexDirection: 'row', - padding: 20, - alignItems: 'center', - }, - - notFoundTextHeader: { - ...headlineFont, - color: theme.heading, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - marginTop: 20, - marginBottom: 8, - textAlign: 'center', - }, - - blockingViewContainer: { - paddingBottom: variables.contentHeaderHeight, - }, - - defaultModalContainer: { - backgroundColor: theme.componentBG, - borderColor: theme.transparent, - }, - - reportActionContextMenuMiniButton: { - ...spacing.p1, - ...spacing.mv1, - ...spacing.mh1, - ...{borderRadius: variables.buttonBorderRadius}, - }, - - reportActionSystemMessageContainer: { - marginLeft: 42, - }, - - reportDetailsTitleContainer: { - ...flex.dFlex, - ...flex.flexColumn, - ...flex.alignItemsCenter, - paddingHorizontal: 20, - paddingBottom: 20, - }, - - reportDetailsRoomInfo: { - ...flex.flex1, - ...flex.dFlex, - ...flex.flexColumn, - ...flex.alignItemsCenter, - }, - - reportSettingsVisibilityText: { - textTransform: 'capitalize', - }, - - settingsPageBackground: { - flexDirection: 'column', - width: '100%', - flexGrow: 1, - }, - - settingsPageBody: { - width: '100%', - justifyContent: 'space-around', - }, - - twoFactorAuthSection: { - backgroundColor: theme.appBG, - padding: 0, - }, - - twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}) => { - let paddingHorizontal = styles.ph9; - - if (isSmallScreenWidth) { - paddingHorizontal = styles.ph4; - } - - if (isExtraSmallScreenWidth) { - paddingHorizontal = styles.ph2; - } - - return { - alignItems: 'center', - justifyContent: 'center', - backgroundColor: theme.highlightBG, - paddingVertical: 28, - borderRadius: 16, - marginTop: 32, - ...paddingHorizontal, - }; - }, - - twoFactorLoadingContainer: { - alignItems: 'center', - justifyContent: 'center', - height: 210, - }, - - twoFactorAuthCodesContainer: { - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - flexWrap: 'wrap', - gap: 12, - }, - - twoFactorAuthCode: { - fontFamily: fontFamily.MONOSPACE, - width: 112, - textAlign: 'center', - }, - - twoFactorAuthCodesButtonsContainer: { - flexDirection: 'row', - justifyContent: 'center', - gap: 12, - marginTop: 20, - flexWrap: 'wrap', - }, - - twoFactorAuthCodesButton: { - minWidth: 112, - }, - - twoFactorAuthCopyCodeButton: { - minWidth: 110, - }, - - anonymousRoomFooter: (isSmallSizeLayout) => ({ - flexDirection: isSmallSizeLayout ? 'column' : 'row', - ...(!isSmallSizeLayout && { - alignItems: 'center', - justifyContent: 'space-between', - }), - padding: 20, - backgroundColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusLarge, - overflow: 'hidden', - }), - anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout) => ({ - flexDirection: 'row', - alignItems: 'center', - ...(isSmallSizeLayout && { - justifyContent: 'space-between', - marginTop: 16, - }), - }), - anonymousRoomFooterLogo: { - width: 88, - marginLeft: 0, - height: 20, - }, - anonymousRoomFooterLogoTaglineText: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeMedium, - color: theme.textLight, - }, - signInButtonAvatar: { - width: 80, - }, - - anonymousRoomFooterSignInButton: { - width: 110, - }, - - roomHeaderAvatarSize: { - height: variables.componentSizeLarge, - width: variables.componentSizeLarge, - }, - - roomHeaderAvatar: { - backgroundColor: theme.appBG, - borderRadius: 100, - borderColor: theme.componentBG, - borderWidth: 4, - }, - - roomHeaderAvatarOverlay: { - position: 'absolute', - top: 0, - right: 0, - bottom: 0, - left: 0, - backgroundColor: theme.overlay, - opacity: variables.overlayOpacity, - borderRadius: 88, - }, - - rootNavigatorContainerStyles: (isSmallScreenWidth) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), - RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), - - avatarInnerTextChat: { - color: theme.textLight, - fontSize: variables.fontSizeXLarge, - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - textAlign: 'center', - fontWeight: 'normal', - position: 'absolute', - width: 88, - left: -16, - }, - - pageWrapper: { - width: '100%', - alignItems: 'center', - padding: 20, - }, - - avatarSectionWrapper: { - width: '100%', - alignItems: 'center', - paddingHorizontal: 20, - paddingBottom: 20, - }, - - avatarSectionWrapperSkeleton: { - width: '100%', - paddingHorizontal: 20, - paddingBottom: 20, - }, - - selectCircle: { - width: variables.componentSizeSmall, - height: variables.componentSizeSmall, - borderColor: theme.border, - borderWidth: 1, - borderRadius: variables.componentSizeSmall / 2, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: theme.componentBG, - marginLeft: 8, - }, - - unreadIndicatorContainer: { - position: 'absolute', - top: -10, - left: 0, - width: '100%', - height: 20, - paddingHorizontal: 20, - flexDirection: 'row', - alignItems: 'center', - zIndex: 1, - ...cursor.cursorDefault, - }, - - unreadIndicatorLine: { - height: 1, - backgroundColor: theme.unreadIndicator, - flexGrow: 1, - marginRight: 8, - opacity: 0.5, - }, - - threadDividerLine: { - height: 1, - backgroundColor: theme.border, - flexGrow: 1, - marginHorizontal: 20, - }, - - unreadIndicatorText: { - color: theme.unreadIndicator, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeSmall, - fontWeight: fontWeightBold, - textTransform: 'capitalize', - }, - - flipUpsideDown: { - transform: [{rotate: '180deg'}], - }, - - navigationScreenCardStyle: { - backgroundColor: theme.appBG, - height: '100%', - }, - - invisible: { - position: 'absolute', - opacity: 0, - }, - - invisiblePopover: { - position: 'absolute', - opacity: 0, - left: -9999, - }, - - containerWithSpaceBetween: { - justifyContent: 'space-between', - width: '100%', - flex: 1, - }, - - detailsPageSectionContainer: { - alignSelf: 'flex-start', - }, - - attachmentCarouselContainer: { - height: '100%', - width: '100%', - display: 'flex', - justifyContent: 'center', - ...cursor.cursorUnset, - }, - - attachmentArrow: { - zIndex: 23, - position: 'absolute', - }, - - attachmentRevealButtonContainer: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - ...spacing.ph4, - }, - - arrowIcon: { - height: 40, - width: 40, - alignItems: 'center', - paddingHorizontal: 0, - paddingTop: 0, - paddingBottom: 0, - }, - - switchTrack: { - width: 50, - height: 28, - justifyContent: 'center', - borderRadius: 20, - padding: 15, - backgroundColor: theme.success, - }, - - switchInactive: { - backgroundColor: theme.border, - }, - - switchThumb: { - width: 22, - height: 22, - borderRadius: 11, - position: 'absolute', - left: 4, - backgroundColor: theme.appBG, - }, - - switchThumbTransformation: (translateX) => ({ - transform: [{translateX}], - }), - - radioButtonContainer: { - backgroundColor: theme.componentBG, - borderRadius: 10, - height: 20, - width: 20, - borderColor: theme.icon, - borderWidth: 1, - justifyContent: 'center', - alignItems: 'center', - }, - - checkedContainer: { - backgroundColor: theme.checkBox, - }, - - magicCodeInputContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - minHeight: variables.inputHeight, - }, - - magicCodeInput: { - fontSize: variables.fontSizeXLarge, - color: theme.heading, - lineHeight: variables.inputHeight, - }, - - // Manually style transparent, in iOS Safari, an input in a container with its opacity set to - // 0 (completely transparent) cannot handle user interaction, hence the Paste option is never shown - inputTransparent: { - color: 'transparent', - // These properties are available in browser only - ...(Browser.getBrowser() - ? { - caretColor: 'transparent', - WebkitTextFillColor: 'transparent', - // After setting the input text color to transparent, it acquires the background-color. - // However, it is not possible to override the background-color directly as explained in this resource: https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill - // Therefore, the transition effect needs to be delayed. - transitionDelay: '99999s', - transitionProperty: 'background-color', - } - : {}), - }, - - iouAmountText: { - ...headlineFont, - fontSize: variables.iouAmountTextSize, - color: theme.heading, - lineHeight: variables.inputHeight, - }, - - iouAmountTextInput: addOutlineWidth( - { - ...headlineFont, - fontSize: variables.iouAmountTextSize, - color: theme.heading, - padding: 0, - lineHeight: undefined, - }, - 0, - ), - - moneyRequestConfirmationAmount: { - ...headlineFont, - fontSize: variables.fontSizeh1, - }, - - moneyRequestMenuItem: { - flexDirection: 'row', - borderRadius: 0, - justifyContent: 'space-between', - width: '100%', - paddingHorizontal: 20, - paddingVertical: 12, - }, - - requestPreviewBox: { - marginTop: 12, - maxWidth: variables.reportPreviewMaxWidth, - }, - - moneyRequestPreviewBox: { - backgroundColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusLarge, - maxWidth: variables.reportPreviewMaxWidth, - width: '100%', - }, - - moneyRequestPreviewBoxText: { - padding: 16, - }, - - amountSplitPadding: { - paddingTop: 2, - }, - - moneyRequestPreviewBoxLoading: { - // When a new IOU request arrives it is very briefly in a loading state, so set the minimum height of the container to 94 to match the rendered height after loading. - // Otherwise, the IOU request pay button will not be fully visible and the user will have to scroll up to reveal the entire IOU request container. - // See https://github.com/Expensify/App/issues/10283. - minHeight: 94, - width: '100%', - }, - - moneyRequestPreviewBoxAvatar: { - marginRight: -10, - marginBottom: 0, - }, - - moneyRequestPreviewAmount: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - }, - - defaultCheckmarkWrapper: { - marginLeft: 8, - alignSelf: 'center', - }, - - codeWordWrapper: { - ...codeStyles.codeWordWrapper, - }, - - codeWordStyle: { - borderLeftWidth: 0, - borderRightWidth: 0, - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - paddingLeft: 0, - paddingRight: 0, - justifyContent: 'center', - ...codeStyles.codeWordStyle, - }, - - codeFirstWordStyle: { - borderLeftWidth: 1, - borderTopLeftRadius: 4, - borderBottomLeftRadius: 4, - paddingLeft: 5, - }, - - codeLastWordStyle: { - borderRightWidth: 1, - borderTopRightRadius: 4, - borderBottomRightRadius: 4, - paddingRight: 5, - }, - - fullScreenLoading: { - backgroundColor: theme.componentBG, - opacity: 0.8, - justifyContent: 'center', - alignItems: 'center', - zIndex: 10, - }, - - reimbursementAccountFullScreenLoading: { - backgroundColor: theme.componentBG, - opacity: 0.8, - justifyContent: 'flex-start', - alignItems: 'center', - zIndex: 10, - }, - - hiddenElementOutsideOfWindow: { - position: 'absolute', - top: -10000, - left: 0, - opacity: 0, - }, - - growlNotificationWrapper: { - zIndex: 2, - }, - - growlNotificationContainer: { - flex: 1, - justifyContent: 'flex-start', - position: 'absolute', - width: '100%', - top: 20, - ...spacing.pl5, - ...spacing.pr5, - }, - - growlNotificationDesktopContainer: { - maxWidth: variables.sideBarWidth, - right: 0, - position: 'fixed', - }, - - growlNotificationTranslateY: (y) => ({ - transform: [{translateY: y}], - }), - - makeSlideInTranslation: (translationType, fromValue) => ({ - from: { - [translationType]: fromValue, - }, - to: { - [translationType]: 0, - }, - }), - - growlNotificationBox: { - backgroundColor: theme.inverse, - borderRadius: variables.componentBorderRadiusNormal, - alignItems: 'center', - flexDirection: 'row', - justifyContent: 'space-between', - shadowColor: theme.shadow, - ...spacing.p5, - }, - - growlNotificationText: { - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE, - width: '90%', - lineHeight: variables.fontSizeNormalHeight, - color: theme.textReversed, - ...spacing.ml4, - }, - - blockquote: { - borderLeftColor: theme.border, - borderLeftWidth: 4, - paddingLeft: 12, - marginVertical: 4, - }, - - noSelect: { - boxShadow: 'none', - outlineStyle: 'none', - }, - - cardStyleNavigator: { - overflow: 'hidden', - height: '100%', - }, - - smallEditIcon: { - alignItems: 'center', - backgroundColor: theme.buttonHoveredBG, - borderColor: theme.textReversed, - borderRadius: 14, - borderWidth: 3, - color: theme.textReversed, - height: 28, - width: 28, - justifyContent: 'center', - }, - - smallAvatarEditIcon: { - position: 'absolute', - right: -4, - bottom: -4, - }, - - autoGrowHeightMultilineInput: { - maxHeight: 115, - }, - - peopleRow: { - width: '100%', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - ...spacing.ph5, - }, - - peopleRowBorderBottom: { - borderColor: theme.border, - borderBottomWidth: 1, - ...spacing.pb2, - }, - - peopleBadge: { - backgroundColor: theme.icon, - ...spacing.ph3, - }, - - peopleBadgeText: { - color: theme.textReversed, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightNormal, - ...whiteSpace.noWrap, - }, - - offlineFeedback: { - deleted: { - textDecorationLine: 'line-through', - textDecorationStyle: 'solid', - }, - pending: { - opacity: 0.5, - }, - error: { - flexDirection: 'row', - alignItems: 'center', - }, - container: { - ...spacing.pv2, - }, - textContainer: { - flexDirection: 'column', - flex: 1, - }, - text: { - color: theme.textSupporting, - textAlignVertical: 'center', - fontSize: variables.fontSizeLabel, - }, - errorDot: { - marginRight: 12, - }, - }, - - dotIndicatorMessage: { - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - }, - - locationErrorLinkText: { - textAlignVertical: 'center', - fontSize: variables.fontSizeLabel, - }, - - sidebarPopover: { - width: variables.sideBarWidth - 68, - }, - - cardOverlay: { - backgroundColor: theme.overlay, - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - opacity: variables.overlayOpacity, - }, - - shortTermsBorder: { - borderWidth: 1, - borderColor: theme.border, - }, - - shortTermsHorizontalRule: { - borderBottomWidth: 1, - borderColor: theme.border, - ...spacing.mh3, - }, - - shortTermsLargeHorizontalRule: { - borderWidth: 1, - borderColor: theme.border, - ...spacing.mh3, - }, - - shortTermsRow: { - flexDirection: 'row', - padding: 12, - }, - - termsCenterRight: { - marginTop: 'auto', - marginBottom: 'auto', - }, - - shortTermsBoldHeadingSection: { - paddingRight: 12, - paddingLeft: 12, - marginTop: 12, - }, - - shortTermsHeadline: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - fontSize: variables.fontSizeXXXLarge, - lineHeight: variables.lineHeightXXXLarge, - }, - - longTermsRow: { - flexDirection: 'row', - marginTop: 20, - }, - - collapsibleSectionBorder: { - borderBottomWidth: 2, - borderBottomColor: theme.border, - }, - - communicationsLinkHeight: { - height: variables.communicationsLinkHeight, - }, - - floatingMessageCounterWrapper: { - position: 'absolute', - left: '50%', - top: 0, - zIndex: 100, - ...visibility.hidden, - }, - - floatingMessageCounterWrapperAndroid: { - left: 0, - width: '100%', - alignItems: 'center', - position: 'absolute', - top: 0, - zIndex: 100, - ...visibility.hidden, - }, - - floatingMessageCounterSubWrapperAndroid: { - left: '50%', - width: 'auto', - }, - - floatingMessageCounter: { - left: '-50%', - ...visibility.visible, - }, - - floatingMessageCounterTransformation: (translateY) => ({ - transform: [{translateY}], - }), - - confirmationAnimation: { - height: 180, - width: 180, - marginBottom: 20, - }, - - googleSearchTextInputContainer: { - flexDirection: 'column', - }, - - googleSearchSeparator: { - height: 1, - backgroundColor: theme.border, - }, - - googleSearchText: { - color: theme.text, - fontSize: variables.fontSizeNormal, - lineHeight: variables.fontSizeNormalHeight, - fontFamily: fontFamily.EXP_NEUE, - flex: 1, - }, - - threeDotsPopoverOffset: (windowWidth) => ({ - ...getPopOverVerticalOffset(60), - horizontal: windowWidth - 60, - }), - - threeDotsPopoverOffsetNoCloseButton: (windowWidth) => ({ - ...getPopOverVerticalOffset(60), - horizontal: windowWidth - 10, - }), - - threeDotsPopoverOffsetAttachmentModal: (windowWidth) => ({ - ...getPopOverVerticalOffset(80), - horizontal: windowWidth - 140, - }), - - iPhoneXSafeArea: { - backgroundColor: theme.inverse, - flex: 1, - }, - - transferBalancePayment: { - borderWidth: 1, - borderRadius: variables.componentBorderRadiusNormal, - borderColor: theme.border, - }, - - transferBalanceSelectedPayment: { - borderColor: theme.iconSuccessFill, - }, - - transferBalanceBalance: { - fontSize: 48, - }, - - imageCropContainer: { - overflow: 'hidden', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: theme.imageCropBackgroundColor, - ...cursor.cursorMove, - }, - - sliderKnobTooltipView: { - height: variables.sliderKnobSize, - width: variables.sliderKnobSize, - borderRadius: variables.sliderKnobSize / 2, - }, - - sliderKnob: { - backgroundColor: theme.success, - position: 'absolute', - height: variables.sliderKnobSize, - width: variables.sliderKnobSize, - borderRadius: variables.sliderKnobSize / 2, - left: -(variables.sliderKnobSize / 2), - ...cursor.cursorPointer, - }, - - sliderBar: { - backgroundColor: theme.border, - height: variables.sliderBarHeight, - borderRadius: variables.sliderBarHeight / 2, - alignSelf: 'stretch', - justifyContent: 'center', - }, - - screenCenteredContainer: { - flex: 1, - justifyContent: 'center', - marginBottom: 40, - padding: 16, - }, - - inlineSystemMessage: { - color: theme.textSupporting, - fontSize: variables.fontSizeLabel, - fontFamily: fontFamily.EXP_NEUE, - marginLeft: 6, - }, - - fullScreen: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, - - invisibleOverlay: { - backgroundColor: theme.transparent, - zIndex: 1000, - }, - - reportDropOverlay: { - backgroundColor: theme.dropUIBG, - zIndex: 2, - }, - - receiptDropOverlay: { - backgroundColor: theme.receiptDropUIBG, - zIndex: 2, - }, - - receiptImageWrapper: (receiptImageTopPosition) => ({ - position: 'absolute', - top: receiptImageTopPosition, - }), - - cardSection: { - backgroundColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusCard, - marginBottom: 20, - marginHorizontal: 16, - padding: 20, - width: 'auto', - textAlign: 'left', - }, - - cardSectionTitle: { - lineHeight: variables.lineHeightXXLarge, - }, - - cardMenuItem: { - paddingLeft: 0, - paddingRight: 0, - borderRadius: variables.buttonBorderRadius, - height: variables.componentSizeLarge, - alignItems: 'center', - }, - - transferBalance: { - paddingLeft: 20, - paddingRight: 20, - borderRadius: 0, - height: 64, - alignItems: 'center', - }, - - paymentMethod: { - paddingHorizontal: 20, - height: 64, - }, - - archivedReportFooter: { - borderRadius: variables.componentBorderRadius, - ...wordBreak.breakWord, - }, - - deeplinkWrapperContainer: { - padding: 20, - flex: 1, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: theme.appBG, - }, - - deeplinkWrapperMessage: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - - deeplinkWrapperFooter: { - paddingTop: 80, - paddingBottom: 45, - }, - - emojiReactionBubble: { - borderRadius: 28, - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - alignSelf: 'flex-start', - }, - - emojiReactionListHeader: { - marginTop: 8, - paddingBottom: 20, - borderBottomColor: theme.border, - borderBottomWidth: 1, - marginHorizontal: 20, - }, - emojiReactionListHeaderBubble: { - paddingVertical: 2, - paddingHorizontal: 8, - borderRadius: 28, - backgroundColor: theme.border, - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - alignSelf: 'flex-start', - marginRight: 4, - }, - - reactionListHeaderText: { - color: theme.textSupporting, - marginLeft: 8, - alignSelf: 'center', - }, - - miniQuickEmojiReactionText: { - fontSize: 15, - lineHeight: 20, - textAlignVertical: 'center', - }, - - emojiReactionBubbleText: { - textAlignVertical: 'center', - }, - - reactionCounterText: { - fontSize: 13, - marginLeft: 4, - fontWeight: 'bold', - }, - - fontColorReactionLabel: { - color: theme.tooltipSupportingText, - }, - - reactionEmojiTitle: { - fontSize: variables.iconSizeLarge, - lineHeight: variables.iconSizeXLarge, - }, - - textReactionSenders: { - color: theme.tooltipPrimaryText, - ...wordBreak.breakWord, - }, - - quickReactionsContainer: { - gap: 12, - flexDirection: 'row', - paddingHorizontal: 25, - paddingVertical: 12, - justifyContent: 'space-between', - }, - - reactionListContainer: { - maxHeight: variables.listItemHeightNormal * 5.75, - ...spacing.pv2, - }, - - reactionListContainerFixedWidth: { - maxWidth: variables.popoverWidth, - }, - - validateCodeDigits: { - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeXXLarge, - letterSpacing: 4, - }, - - footerWrapper: { - fontSize: variables.fontSizeNormal, - paddingTop: 64, - maxWidth: 1100, // Match footer across all Expensify platforms - }, - - footerColumnsContainer: { - flex: 1, - flexWrap: 'wrap', - marginBottom: 40, - marginHorizontal: -16, - }, - - footerTitle: { - fontSize: variables.fontSizeLarge, - color: theme.success, - marginBottom: 16, - }, - - footerRow: { - paddingVertical: 4, - marginBottom: 8, - color: theme.textLight, - fontSize: variables.fontSizeMedium, - }, - - footerBottomLogo: { - marginTop: 40, - width: '100%', - }, - - datePickerRoot: { - position: 'relative', - zIndex: 99, - }, - - datePickerPopover: { - backgroundColor: theme.appBG, - width: '100%', - alignSelf: 'center', - zIndex: 100, - marginTop: 8, - }, - - loginHeroHeader: { - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - color: theme.success, - fontWeight: '500', - textAlign: 'center', - }, - - newKansasLarge: { - ...headlineFont, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - }, - - eReceiptAmount: { - ...headlineFont, - fontSize: variables.fontSizeXXXLarge, - lineHeight: variables.lineHeightXXXLarge, - color: colors.green400, - }, - - eReceiptAmountLarge: { - ...headlineFont, - fontSize: variables.fontSizeEReceiptLarge, - lineHeight: variables.lineHeightXXsLarge, - wordBreak: 'break-word', - textAlign: 'center', - }, - - eReceiptCurrency: { - ...headlineFont, - fontSize: variables.fontSizeXXLarge, - lineHeight: variables.lineHeightXXLarge, - wordBreak: 'break-all', - }, - - eReceiptMerchant: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - color: theme.text, - }, - - eReceiptWaypointTitle: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - color: colors.green400, - }, - - eReceiptWaypointAddress: { - fontFamily: fontFamily.MONOSPACE, - fontSize: variables.fontSizeNormal, - lineHeight: variables.lineHeightNormal, - color: theme.textColorfulBackground, - }, - - eReceiptGuaranteed: { - fontFamily: fontFamily.MONOSPACE, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - color: theme.textColorfulBackground, - }, - - eReceiptBackground: { - ...sizing.w100, - borderRadius: 20, - position: 'absolute', - top: 0, - left: 0, - height: 540, - }, - - eReceiptPanel: { - ...spacing.p5, - ...spacing.pb8, - ...spacing.m5, - backgroundColor: colors.green800, - borderRadius: 20, - width: 335, - }, - - eReceiptBackgroundThumbnail: { - ...sizing.w100, - position: 'absolute', - aspectRatio: 335 / 540, - top: 0, - minWidth: 217, - }, - - eReceiptContainer: { - flex: 1, - width: 335, - minHeight: 540, - borderRadius: 20, - overflow: 'hidden', - }, - - loginHeroBody: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSignInHeroBody, - color: theme.textLight, - textAlign: 'center', - }, - - linkPreviewWrapper: { - marginTop: 16, - borderLeftWidth: 4, - borderLeftColor: theme.border, - paddingLeft: 12, - }, - - linkPreviewImage: { - flex: 1, - resizeMode: 'contain', - borderRadius: 8, - marginTop: 8, - }, - - linkPreviewLogoImage: { - height: 16, - width: 16, - }, - - contextMenuItemPopoverMaxWidth: { - maxWidth: 375, - }, - - formSpaceVertical: { - height: 20, - width: 1, - }, - - taskCheckbox: { - height: 16, - width: 16, - }, - - taskTitleMenuItem: { - ...writingDirection.ltr, - ...headlineFont, - ...spacing.flexWrap, - ...spacing.flex1, - fontSize: variables.fontSizeXLarge, - maxWidth: '100%', - ...wordBreak.breakWord, - }, - - taskDescriptionMenuItem: { - maxWidth: '100%', - ...wordBreak.breakWord, - }, - - taskTitleDescription: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.textSupporting, - lineHeight: variables.lineHeightNormal, - ...spacing.mb1, - }, - - taskMenuItemCheckbox: { - height: 27, - ...spacing.mr3, - }, - - reportHorizontalRule: { - borderColor: theme.border, - ...spacing.mh5, - }, - - assigneeTextStyle: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - minHeight: variables.avatarSizeSubscript, - }, - - taskRightIconContainer: { - width: variables.componentSizeNormal, - marginLeft: 'auto', - ...spacing.mt1, - ...pointerEventsAuto, - ...spacing.dFlex, - ...spacing.alignItemsCenter, - }, - - shareCodePage: { - paddingHorizontal: 38.5, - }, - - shareCodeContainer: { - width: '100%', - alignItems: 'center', - paddingHorizontal: variables.qrShareHorizontalPadding, - paddingVertical: 20, - borderRadius: 20, - overflow: 'hidden', - borderColor: theme.borderFocus, - borderWidth: 2, - backgroundColor: theme.highlightBG, - }, - - splashScreenHider: { - backgroundColor: theme.splashBG, - alignItems: 'center', - justifyContent: 'center', - }, - - headerEnvBadge: { - marginLeft: 0, - marginBottom: 2, - height: 12, - paddingLeft: 4, - paddingRight: 4, - alignItems: 'center', - }, - - headerEnvBadgeText: { - fontSize: 7, - fontWeight: fontWeightBold, - lineHeight: undefined, - }, - - expensifyQrLogo: { - alignSelf: 'stretch', - height: 27, - marginBottom: 20, - }, - - qrShareTitle: { - marginTop: 15, - textAlign: 'center', - }, - - loginButtonRow: { - width: '100%', - gap: 12, - ...flex.flexRow, - ...flex.justifyContentCenter, - }, - - loginButtonRowSmallScreen: { - width: '100%', - gap: 12, - ...flex.flexRow, - ...flex.justifyContentCenter, - marginBottom: 10, - }, - - desktopSignInButtonContainer: { - width: 40, - height: 40, - }, - - signInIconButton: { - paddingVertical: 2, - }, - - googleButtonContainer: { - colorScheme: 'light', - width: 40, - height: 40, - alignItems: 'center', - overflow: 'hidden', - }, - - googlePillButtonContainer: { - colorScheme: 'light', - height: 40, - width: 219, - }, - - thirdPartyLoadingContainer: { - alignItems: 'center', - justifyContent: 'center', - height: 450, - }, - - tabSelectorButton: { - height: variables.tabSelectorButtonHeight, - padding: variables.tabSelectorButtonPadding, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - borderRadius: variables.buttonBorderRadius, - }, - - tabSelector: { - flexDirection: 'row', - paddingHorizontal: 20, - paddingBottom: 12, - }, - - tabText: (isSelected) => ({ - marginLeft: 8, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - color: isSelected ? theme.textLight : theme.textSupporting, - }), - - tabBackground: (hovered, isFocused, background) => ({ - backgroundColor: hovered && !isFocused ? theme.highlightBG : background, - }), - - tabOpacity: (hovered, isFocused, activeOpacityValue, inactiveOpacityValue) => (hovered && !isFocused ? inactiveOpacityValue : activeOpacityValue), - - /** - * @param {String} backgroundColor - * @param {Number} height - * @returns {Object} - */ - overscrollSpacer: (backgroundColor, height) => ({ - backgroundColor, - height, - width: '100%', - position: 'absolute', - top: -height, - left: 0, - right: 0, - }), - - dualColorOverscrollSpacer: { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - zIndex: -1, - }, - - willChangeTransform: { - willChange: 'transform', - }, - - dropDownButtonCartIconContainerPadding: { - paddingRight: 0, - paddingLeft: 0, - }, - - dropDownButtonArrowContain: { - marginLeft: 12, - marginRight: 14, - }, - - dropDownButtonCartIconView: { - borderTopRightRadius: variables.buttonBorderRadius, - borderBottomRightRadius: variables.buttonBorderRadius, - ...flex.flexRow, - ...flex.alignItemsCenter, - }, - - emojiPickerButtonDropdown: { - justifyContent: 'center', - backgroundColor: theme.activeComponentBG, - width: 86, - height: 52, - borderRadius: 26, - alignItems: 'center', - paddingLeft: 10, - paddingRight: 4, - marginBottom: 32, - alignSelf: 'flex-start', - }, - - emojiPickerButtonDropdownIcon: { - fontSize: 30, - }, - - moneyRequestImage: { - height: 200, - borderRadius: 16, - margin: 20, - }, - - reportPreviewBox: { - backgroundColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusLarge, - maxWidth: variables.sideBarWidth, - width: '100%', - }, - - reportPreviewBoxHoverBorder: { - borderColor: theme.border, - backgroundColor: theme.border, - }, - - reportContainerBorderRadius: { - borderRadius: variables.componentBorderRadiusLarge, - }, - - reportPreviewBoxBody: { - padding: 16, - }, - - reportActionItemImages: { - flexDirection: 'row', - borderWidth: 4, - borderColor: theme.transparent, - borderTopLeftRadius: variables.componentBorderRadiusLarge, - borderTopRightRadius: variables.componentBorderRadiusLarge, - overflow: 'hidden', - height: variables.reportActionImagesSingleImageHeight, - }, - - reportActionItemImage: { - flex: 1, - width: '100%', - height: '100%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - }, - - reportActionItemImageBorder: { - borderRightWidth: 4, - borderColor: theme.cardBG, - }, - - reportActionItemImagesMoreContainer: { - position: 'absolute', - bottom: 0, - right: 0, - display: 'flex', - }, - - reportActionItemImagesMore: { - borderTopLeftRadius: 12, - backgroundColor: theme.border, - width: 40, - height: 40, - }, - - reportActionItemImagesMoreHovered: { - backgroundColor: theme.cardBG, - }, - - reportActionItemImagesMoreText: { - position: 'absolute', - marginLeft: 20, - marginTop: 16, - color: theme.textSupporting, - }, - - reportActionItemImagesMoreCornerTriangle: { - position: 'absolute', - bottom: 0, - right: 0, - width: 0, - height: 0, - borderStyle: 'solid', - borderWidth: 0, - borderBottomWidth: 40, - borderLeftWidth: 40, - borderColor: 'transparent', - borderBottomColor: theme.cardBG, - }, - - reportActionItemImagesMoreCornerTriangleHighlighted: { - borderColor: 'transparent', - borderBottomColor: theme.border, - }, - - moneyRequestHeaderStatusBarBadge: { - paddingHorizontal: 8, - borderRadius: variables.componentBorderRadiusSmall, - height: variables.inputHeightSmall, - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - backgroundColor: theme.border, - marginRight: 12, - }, - - staticHeaderImage: { - minHeight: 240, - }, - - emojiPickerButtonDropdownContainer: { - flexDirection: 'row', - alignItems: 'center', - }, - - rotate90: { - transform: [{rotate: '90deg'}], - }, - - emojiStatusLHN: { - fontSize: 22, - }, - sidebarStatusAvatarContainer: { - height: 44, - width: 84, - backgroundColor: theme.componentBG, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - borderRadius: 42, - paddingHorizontal: 2, - marginVertical: -2, - marginRight: -2, - }, - sidebarStatusAvatar: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - - moneyRequestViewImage: { - ...spacing.mh5, - ...spacing.mv3, - overflow: 'hidden', - borderWidth: 2, - borderColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusLarge, - height: 200, - maxWidth: 400, - }, - - mapViewContainer: { - ...flex.flex1, - ...spacing.p4, - ...spacing.flex1, - minHeight: 300, - maxHeight: 500, - }, - - mapView: { - flex: 1, - borderRadius: 16, - overflow: 'hidden', - }, - - mapViewOverlay: { - flex: 1, - position: 'absolute', - left: 0, - top: 0, - borderRadius: variables.componentBorderRadiusLarge, - overflow: 'hidden', - backgroundColor: theme.highlightBG, - ...sizing.w100, - ...sizing.h100, - }, - - confirmationListMapItem: { - ...spacing.mv2, - ...spacing.mh5, - height: 200, - }, - - mapDirection: { - lineColor: theme.success, - lineWidth: 7, - }, - - mapDirectionLayer: { - layout: {'line-join': 'round', 'line-cap': 'round'}, - paint: {'line-color': theme.success, 'line-width': 7}, - }, - - mapPendingView: { - backgroundColor: theme.highlightBG, - ...flex.flex1, - borderRadius: variables.componentBorderRadiusLarge, - }, - userReportStatusEmoji: { - flexShrink: 0, - fontSize: variables.fontSizeNormal, - marginRight: 4, - }, - draggableTopBar: { - height: 30, - width: '100%', - }, - videoContainer: { - ...flex.flex1, - ...flex.alignItemsCenter, - ...flex.justifyContentCenter, - ...objectFit.oFCover, - }, - - globalNavigation: { - width: variables.globalNavigationWidth, - backgroundColor: theme.highlightBG, - }, - - globalNavigationMenuContainer: { - marginTop: 13, - }, - - globalAndSubNavigationContainer: { - backgroundColor: theme.highlightBG, - }, - - globalNavigationSelectionIndicator: (isFocused) => ({ - width: 4, - height: 52, - borderTopRightRadius: variables.componentBorderRadiusRounded, - borderBottomRightRadius: variables.componentBorderRadiusRounded, - backgroundColor: isFocused ? theme.iconMenu : theme.transparent, - }), - - globalNavigationMenuItem: (isFocused) => (isFocused ? {color: theme.text, fontWeight: fontWeightBold, fontFamily: fontFamily.EXP_NEUE_BOLD} : {color: theme.icon}), - - globalNavigationItemContainer: { - width: variables.globalNavigationWidth, - height: variables.globalNavigationWidth, - }, - - walletCard: { - borderRadius: variables.componentBorderRadiusLarge, - position: 'relative', - alignSelf: 'center', - overflow: 'hidden', - }, - - walletCardMenuItem: { - color: theme.text, - fontSize: variables.fontSizeNormal, - }, - - walletCardHolder: { - position: 'absolute', - left: 16, - bottom: 16, - width: variables.cardNameWidth, - color: theme.text, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightLarge, - }, - - walletBalance: { - lineHeight: undefined, - fontSize: 45, - paddingTop: 0, - paddingBottom: 0, - }, - - aspectRatioLottie: (source) => { - if (!source.uri && typeof source === 'object' && source.w && source.h) { - return {aspectRatio: source.w / source.h}; - } - return {}; - }, - - receiptDropHeaderGap: { - backgroundColor: theme.receiptDropUIBG, - }, - - checkboxWithLabelCheckboxStyle: { - marginLeft: -2, - }, -}); - -// For now we need to export the styles function that takes the theme as an argument -// as something named different than "styles", because a lot of files import the "defaultStyles" -// as "styles", which causes ESLint to throw an error. -// TODO: Remove "stylesGenerator" and instead only return "styles" once the app is migrated to theme switching hooks and HOCs and "styles/theme/default.js" is not used anywhere anymore (GH issue: https://github.com/Expensify/App/issues/27337) -const stylesGenerator = styles; -const defaultStyles = styles(defaultTheme); - -export default defaultStyles; -export {stylesGenerator}; diff --git a/src/styles/styles.ts b/src/styles/styles.ts new file mode 100644 index 000000000000..ba3c4d888858 --- /dev/null +++ b/src/styles/styles.ts @@ -0,0 +1,3979 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import {LineLayerStyleProps} from '@rnmapbox/maps/src/utils/MapboxStyles'; +import lodashClamp from 'lodash/clamp'; +import {LineLayer} from 'react-map-gl'; +import {AnimatableNumericValue, Animated, ImageStyle, TextStyle, ViewStyle} from 'react-native'; +import {CustomAnimation} from 'react-native-animatable'; +import {PickerStyle} from 'react-native-picker-select'; +import {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html'; +import CONST from '../CONST'; +import * as Browser from '../libs/Browser'; +import addOutlineWidth from './addOutlineWidth'; +import codeStyles from './codeStyles'; +import fontFamily from './fontFamily'; +import fontWeightBold from './fontWeight/bold'; +import getPopOverVerticalOffset from './getPopOverVerticalOffset'; +import optionAlternateTextPlatformStyles from './optionAlternateTextPlatformStyles'; +import overflowXHidden from './overflowXHidden'; +import pointerEventsAuto from './pointerEventsAuto'; +import pointerEventsNone from './pointerEventsNone'; +import defaultTheme from './themes/default'; +import {ThemeDefault} from './themes/types'; +import cursor from './utilities/cursor'; +import display from './utilities/display'; +import flex from './utilities/flex'; +import overflow from './utilities/overflow'; +import positioning from './utilities/positioning'; +import sizing from './utilities/sizing'; +import spacing from './utilities/spacing'; +import textUnderline from './utilities/textUnderline'; +import userSelect from './utilities/userSelect'; +import visibility from './utilities/visibility'; +import whiteSpace from './utilities/whiteSpace'; +import wordBreak from './utilities/wordBreak'; +import writingDirection from './utilities/writingDirection'; +import variables from './variables'; +import colors from './colors'; +import objectFit from './utilities/objectFit'; + +type AnchorPosition = { + horizontal: number; + vertical: number; +}; + +type WebViewStyle = { + tagStyles: MixedStyleRecord; + baseFontStyle: MixedStyleDeclaration; +}; + +type CustomPickerStyle = PickerStyle & {icon?: ViewStyle}; + +type OverlayStylesParams = {progress: Animated.AnimatedInterpolation}; + +type TwoFactorAuthCodesBoxParams = {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}; + +type Translation = 'perspective' | 'rotate' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scaleX' | 'scaleY' | 'translateX' | 'translateY' | 'skewX' | 'skewY' | 'matrix'; + +type OfflineFeedbackStyle = Record<'deleted' | 'pending' | 'error' | 'container' | 'textContainer' | 'text' | 'errorDot', ViewStyle | TextStyle>; + +type MapDirectionStyle = Pick; + +type MapDirectionLayerStyle = Pick; + +type Styles = Record< + string, + | ViewStyle + | TextStyle + | ImageStyle + | WebViewStyle + | OfflineFeedbackStyle + | MapDirectionStyle + | MapDirectionLayerStyle + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | ((...args: any[]) => ViewStyle | TextStyle | ImageStyle | AnchorPosition | CustomAnimation | CustomPickerStyle) +>; + +// touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target +const touchCalloutNone: Pick = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; +// to prevent vertical text offset in Safari for badges, new lineHeight values have been added +const lineHeightBadge: Pick = Browser.isSafari() ? {lineHeight: variables.lineHeightXSmall} : {lineHeight: variables.lineHeightNormal}; + +const picker = (theme: ThemeDefault) => + ({ + backgroundColor: theme.transparent, + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + lineHeight: variables.fontSizeNormalHeight, + paddingBottom: 8, + paddingTop: 23, + paddingLeft: 0, + paddingRight: 25, + height: variables.inputHeight, + borderWidth: 0, + textAlign: 'left', + } satisfies TextStyle); + +const link = (theme: ThemeDefault) => + ({ + color: theme.link, + textDecorationColor: theme.link, + fontFamily: fontFamily.EXP_NEUE, + } satisfies ViewStyle & MixedStyleDeclaration); + +const baseCodeTagStyles = (theme: ThemeDefault) => + ({ + borderWidth: 1, + borderRadius: 5, + borderColor: theme.border, + backgroundColor: theme.textBackground, + } satisfies ViewStyle & MixedStyleDeclaration); + +const headlineFont = { + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + fontWeight: '500', +} satisfies TextStyle; + +const webViewStyles = (theme: ThemeDefault) => + ({ + // As of react-native-render-html v6, don't declare distinct styles for + // custom renderers, the API for custom renderers has changed. Declare the + // styles in the below "tagStyles" instead. If you need to reuse those + // styles from the renderer, just pass the "style" prop to the underlying + // component. + tagStyles: { + em: { + fontFamily: fontFamily.EXP_NEUE, + fontStyle: 'italic', + }, + + del: { + textDecorationLine: 'line-through', + textDecorationStyle: 'solid', + }, + + strong: { + fontFamily: fontFamily.EXP_NEUE, + fontWeight: 'bold', + }, + + a: link(theme), + + ul: { + maxWidth: '100%', + }, + + ol: { + maxWidth: '100%', + }, + + li: { + flexShrink: 1, + }, + + blockquote: { + borderLeftColor: theme.border, + borderLeftWidth: 4, + paddingLeft: 12, + marginTop: 4, + marginBottom: 4, + + // Overwrite default HTML margin for blockquotes + marginLeft: 0, + }, + + pre: { + ...baseCodeTagStyles(theme), + paddingTop: 12, + paddingBottom: 12, + paddingRight: 8, + paddingLeft: 8, + fontFamily: fontFamily.MONOSPACE, + marginTop: 0, + marginBottom: 0, + }, + + code: { + ...baseCodeTagStyles(theme), + ...(codeStyles.codeTextStyle as MixedStyleDeclaration), + paddingLeft: 5, + paddingRight: 5, + fontFamily: fontFamily.MONOSPACE, + fontSize: 13, + }, + + img: { + borderColor: theme.border, + borderRadius: variables.componentBorderRadiusNormal, + borderWidth: 1, + ...touchCalloutNone, + }, + + p: { + marginTop: 0, + marginBottom: 0, + }, + h1: { + fontSize: variables.fontSizeLarge, + marginBottom: 8, + }, + }, + + baseFontStyle: { + color: theme.text, + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.EXP_NEUE, + flex: 1, + lineHeight: variables.fontSizeNormalHeight, + ...writingDirection.ltr, + }, + } satisfies WebViewStyle); + +const styles = (theme: ThemeDefault) => + ({ + // Add all of our utility and helper styles + ...spacing, + ...sizing, + ...flex, + ...display, + ...overflow, + ...positioning, + ...wordBreak, + ...whiteSpace, + ...writingDirection, + ...cursor, + ...userSelect, + ...textUnderline, + ...objectFit, + + autoCompleteSuggestionsContainer: { + backgroundColor: theme.appBG, + borderRadius: 8, + borderWidth: 1, + borderColor: theme.border, + justifyContent: 'center', + boxShadow: variables.popoverMenuShadow, + position: 'absolute', + left: 0, + right: 0, + paddingVertical: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTER_INNER_PADDING, + }, + + autoCompleteSuggestionContainer: { + flexDirection: 'row', + alignItems: 'center', + }, + + emojiSuggestionsEmoji: { + fontSize: variables.fontSizeMedium, + width: 51, + textAlign: 'center', + }, + emojiSuggestionsText: { + fontSize: variables.fontSizeMedium, + flex: 1, + ...wordBreak.breakWord, + ...spacing.pr4, + }, + + mentionSuggestionsAvatarContainer: { + width: 24, + height: 24, + alignItems: 'center', + justifyContent: 'center', + }, + + mentionSuggestionsText: { + fontSize: variables.fontSizeMedium, + ...spacing.ml2, + }, + + mentionSuggestionsDisplayName: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + }, + + mentionSuggestionsHandle: { + color: theme.textSupporting, + }, + + webViewStyles: webViewStyles(theme), + + link: link(theme), + + linkMuted: { + color: theme.textSupporting, + textDecorationColor: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, + }, + + linkMutedHovered: { + color: theme.textMutedReversed, + }, + + highlightBG: { + backgroundColor: theme.highlightBG, + }, + + appBG: { + backgroundColor: theme.appBG, + }, + + h4: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeLabel, + fontWeight: fontWeightBold, + }, + + textAlignCenter: { + textAlign: 'center', + }, + + textAlignRight: { + textAlign: 'right', + }, + + textAlignLeft: { + textAlign: 'left', + }, + + textUnderline: { + textDecorationLine: 'underline', + }, + + label: { + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + }, + + textLabel: { + color: theme.text, + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + }, + + mutedTextLabel: { + color: theme.textSupporting, + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + }, + + textMicro: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, + + textMicroBold: { + color: theme.text, + fontWeight: fontWeightBold, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, + + textMicroSupporting: { + color: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, + + textExtraSmallSupporting: { + color: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeExtraSmall, + }, + + textNormal: { + fontSize: variables.fontSizeNormal, + }, + + textLarge: { + fontSize: variables.fontSizeLarge, + }, + + textXLarge: { + fontSize: variables.fontSizeXLarge, + }, + + textXXLarge: { + fontSize: variables.fontSizeXXLarge, + }, + + textXXXLarge: { + fontSize: variables.fontSizeXXXLarge, + }, + + textHero: { + fontSize: variables.fontSizeHero, + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + lineHeight: variables.lineHeightHero, + }, + + textStrong: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + }, + + textItalic: { + fontFamily: fontFamily.EXP_NEUE_ITALIC, + fontStyle: 'italic', + }, + + textHeadline: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + }, + + textHeadlineH1: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + fontSize: variables.fontSizeh1, + lineHeight: variables.lineHeightSizeh1, + }, + + textDecorationNoLine: { + textDecorationLine: 'none', + }, + + textWhite: { + color: theme.textLight, + }, + + textBlue: { + color: theme.link, + }, + + textUppercase: { + textTransform: 'uppercase', + }, + + textNoWrap: { + ...whiteSpace.noWrap, + }, + + colorReversed: { + color: theme.textReversed, + }, + + colorMutedReversed: { + color: theme.textMutedReversed, + }, + + colorMuted: { + color: theme.textSupporting, + }, + + bgTransparent: { + backgroundColor: 'transparent', + }, + + bgDark: { + backgroundColor: theme.inverse, + }, + + opacity0: { + opacity: 0, + }, + + opacity1: { + opacity: 1, + }, + + textDanger: { + color: theme.danger, + }, + + borderRadiusNormal: { + borderRadius: variables.buttonBorderRadius, + }, + + button: { + backgroundColor: theme.buttonDefaultBG, + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeLarge, + justifyContent: 'center', + ...spacing.ph3, + }, + + buttonContainer: { + padding: 1, + borderRadius: variables.buttonBorderRadius, + }, + + buttonText: { + color: theme.text, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeNormal, + fontWeight: fontWeightBold, + textAlign: 'center', + flexShrink: 1, + + // It is needed to unset the Lineheight. We don't need it for buttons as button always contains single line of text. + // It allows to vertically center the text. + lineHeight: undefined, + + // Add 1px to the Button text to give optical vertical alignment. + paddingBottom: 1, + }, + + buttonSmall: { + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeSmall, + paddingTop: 4, + paddingHorizontal: 14, + paddingBottom: 4, + backgroundColor: theme.buttonDefaultBG, + }, + + buttonMedium: { + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeNormal, + paddingTop: 12, + paddingRight: 16, + paddingBottom: 12, + paddingLeft: 16, + backgroundColor: theme.buttonDefaultBG, + }, + + buttonLarge: { + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeLarge, + paddingTop: 8, + paddingRight: 10, + paddingBottom: 8, + paddingLeft: 18, + backgroundColor: theme.buttonDefaultBG, + }, + + buttonSmallText: { + fontSize: variables.fontSizeSmall, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + textAlign: 'center', + }, + + buttonMediumText: { + fontSize: variables.fontSizeLabel, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + textAlign: 'center', + }, + + buttonLargeText: { + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + textAlign: 'center', + }, + + buttonDefaultHovered: { + backgroundColor: theme.buttonHoveredBG, + borderWidth: 0, + }, + + buttonSuccess: { + backgroundColor: theme.success, + borderWidth: 0, + }, + + buttonOpacityDisabled: { + opacity: 0.5, + }, + + buttonSuccessHovered: { + backgroundColor: theme.successHover, + borderWidth: 0, + }, + + buttonDanger: { + backgroundColor: theme.danger, + borderWidth: 0, + }, + + buttonDangerHovered: { + backgroundColor: theme.dangerHover, + borderWidth: 0, + }, + + buttonDisabled: { + backgroundColor: theme.buttonDefaultBG, + borderWidth: 0, + }, + + buttonDivider: { + height: variables.dropDownButtonDividerHeight, + borderWidth: 0.7, + borderColor: theme.text, + }, + + noBorderRadius: { + borderRadius: 0, + }, + + noRightBorderRadius: { + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + }, + + noLeftBorderRadius: { + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, + }, + + buttonCTA: { + paddingVertical: 6, + ...spacing.mh4, + }, + + buttonCTAIcon: { + marginRight: 22, + + // Align vertically with the Button text + paddingBottom: 1, + paddingTop: 1, + }, + + buttonConfirm: { + margin: 20, + }, + + attachmentButtonBigScreen: { + minWidth: 300, + alignSelf: 'center', + }, + + buttonConfirmText: { + paddingLeft: 20, + paddingRight: 20, + }, + + buttonSuccessText: { + color: theme.textLight, + }, + + buttonDangerText: { + color: theme.textLight, + }, + + hoveredComponentBG: { + backgroundColor: theme.hoverComponentBG, + }, + + activeComponentBG: { + backgroundColor: theme.activeComponentBG, + }, + + touchableButtonImage: { + alignItems: 'center', + height: variables.componentSizeNormal, + justifyContent: 'center', + width: variables.componentSizeNormal, + }, + + visuallyHidden: { + ...visibility.hidden, + overflow: 'hidden', + width: 0, + height: 0, + }, + + visibilityHidden: { + ...visibility.hidden, + }, + + loadingVBAAnimation: { + width: 140, + height: 140, + }, + + pickerSmall: (backgroundColor = theme.highlightBG) => + ({ + inputIOS: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + paddingLeft: 0, + paddingRight: 17, + paddingTop: 6, + paddingBottom: 6, + borderWidth: 0, + color: theme.text, + height: 26, + opacity: 1, + backgroundColor: 'transparent', + }, + done: { + color: theme.text, + }, + doneDepressed: { + // Extracted from react-native-picker-select, src/styles.js + fontSize: 17, + }, + modalViewMiddle: { + backgroundColor: theme.border, + borderTopWidth: 0, + }, + modalViewBottom: { + backgroundColor: theme.highlightBG, + }, + inputWeb: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + paddingLeft: 0, + paddingRight: 17, + paddingTop: 6, + paddingBottom: 6, + borderWidth: 0, + color: theme.text, + appearance: 'none', + height: 26, + opacity: 1, + backgroundColor, + ...cursor.cursorPointer, + }, + inputAndroid: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + paddingLeft: 0, + paddingRight: 17, + paddingTop: 6, + paddingBottom: 6, + borderWidth: 0, + color: theme.text, + height: 26, + opacity: 1, + backgroundColor: 'transparent', + }, + iconContainer: { + top: 7, + ...pointerEventsNone, + }, + icon: { + width: variables.iconSizeExtraSmall, + height: variables.iconSizeExtraSmall, + }, + } satisfies CustomPickerStyle), + + badge: { + backgroundColor: theme.border, + borderRadius: 14, + height: variables.iconSizeNormal, + flexDirection: 'row', + paddingHorizontal: 7, + alignItems: 'center', + }, + + badgeSuccess: { + backgroundColor: theme.success, + }, + + badgeSuccessPressed: { + backgroundColor: theme.successHover, + }, + + badgeAdHocSuccess: { + backgroundColor: theme.badgeAdHoc, + }, + + badgeAdHocSuccessPressed: { + backgroundColor: theme.badgeAdHocHover, + }, + + badgeDanger: { + backgroundColor: theme.danger, + }, + + badgeDangerPressed: { + backgroundColor: theme.dangerPressed, + }, + + badgeText: { + color: theme.text, + fontSize: variables.fontSizeSmall, + ...lineHeightBadge, + ...whiteSpace.noWrap, + }, + + border: { + borderWidth: 1, + borderRadius: variables.componentBorderRadius, + borderColor: theme.border, + }, + + borderColorFocus: { + borderColor: theme.borderFocus, + }, + + borderColorDanger: { + borderColor: theme.danger, + }, + + textInputDisabled: { + // Adding disabled color theme to indicate user that the field is not editable. + backgroundColor: theme.highlightBG, + borderBottomWidth: 2, + borderColor: theme.borderLighter, + // Adding browser specefic style to bring consistency between Safari and other platforms. + // Applying the Webkit styles only to browsers as it is not available in native. + ...(Browser.getBrowser() + ? { + WebkitTextFillColor: theme.textSupporting, + WebkitOpacity: 1, + } + : {}), + color: theme.textSupporting, + }, + + uploadReceiptView: (isSmallScreenWidth: boolean) => + ({ + borderRadius: variables.componentBorderRadiusLarge, + borderWidth: isSmallScreenWidth ? 0 : 2, + borderColor: theme.borderFocus, + borderStyle: 'dotted', + marginBottom: 20, + marginLeft: 20, + marginRight: 20, + justifyContent: 'center', + alignItems: 'center', + paddingVertical: 40, + gap: 4, + flex: 1, + } satisfies ViewStyle), + + receiptViewTextContainer: { + paddingHorizontal: 40, + ...sizing.w100, + }, + + cameraView: { + flex: 1, + overflow: 'hidden', + borderRadius: 28, + borderStyle: 'solid', + borderWidth: 8, + backgroundColor: theme.highlightBG, + borderColor: theme.appBG, + display: 'flex', + justifyContent: 'center', + justifyItems: 'center', + }, + + permissionView: { + paddingVertical: 108, + paddingHorizontal: 61, + alignItems: 'center', + justifyContent: 'center', + }, + + headerAnonymousFooter: { + color: theme.heading, + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + }, + + headerText: { + color: theme.heading, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeNormal, + fontWeight: fontWeightBold, + }, + + headerGap: { + height: CONST.DESKTOP_HEADER_PADDING, + }, + + reportOptions: { + marginLeft: 8, + }, + + chatItemComposeSecondaryRow: { + height: CONST.CHAT_FOOTER_SECONDARY_ROW_HEIGHT, + marginBottom: CONST.CHAT_FOOTER_SECONDARY_ROW_PADDING, + marginTop: CONST.CHAT_FOOTER_SECONDARY_ROW_PADDING, + }, + + chatItemComposeSecondaryRowSubText: { + color: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, + + chatItemComposeSecondaryRowOffset: { + marginLeft: variables.chatInputSpacing, + }, + + offlineIndicator: { + marginLeft: variables.chatInputSpacing, + }, + + offlineIndicatorMobile: { + paddingLeft: 20, + paddingTop: 5, + paddingBottom: 30, + marginBottom: -25, + }, + + offlineIndicatorRow: { + height: 25, + }, + + // Actions + actionAvatar: { + borderRadius: 20, + }, + + componentHeightLarge: { + height: variables.inputHeight, + }, + + calendarHeader: { + height: 50, + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingHorizontal: 15, + paddingRight: 5, + ...userSelect.userSelectNone, + }, + + calendarDayRoot: { + flex: 1, + height: 45, + justifyContent: 'center', + alignItems: 'center', + ...userSelect.userSelectNone, + }, + + calendarDayContainer: { + width: 30, + height: 30, + justifyContent: 'center', + alignItems: 'center', + borderRadius: 15, + overflow: 'hidden', + }, + + calendarDayContainerSelected: { + backgroundColor: theme.buttonDefaultBG, + }, + + autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number) => + ({ + height: lodashClamp(textInputHeight, minHeight, maxHeight), + minHeight, + } satisfies ViewStyle), + + autoGrowHeightHiddenInput: (maxWidth: number, maxHeight?: number) => + ({ + maxWidth, + maxHeight: maxHeight && maxHeight + 1, + overflow: 'hidden', + } satisfies TextStyle), + + textInputContainer: { + flex: 1, + justifyContent: 'center', + height: '100%', + backgroundColor: 'transparent', + borderBottomWidth: 2, + borderColor: theme.border, + overflow: 'hidden', + }, + + textInputLabel: { + position: 'absolute', + left: 0, + top: 0, + fontSize: variables.fontSizeNormal, + color: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, + width: '100%', + }, + + textInputLabelBackground: { + position: 'absolute', + top: 0, + width: '100%', + height: 23, + backgroundColor: theme.componentBG, + }, + + textInputLabelDesktop: { + transformOrigin: 'left center', + }, + + textInputLabelTransformation: (translateY: AnimatableNumericValue, translateX: AnimatableNumericValue, scale: AnimatableNumericValue) => + ({ + transform: [{translateY}, {translateX}, {scale}], + } satisfies TextStyle), + + baseTextInput: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + lineHeight: variables.lineHeightXLarge, + color: theme.text, + paddingTop: 23, + paddingBottom: 8, + paddingLeft: 0, + borderWidth: 0, + }, + + textInputMultiline: { + scrollPadding: '23px 0 0 0', + }, + + textInputMultilineContainer: { + paddingTop: 23, + }, + + textInputAndIconContainer: { + flex: 1, + height: '100%', + zIndex: -1, + flexDirection: 'row', + }, + + textInputDesktop: addOutlineWidth({}, 0), + + textInputIconContainer: { + paddingHorizontal: 11, + justifyContent: 'center', + margin: 1, + }, + + secureInput: { + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + }, + + textInput: { + backgroundColor: 'transparent', + borderRadius: variables.componentBorderRadiusNormal, + height: variables.inputComponentSizeNormal, + borderColor: theme.border, + borderWidth: 1, + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + paddingLeft: 12, + paddingRight: 12, + paddingTop: 10, + paddingBottom: 10, + textAlignVertical: 'center', + }, + + textInputPrefixWrapper: { + position: 'absolute', + left: 0, + top: 0, + height: variables.inputHeight, + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + paddingTop: 23, + paddingBottom: 8, + }, + + textInputPrefix: { + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + textAlignVertical: 'center', + }, + + pickerContainer: { + borderBottomWidth: 2, + paddingLeft: 0, + borderStyle: 'solid', + borderColor: theme.border, + justifyContent: 'center', + backgroundColor: 'transparent', + height: variables.inputHeight, + overflow: 'hidden', + }, + + pickerContainerSmall: { + height: variables.inputHeightSmall, + }, + + pickerLabel: { + position: 'absolute', + left: 0, + top: 6, + zIndex: 1, + }, + + picker: (disabled = false, backgroundColor = theme.appBG) => + ({ + iconContainer: { + top: Math.round(variables.inputHeight * 0.5) - 11, + right: 0, + ...pointerEventsNone, + }, + + inputWeb: { + appearance: 'none', + ...(disabled ? cursor.cursorDisabled : cursor.cursorPointer), + ...picker(theme), + backgroundColor, + }, + + inputIOS: { + ...picker(theme), + }, + done: { + color: theme.text, + }, + doneDepressed: { + // Extracted from react-native-picker-select, src/styles.js + fontSize: 17, + }, + modalViewMiddle: { + backgroundColor: theme.border, + borderTopWidth: 0, + }, + modalViewBottom: { + backgroundColor: theme.highlightBG, + }, + + inputAndroid: { + ...picker(theme), + }, + } satisfies CustomPickerStyle), + + disabledText: { + color: theme.icon, + }, + + inputDisabled: { + backgroundColor: theme.highlightBG, + color: theme.icon, + }, + + noOutline: addOutlineWidth({}, 0), + + textLabelSupporting: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.textSupporting, + }, + + textLabelError: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.textError, + }, + + textReceiptUpload: { + ...headlineFont, + fontSize: variables.fontSizeXLarge, + color: theme.textLight, + textAlign: 'center', + }, + + subTextReceiptUpload: { + fontFamily: fontFamily.EXP_NEUE, + lineHeight: variables.lineHeightLarge, + textAlign: 'center', + color: theme.textLight, + }, + + furtherDetailsText: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + color: theme.textSupporting, + }, + + lh16: { + lineHeight: 16, + }, + + lh20: { + lineHeight: 20, + }, + + lh140Percent: { + lineHeight: '140%', + }, + + formHelp: { + color: theme.textSupporting, + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + marginBottom: 4, + }, + + formError: { + color: theme.textError, + fontSize: variables.fontSizeLabel, + lineHeight: variables.formErrorLineHeight, + marginBottom: 4, + }, + + formSuccess: { + color: theme.success, + fontSize: variables.fontSizeLabel, + lineHeight: 18, + marginBottom: 4, + }, + + signInPage: { + backgroundColor: theme.highlightBG, + minHeight: '100%', + flex: 1, + }, + + signInPageHeroCenter: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'center', + alignItems: 'center', + }, + + signInPageGradient: { + height: '100%', + width: 540, + position: 'absolute', + top: 0, + left: 0, + }, + + signInPageGradientMobile: { + height: 300, + width: 800, + position: 'absolute', + top: 0, + left: 0, + }, + + signInBackground: { + position: 'absolute', + bottom: 0, + left: 0, + minHeight: 700, + }, + + signInPageInner: { + marginLeft: 'auto', + marginRight: 'auto', + height: '100%', + width: '100%', + }, + + signInPageContentTopSpacer: { + maxHeight: 132, + minHeight: 24, + }, + + signInPageContentTopSpacerSmallScreens: { + maxHeight: 132, + minHeight: 45, + }, + + signInPageLeftContainer: { + paddingLeft: 40, + paddingRight: 40, + }, + + signInPageLeftContainerWide: { + maxWidth: variables.sideBarWidth, + }, + + signInPageWelcomeFormContainer: { + maxWidth: CONST.SIGN_IN_FORM_WIDTH, + }, + + signInPageWelcomeTextContainer: { + width: CONST.SIGN_IN_FORM_WIDTH, + }, + + changeExpensifyLoginLinkContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + ...wordBreak.breakWord, + }, + + // Sidebar Styles + sidebar: { + backgroundColor: theme.sidebar, + height: '100%', + }, + + sidebarHeaderContainer: { + flexDirection: 'row', + paddingHorizontal: 20, + paddingVertical: 19, + justifyContent: 'space-between', + alignItems: 'center', + }, + + subNavigationContainer: { + backgroundColor: theme.sidebar, + flex: 1, + borderTopLeftRadius: variables.componentBorderRadiusRounded, + }, + + sidebarAnimatedWrapperContainer: { + height: '100%', + position: 'absolute', + }, + + sidebarFooter: { + alignItems: 'center', + display: 'flex', + justifyContent: 'center', + paddingVertical: variables.lineHeightXLarge, + width: '100%', + }, + + sidebarAvatar: { + backgroundColor: theme.icon, + borderRadius: 20, + height: variables.componentSizeNormal, + width: variables.componentSizeNormal, + }, + + statusIndicator: (backgroundColor = theme.danger) => + ({ + borderColor: theme.sidebar, + backgroundColor, + borderRadius: 8, + borderWidth: 2, + position: 'absolute', + right: -2, + top: -1, + height: 16, + width: 16, + zIndex: 10, + } satisfies ViewStyle), + + floatingActionButtonContainer: { + position: 'absolute', + left: 16, + + // The bottom of the floating action button should align with the bottom of the compose box. + // The value should be equal to the height + marginBottom + marginTop of chatItemComposeSecondaryRow + bottom: 25, + }, + + floatingActionButton: { + backgroundColor: theme.success, + height: variables.componentSizeNormal, + width: variables.componentSizeNormal, + borderRadius: 999, + alignItems: 'center', + justifyContent: 'center', + }, + + sidebarFooterUsername: { + color: theme.heading, + fontSize: variables.fontSizeLabel, + fontWeight: '700', + width: 200, + textOverflow: 'ellipsis', + overflow: 'hidden', + ...whiteSpace.noWrap, + }, + + sidebarFooterLink: { + color: theme.textSupporting, + fontSize: variables.fontSizeSmall, + textDecorationLine: 'none', + fontFamily: fontFamily.EXP_NEUE, + lineHeight: 20, + }, + + sidebarListContainer: { + scrollbarWidth: 'none', + paddingBottom: 4, + }, + + sidebarListItem: { + justifyContent: 'center', + textDecorationLine: 'none', + }, + + RHPNavigatorContainer: (isSmallScreenWidth: boolean) => + ({ + width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, + position: 'absolute', + right: 0, + height: '100%', + } satisfies ViewStyle), + + onlyEmojisText: { + fontSize: variables.fontSizeOnlyEmojis, + lineHeight: variables.fontSizeOnlyEmojisHeight, + }, + + onlyEmojisTextLineHeight: { + lineHeight: variables.fontSizeOnlyEmojisHeight, + }, + + createMenuPositionSidebar: (windowHeight: number) => + ({ + horizontal: 18, + vertical: windowHeight - 75, + } satisfies AnchorPosition), + + createMenuPositionProfile: (windowWidth: number) => + ({ + horizontal: windowWidth - 355, + ...getPopOverVerticalOffset(162), + } satisfies AnchorPosition), + + createMenuPositionReportActionCompose: (windowHeight: number) => + ({ + horizontal: 18 + variables.sideBarWidth, + vertical: windowHeight - 83, + } satisfies AnchorPosition), + + createMenuPositionRightSidepane: { + right: 18, + bottom: 75, + }, + + createMenuContainer: { + width: variables.sideBarWidth - 40, + paddingVertical: 12, + }, + + createMenuHeaderText: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.heading, + }, + + popoverMenuItem: { + flexDirection: 'row', + borderRadius: 0, + paddingHorizontal: 20, + paddingVertical: 12, + justifyContent: 'space-between', + width: '100%', + }, + + popoverMenuIcon: { + width: variables.componentSizeNormal, + justifyContent: 'center', + alignItems: 'center', + }, + + popoverMenuText: { + fontSize: variables.fontSizeNormal, + color: theme.heading, + }, + + popoverInnerContainer: { + paddingTop: 0, // adjusting this because the mobile modal adds additional padding that we don't need for our layout + maxHeight: '95%', + }, + + menuItemTextContainer: { + minHeight: variables.componentSizeNormal, + }, + + chatLinkRowPressable: { + minWidth: 0, + textDecorationLine: 'none', + flex: 1, + }, + + sidebarLink: { + textDecorationLine: 'none', + }, + + sidebarLinkLHN: { + textDecorationLine: 'none', + marginLeft: 12, + marginRight: 12, + borderRadius: 8, + }, + + sidebarLinkInner: { + alignItems: 'center', + flexDirection: 'row', + paddingLeft: 20, + paddingRight: 20, + }, + + sidebarLinkInnerLHN: { + alignItems: 'center', + flexDirection: 'row', + paddingLeft: 8, + paddingRight: 8, + }, + + sidebarLinkText: { + color: theme.textSupporting, + fontSize: variables.fontSizeNormal, + textDecorationLine: 'none', + overflow: 'hidden', + }, + + sidebarLinkHover: { + backgroundColor: theme.sidebarHover, + }, + + sidebarLinkHoverLHN: { + backgroundColor: theme.highlightBG, + }, + + sidebarLinkActive: { + backgroundColor: theme.border, + textDecorationLine: 'none', + }, + + sidebarLinkActiveLHN: { + backgroundColor: theme.highlightBG, + textDecorationLine: 'none', + }, + + sidebarLinkTextBold: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + color: theme.heading, + }, + + sidebarLinkActiveText: { + color: theme.textSupporting, + fontSize: variables.fontSizeNormal, + textDecorationLine: 'none', + overflow: 'hidden', + }, + + optionItemAvatarNameWrapper: { + minWidth: 0, + flex: 1, + }, + + optionDisplayName: { + fontFamily: fontFamily.EXP_NEUE, + minHeight: variables.alternateTextHeight, + lineHeight: variables.lineHeightXLarge, + ...whiteSpace.noWrap, + }, + + optionDisplayNameCompact: { + minWidth: 'auto', + flexBasis: 'auto', + flexGrow: 0, + flexShrink: 1, + }, + + displayNameTooltipEllipsis: { + position: 'absolute', + opacity: 0, + right: 0, + bottom: 0, + }, + + optionAlternateText: { + minHeight: variables.alternateTextHeight, + lineHeight: variables.lineHeightXLarge, + }, + + optionAlternateTextCompact: { + flexShrink: 1, + flexGrow: 1, + flexBasis: 'auto', + ...optionAlternateTextPlatformStyles, + }, + + optionRow: { + minHeight: variables.optionRowHeight, + paddingTop: 12, + paddingBottom: 12, + }, + + optionRowSelected: { + backgroundColor: theme.activeComponentBG, + }, + + optionRowDisabled: { + color: theme.textSupporting, + }, + + optionRowCompact: { + height: variables.optionRowHeightCompact, + paddingTop: 12, + paddingBottom: 12, + }, + + optionsListSectionHeader: { + marginTop: 8, + marginBottom: 4, + }, + + overlayStyles: (current: OverlayStylesParams) => + ({ + ...positioning.pFixed, + // We need to stretch the overlay to cover the sidebar and the translate animation distance. + left: -2 * variables.sideBarWidth, + top: 0, + bottom: 0, + right: 0, + backgroundColor: theme.overlay, + opacity: current.progress.interpolate({ + inputRange: [0, 1], + outputRange: [0, variables.overlayOpacity], + extrapolate: 'clamp', + }), + } satisfies ViewStyle), + + appContent: { + backgroundColor: theme.appBG, + overflow: 'hidden', + }, + + appContentHeader: { + height: variables.contentHeaderHeight, + justifyContent: 'center', + display: 'flex', + paddingRight: 20, + }, + + appContentHeaderTitle: { + alignItems: 'center', + flexDirection: 'row', + }, + + LHNToggle: { + alignItems: 'center', + height: variables.contentHeaderHeight, + justifyContent: 'center', + paddingRight: 10, + paddingLeft: 20, + }, + + LHNToggleIcon: { + height: 15, + width: 18, + }, + + chatContentScrollView: { + flexGrow: 1, + justifyContent: 'flex-start', + paddingBottom: 16, + }, + + // Chat Item + chatItem: { + display: 'flex', + flexDirection: 'row', + paddingTop: 8, + paddingBottom: 8, + paddingLeft: 20, + paddingRight: 20, + }, + + chatItemRightGrouped: { + flexGrow: 1, + flexShrink: 1, + flexBasis: 0, + position: 'relative', + marginLeft: variables.chatInputSpacing, + }, + + chatItemRight: { + flexGrow: 1, + flexShrink: 1, + flexBasis: 0, + position: 'relative', + }, + + chatItemMessageHeader: { + alignItems: 'center', + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap', + }, + + chatItemMessageHeaderSender: { + color: theme.heading, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeNormal, + fontWeight: fontWeightBold, + lineHeight: variables.lineHeightXLarge, + ...wordBreak.breakWord, + }, + + chatItemMessageHeaderTimestamp: { + flexShrink: 0, + color: theme.textSupporting, + fontSize: variables.fontSizeSmall, + paddingTop: 2, + }, + + chatItemMessage: { + color: theme.text, + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.EXP_NEUE, + lineHeight: variables.lineHeightXLarge, + maxWidth: '100%', + ...cursor.cursorAuto, + ...whiteSpace.preWrap, + ...wordBreak.breakWord, + }, + + renderHTMLTitle: { + color: theme.text, + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.EXP_NEUE, + lineHeight: variables.lineHeightXLarge, + maxWidth: '100%', + ...whiteSpace.preWrap, + ...wordBreak.breakWord, + }, + + chatItemComposeWithFirstRow: { + minHeight: 90, + }, + + chatItemFullComposeRow: { + ...sizing.h100, + }, + + chatItemComposeBoxColor: { + borderColor: theme.border, + }, + + chatItemComposeBoxFocusedColor: { + borderColor: theme.borderFocus, + }, + + chatItemComposeBox: { + backgroundColor: theme.componentBG, + borderWidth: 1, + borderRadius: variables.componentBorderRadiusRounded, + minHeight: variables.componentSizeMedium, + }, + + chatItemFullComposeBox: { + ...flex.flex1, + ...sizing.h100, + }, + + chatFooter: { + paddingLeft: 20, + paddingRight: 20, + display: 'flex', + backgroundColor: theme.appBG, + }, + + chatFooterFullCompose: { + flex: 1, + }, + + chatItemDraft: { + display: 'flex', + flexDirection: 'row', + paddingTop: 8, + paddingBottom: 8, + paddingLeft: 20, + paddingRight: 20, + }, + + chatItemReactionsDraftRight: { + marginLeft: 52, + }, + chatFooterAtTheTop: { + flexGrow: 1, + justifyContent: 'flex-start', + }, + + // Be extremely careful when editing the compose styles, as it is easy to introduce regressions. + // Make sure you run the following tests against any changes: #12669 + textInputCompose: addOutlineWidth( + { + backgroundColor: theme.componentBG, + borderColor: theme.border, + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + borderWidth: 0, + height: 'auto', + lineHeight: variables.lineHeightXLarge, + ...overflowXHidden, + + // On Android, multiline TextInput with height: 'auto' will show extra padding unless they are configured with + // paddingVertical: 0, alignSelf: 'center', and textAlignVertical: 'center' + + paddingHorizontal: variables.avatarChatSpacing, + paddingTop: 0, + paddingBottom: 0, + alignSelf: 'center', + textAlignVertical: 'center', + }, + 0, + ), + + textInputFullCompose: { + alignSelf: 'stretch', + flex: 1, + maxHeight: '100%', + textAlignVertical: 'top', + }, + + // composer padding should not be modified unless thoroughly tested against the cases in this PR: #12669 + textInputComposeSpacing: { + paddingVertical: 5, + ...flex.flexRow, + flex: 1, + }, + + textInputComposeBorder: { + borderLeftWidth: 1, + borderColor: theme.border, + }, + + chatItemSubmitButton: { + alignSelf: 'flex-end', + borderRadius: variables.componentBorderRadiusRounded, + backgroundColor: theme.transparent, + height: 40, + padding: 10, + margin: 3, + justifyContent: 'center', + }, + + emojiPickerContainer: { + backgroundColor: theme.componentBG, + }, + + emojiHeaderContainer: { + backgroundColor: theme.componentBG, + display: 'flex', + height: CONST.EMOJI_PICKER_HEADER_HEIGHT, + justifyContent: 'center', + width: '100%', + }, + + emojiSkinToneTitle: { + ...spacing.pv1, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + color: theme.heading, + fontSize: variables.fontSizeSmall, + }, + + // Emoji Picker Styles + emojiText: { + textAlign: 'center', + fontSize: variables.emojiSize, + ...spacing.pv0, + ...spacing.ph0, + lineHeight: variables.emojiLineHeight, + }, + + emojiItem: { + width: '12.5%', + textAlign: 'center', + borderRadius: 8, + paddingTop: 2, + paddingBottom: 2, + height: CONST.EMOJI_PICKER_ITEM_HEIGHT, + ...userSelect.userSelectNone, + }, + + emojiItemHighlighted: { + transition: '0.2s ease', + backgroundColor: theme.buttonDefaultBG, + }, + + emojiItemKeyboardHighlighted: { + transition: '0.2s ease', + borderWidth: 1, + borderColor: theme.link, + borderRadius: variables.buttonBorderRadius, + }, + + categoryShortcutButton: { + flex: 1, + borderRadius: 8, + height: CONST.EMOJI_PICKER_ITEM_HEIGHT, + alignItems: 'center', + justifyContent: 'center', + }, + + chatItemEmojiButton: { + alignSelf: 'flex-end', + borderRadius: variables.buttonBorderRadius, + height: 40, + marginVertical: 3, + paddingHorizontal: 10, + justifyContent: 'center', + }, + + editChatItemEmojiWrapper: { + marginRight: 3, + alignSelf: 'flex-end', + }, + + hoveredButton: { + backgroundColor: theme.buttonHoveredBG, + }, + + composerSizeButton: { + alignSelf: 'center', + height: 32, + width: 32, + padding: 6, + margin: 3, + borderRadius: variables.componentBorderRadiusRounded, + backgroundColor: theme.transparent, + justifyContent: 'center', + }, + + chatItemAttachmentPlaceholder: { + backgroundColor: theme.sidebar, + borderColor: theme.border, + borderWidth: 1, + borderRadius: variables.componentBorderRadiusNormal, + height: 150, + textAlign: 'center', + verticalAlign: 'middle', + width: 200, + }, + + sidebarVisible: { + borderRightWidth: 1, + }, + + sidebarHidden: { + width: 0, + borderRightWidth: 0, + }, + + exampleCheckImage: { + width: '100%', + height: 80, + borderColor: theme.border, + borderWidth: 1, + borderRadius: variables.componentBorderRadiusNormal, + }, + + singleAvatar: { + height: 24, + width: 24, + backgroundColor: theme.icon, + borderRadius: 24, + }, + + singleAvatarSmall: { + height: 18, + width: 18, + backgroundColor: theme.icon, + borderRadius: 18, + }, + + singleAvatarMedium: { + height: 52, + width: 52, + backgroundColor: theme.icon, + borderRadius: 52, + }, + + secondAvatar: { + position: 'absolute', + right: -18, + bottom: -18, + borderWidth: 3, + borderRadius: 30, + borderColor: 'transparent', + }, + + secondAvatarSmall: { + position: 'absolute', + right: -13, + bottom: -13, + borderWidth: 3, + borderRadius: 18, + borderColor: 'transparent', + }, + + secondAvatarMedium: { + position: 'absolute', + right: -36, + bottom: -36, + borderWidth: 3, + borderRadius: 52, + borderColor: 'transparent', + }, + + secondAvatarSubscript: { + position: 'absolute', + right: -6, + bottom: -6, + }, + + secondAvatarSubscriptCompact: { + position: 'absolute', + bottom: -1, + right: -1, + }, + + secondAvatarSubscriptSmallNormal: { + position: 'absolute', + bottom: 0, + right: 0, + }, + + secondAvatarInline: { + bottom: -3, + right: -25, + borderWidth: 3, + borderRadius: 18, + borderColor: theme.cardBorder, + backgroundColor: theme.appBG, + }, + + avatarLarge: { + width: variables.avatarSizeLarge, + height: variables.avatarSizeLarge, + }, + + avatarXLarge: { + width: variables.avatarSizeXLarge, + height: variables.avatarSizeXLarge, + }, + + avatarInnerText: { + color: theme.textLight, + fontSize: variables.fontSizeSmall, + lineHeight: undefined, + marginLeft: -3, + textAlign: 'center', + }, + + avatarInnerTextSmall: { + color: theme.textLight, + fontSize: variables.fontSizeExtraSmall, + lineHeight: undefined, + marginLeft: -2, + textAlign: 'center', + }, + + emptyAvatar: { + height: variables.avatarSizeNormal, + width: variables.avatarSizeNormal, + }, + + emptyAvatarSmallNormal: { + height: variables.avatarSizeSmallNormal, + width: variables.avatarSizeSmallNormal, + }, + + emptyAvatarSmall: { + height: variables.avatarSizeSmall, + width: variables.avatarSizeSmall, + }, + + emptyAvatarSmaller: { + height: variables.avatarSizeSmaller, + width: variables.avatarSizeSmaller, + }, + + emptyAvatarMedium: { + height: variables.avatarSizeMedium, + width: variables.avatarSizeMedium, + }, + + emptyAvatarLarge: { + height: variables.avatarSizeLarge, + width: variables.avatarSizeLarge, + }, + + emptyAvatarMargin: { + marginRight: variables.avatarChatSpacing, + }, + + emptyAvatarMarginChat: { + marginRight: variables.avatarChatSpacing - 12, + }, + + emptyAvatarMarginSmall: { + marginRight: variables.avatarChatSpacing - 4, + }, + + emptyAvatarMarginSmaller: { + marginRight: variables.avatarChatSpacing - 4, + }, + + borderTop: { + borderTopWidth: variables.borderTopWidth, + borderColor: theme.border, + }, + + borderTopRounded: { + borderTopWidth: 1, + borderColor: theme.border, + borderTopLeftRadius: variables.componentBorderRadiusNormal, + borderTopRightRadius: variables.componentBorderRadiusNormal, + }, + + borderBottomRounded: { + borderBottomWidth: 1, + borderColor: theme.border, + borderBottomLeftRadius: variables.componentBorderRadiusNormal, + borderBottomRightRadius: variables.componentBorderRadiusNormal, + }, + + borderBottom: { + borderBottomWidth: 1, + borderColor: theme.border, + }, + + borderNone: { + borderWidth: 0, + borderBottomWidth: 0, + }, + + borderRight: { + borderRightWidth: 1, + borderColor: theme.border, + }, + + borderLeft: { + borderLeftWidth: 1, + borderColor: theme.border, + }, + + pointerEventsNone, + + pointerEventsAuto, + + headerBar: { + overflow: 'hidden', + justifyContent: 'center', + display: 'flex', + paddingLeft: 20, + height: variables.contentHeaderHeight, + width: '100%', + }, + + imageViewContainer: { + width: '100%', + height: '100%', + alignItems: 'center', + justifyContent: 'center', + }, + + imageModalPDF: { + flex: 1, + backgroundColor: theme.modalBackground, + }, + + PDFView: { + // `display: grid` is not supported in native platforms! + // It's being used on Web/Desktop only to vertically center short PDFs, + // while preventing the overflow of the top of long PDF files. + ...display.dGrid, + backgroundColor: theme.modalBackground, + width: '100%', + height: '100%', + justifyContent: 'center', + overflow: 'hidden', + alignItems: 'center', + }, + + PDFViewList: { + overflowX: 'hidden', + // There properties disable "focus" effect on list + boxShadow: 'none', + outline: 'none', + }, + + getPDFPasswordFormStyle: (isSmallScreenWidth: boolean) => + ({ + width: isSmallScreenWidth ? '100%' : 350, + ...(isSmallScreenWidth && flex.flex1), + } satisfies ViewStyle), + + centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean) => + ({ + borderWidth: isSmallScreenWidth && !isFullScreenWhenSmall ? 1 : 0, + marginHorizontal: isSmallScreenWidth ? 0 : 20, + } satisfies ViewStyle), + + imageModalImageCenterContainer: { + alignItems: 'center', + flex: 1, + justifyContent: 'center', + width: '100%', + }, + + defaultAttachmentView: { + backgroundColor: theme.sidebar, + borderRadius: variables.componentBorderRadiusNormal, + borderWidth: 1, + borderColor: theme.border, + flexDirection: 'row', + padding: 20, + alignItems: 'center', + }, + + notFoundTextHeader: { + ...headlineFont, + color: theme.heading, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + marginTop: 20, + marginBottom: 8, + textAlign: 'center', + }, + + blockingViewContainer: { + paddingBottom: variables.contentHeaderHeight, + }, + + defaultModalContainer: { + backgroundColor: theme.componentBG, + borderColor: theme.transparent, + }, + + reportActionContextMenuMiniButton: { + ...spacing.p1, + ...spacing.mv1, + ...spacing.mh1, + ...{borderRadius: variables.buttonBorderRadius}, + }, + + reportActionSystemMessageContainer: { + marginLeft: 42, + }, + + reportDetailsTitleContainer: { + ...display.dFlex, + ...flex.flexColumn, + ...flex.alignItemsCenter, + paddingHorizontal: 20, + paddingBottom: 20, + }, + + reportDetailsRoomInfo: { + ...flex.flex1, + ...display.dFlex, + ...flex.flexColumn, + ...flex.alignItemsCenter, + }, + + reportSettingsVisibilityText: { + textTransform: 'capitalize', + }, + + settingsPageBackground: { + flexDirection: 'column', + width: '100%', + flexGrow: 1, + }, + + settingsPageBody: { + width: '100%', + justifyContent: 'space-around', + }, + + twoFactorAuthSection: { + backgroundColor: theme.appBG, + padding: 0, + }, + + twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: TwoFactorAuthCodesBoxParams) => { + let paddingHorizontal = spacing.ph9; + + if (isSmallScreenWidth) { + paddingHorizontal = spacing.ph4; + } + + if (isExtraSmallScreenWidth) { + paddingHorizontal = spacing.ph2; + } + + return { + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.highlightBG, + paddingVertical: 28, + borderRadius: 16, + marginTop: 32, + ...paddingHorizontal, + } satisfies ViewStyle; + }, + + twoFactorLoadingContainer: { + alignItems: 'center', + justifyContent: 'center', + height: 210, + }, + + twoFactorAuthCodesContainer: { + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'row', + flexWrap: 'wrap', + gap: 12, + }, + + twoFactorAuthCode: { + fontFamily: fontFamily.MONOSPACE, + width: 112, + textAlign: 'center', + }, + + twoFactorAuthCodesButtonsContainer: { + flexDirection: 'row', + justifyContent: 'center', + gap: 12, + marginTop: 20, + flexWrap: 'wrap', + }, + + twoFactorAuthCodesButton: { + minWidth: 112, + }, + + twoFactorAuthCopyCodeButton: { + minWidth: 110, + }, + + anonymousRoomFooter: (isSmallSizeLayout: boolean) => + ({ + flexDirection: isSmallSizeLayout ? 'column' : 'row', + ...(!isSmallSizeLayout && { + alignItems: 'center', + justifyContent: 'space-between', + }), + padding: 20, + backgroundColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusLarge, + overflow: 'hidden', + } satisfies ViewStyle & TextStyle), + anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout: boolean) => + ({ + flexDirection: 'row', + alignItems: 'center', + ...(isSmallSizeLayout && { + justifyContent: 'space-between', + marginTop: 16, + }), + } satisfies ViewStyle), + anonymousRoomFooterLogo: { + width: 88, + marginLeft: 0, + height: 20, + }, + anonymousRoomFooterLogoTaglineText: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeMedium, + color: theme.textLight, + }, + signInButtonAvatar: { + width: 80, + }, + + anonymousRoomFooterSignInButton: { + width: 110, + }, + + roomHeaderAvatarSize: { + height: variables.componentSizeLarge, + width: variables.componentSizeLarge, + }, + + roomHeaderAvatar: { + backgroundColor: theme.appBG, + borderRadius: 100, + borderColor: theme.componentBG, + borderWidth: 4, + }, + + roomHeaderAvatarOverlay: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + backgroundColor: theme.overlay, + opacity: variables.overlayOpacity, + borderRadius: 88, + }, + + rootNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1} satisfies ViewStyle), + RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1} satisfies ViewStyle), + + avatarInnerTextChat: { + color: theme.textLight, + fontSize: variables.fontSizeXLarge, + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + textAlign: 'center', + fontWeight: 'normal', + position: 'absolute', + width: 88, + left: -16, + }, + + pageWrapper: { + width: '100%', + alignItems: 'center', + padding: 20, + }, + + avatarSectionWrapper: { + width: '100%', + alignItems: 'center', + paddingHorizontal: 20, + paddingBottom: 20, + }, + + avatarSectionWrapperSkeleton: { + width: '100%', + paddingHorizontal: 20, + paddingBottom: 20, + }, + + selectCircle: { + width: variables.componentSizeSmall, + height: variables.componentSizeSmall, + borderColor: theme.border, + borderWidth: 1, + borderRadius: variables.componentSizeSmall / 2, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme.componentBG, + marginLeft: 8, + }, + + unreadIndicatorContainer: { + position: 'absolute', + top: -10, + left: 0, + width: '100%', + height: 20, + paddingHorizontal: 20, + flexDirection: 'row', + alignItems: 'center', + zIndex: 1, + ...cursor.cursorDefault, + }, + + unreadIndicatorLine: { + height: 1, + backgroundColor: theme.unreadIndicator, + flexGrow: 1, + marginRight: 8, + opacity: 0.5, + }, + + threadDividerLine: { + height: 1, + backgroundColor: theme.border, + flexGrow: 1, + marginHorizontal: 20, + }, + + unreadIndicatorText: { + color: theme.unreadIndicator, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeSmall, + fontWeight: fontWeightBold, + textTransform: 'capitalize', + }, + + flipUpsideDown: { + transform: [{rotate: '180deg'}], + }, + + navigationScreenCardStyle: { + backgroundColor: theme.appBG, + height: '100%', + }, + + invisible: { + position: 'absolute', + opacity: 0, + }, + + invisiblePopover: { + position: 'absolute', + opacity: 0, + left: -9999, + }, + + containerWithSpaceBetween: { + justifyContent: 'space-between', + width: '100%', + flex: 1, + }, + + detailsPageSectionContainer: { + alignSelf: 'flex-start', + }, + + attachmentCarouselContainer: { + height: '100%', + width: '100%', + display: 'flex', + justifyContent: 'center', + ...cursor.cursorUnset, + }, + + attachmentArrow: { + zIndex: 23, + position: 'absolute', + }, + + attachmentRevealButtonContainer: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + ...spacing.ph4, + }, + + arrowIcon: { + height: 40, + width: 40, + alignItems: 'center', + paddingHorizontal: 0, + paddingTop: 0, + paddingBottom: 0, + }, + + switchTrack: { + width: 50, + height: 28, + justifyContent: 'center', + borderRadius: 20, + padding: 15, + backgroundColor: theme.success, + }, + + switchInactive: { + backgroundColor: theme.border, + }, + + switchThumb: { + width: 22, + height: 22, + borderRadius: 11, + position: 'absolute', + left: 4, + backgroundColor: theme.appBG, + }, + + switchThumbTransformation: (translateX: AnimatableNumericValue) => + ({ + transform: [{translateX}], + } satisfies ViewStyle), + + radioButtonContainer: { + backgroundColor: theme.componentBG, + borderRadius: 10, + height: 20, + width: 20, + borderColor: theme.icon, + borderWidth: 1, + justifyContent: 'center', + alignItems: 'center', + }, + + checkedContainer: { + backgroundColor: theme.checkBox, + }, + + magicCodeInputContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + minHeight: variables.inputHeight, + }, + + magicCodeInput: { + fontSize: variables.fontSizeXLarge, + color: theme.heading, + lineHeight: variables.inputHeight, + }, + + // Manually style transparent, in iOS Safari, an input in a container with its opacity set to + // 0 (completely transparent) cannot handle user interaction, hence the Paste option is never shown + inputTransparent: { + color: 'transparent', + // These properties are available in browser only + ...(Browser.getBrowser() + ? { + caretColor: 'transparent', + WebkitTextFillColor: 'transparent', + // After setting the input text color to transparent, it acquires the background-color. + // However, it is not possible to override the background-color directly as explained in this resource: https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill + // Therefore, the transition effect needs to be delayed. + transitionDelay: '99999s', + transitionProperty: 'background-color', + } + : {}), + }, + + iouAmountText: { + ...headlineFont, + fontSize: variables.iouAmountTextSize, + color: theme.heading, + lineHeight: variables.inputHeight, + }, + + iouAmountTextInput: addOutlineWidth( + { + ...headlineFont, + fontSize: variables.iouAmountTextSize, + color: theme.heading, + padding: 0, + lineHeight: undefined, + }, + 0, + ), + + moneyRequestConfirmationAmount: { + ...headlineFont, + fontSize: variables.fontSizeh1, + }, + + moneyRequestMenuItem: { + flexDirection: 'row', + borderRadius: 0, + justifyContent: 'space-between', + width: '100%', + paddingHorizontal: 20, + paddingVertical: 12, + }, + + requestPreviewBox: { + marginTop: 12, + maxWidth: variables.reportPreviewMaxWidth, + }, + + moneyRequestPreviewBox: { + backgroundColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusLarge, + maxWidth: variables.reportPreviewMaxWidth, + width: '100%', + }, + + moneyRequestPreviewBoxText: { + padding: 16, + }, + + amountSplitPadding: { + paddingTop: 2, + }, + + moneyRequestPreviewBoxLoading: { + // When a new IOU request arrives it is very briefly in a loading state, so set the minimum height of the container to 94 to match the rendered height after loading. + // Otherwise, the IOU request pay button will not be fully visible and the user will have to scroll up to reveal the entire IOU request container. + // See https://github.com/Expensify/App/issues/10283. + minHeight: 94, + width: '100%', + }, + + moneyRequestPreviewBoxAvatar: { + marginRight: -10, + marginBottom: 0, + }, + + moneyRequestPreviewAmount: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + }, + + defaultCheckmarkWrapper: { + marginLeft: 8, + alignSelf: 'center', + }, + + codeWordWrapper: { + ...codeStyles.codeWordWrapper, + }, + + codeWordStyle: { + borderLeftWidth: 0, + borderRightWidth: 0, + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + paddingLeft: 0, + paddingRight: 0, + justifyContent: 'center', + ...codeStyles.codeWordStyle, + }, + + codeFirstWordStyle: { + borderLeftWidth: 1, + borderTopLeftRadius: 4, + borderBottomLeftRadius: 4, + paddingLeft: 5, + }, + + codeLastWordStyle: { + borderRightWidth: 1, + borderTopRightRadius: 4, + borderBottomRightRadius: 4, + paddingRight: 5, + }, + + fullScreenLoading: { + backgroundColor: theme.componentBG, + opacity: 0.8, + justifyContent: 'center', + alignItems: 'center', + zIndex: 10, + }, + + reimbursementAccountFullScreenLoading: { + backgroundColor: theme.componentBG, + opacity: 0.8, + justifyContent: 'flex-start', + alignItems: 'center', + zIndex: 10, + }, + + hiddenElementOutsideOfWindow: { + position: 'absolute', + top: -10000, + left: 0, + opacity: 0, + }, + + growlNotificationWrapper: { + zIndex: 2, + }, + + growlNotificationContainer: { + flex: 1, + justifyContent: 'flex-start', + position: 'absolute', + width: '100%', + top: 20, + ...spacing.pl5, + ...spacing.pr5, + }, + + growlNotificationDesktopContainer: { + maxWidth: variables.sideBarWidth, + right: 0, + ...positioning.pFixed, + }, + + growlNotificationTranslateY: (translateY: AnimatableNumericValue) => + ({ + transform: [{translateY}], + } satisfies ViewStyle), + + makeSlideInTranslation: (translationType: Translation, fromValue: number) => + ({ + from: { + [translationType]: fromValue, + }, + to: { + [translationType]: 0, + }, + } satisfies CustomAnimation), + + growlNotificationBox: { + backgroundColor: theme.inverse, + borderRadius: variables.componentBorderRadiusNormal, + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'space-between', + shadowColor: theme.shadow, + ...spacing.p5, + }, + + growlNotificationText: { + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.EXP_NEUE, + width: '90%', + lineHeight: variables.fontSizeNormalHeight, + color: theme.textReversed, + ...spacing.ml4, + }, + + blockquote: { + borderLeftColor: theme.border, + borderLeftWidth: 4, + paddingLeft: 12, + marginVertical: 4, + }, + + noSelect: { + boxShadow: 'none', + outlineStyle: 'none', + }, + + cardStyleNavigator: { + overflow: 'hidden', + height: '100%', + }, + + smallEditIcon: { + alignItems: 'center', + backgroundColor: theme.buttonHoveredBG, + borderColor: theme.textReversed, + borderRadius: 14, + borderWidth: 3, + color: theme.textReversed, + height: 28, + width: 28, + justifyContent: 'center', + }, + + smallAvatarEditIcon: { + position: 'absolute', + right: -4, + bottom: -4, + }, + + autoGrowHeightMultilineInput: { + maxHeight: 115, + }, + + peopleRow: { + width: '100%', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + ...spacing.ph5, + }, + + peopleRowBorderBottom: { + borderColor: theme.border, + borderBottomWidth: 1, + ...spacing.pb2, + }, + + peopleBadge: { + backgroundColor: theme.icon, + ...spacing.ph3, + }, + + peopleBadgeText: { + color: theme.textReversed, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightNormal, + ...whiteSpace.noWrap, + }, + + offlineFeedback: { + deleted: { + textDecorationLine: 'line-through', + textDecorationStyle: 'solid', + }, + pending: { + opacity: 0.5, + }, + error: { + flexDirection: 'row', + alignItems: 'center', + }, + container: { + ...spacing.pv2, + }, + textContainer: { + flexDirection: 'column', + flex: 1, + }, + text: { + color: theme.textSupporting, + textAlignVertical: 'center', + fontSize: variables.fontSizeLabel, + }, + errorDot: { + marginRight: 12, + }, + }, + + dotIndicatorMessage: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, + + locationErrorLinkText: { + textAlignVertical: 'center', + fontSize: variables.fontSizeLabel, + }, + + sidebarPopover: { + width: variables.sideBarWidth - 68, + }, + + cardOverlay: { + backgroundColor: theme.overlay, + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + opacity: variables.overlayOpacity, + }, + + shortTermsBorder: { + borderWidth: 1, + borderColor: theme.border, + }, + + shortTermsHorizontalRule: { + borderBottomWidth: 1, + borderColor: theme.border, + ...spacing.mh3, + }, + + shortTermsLargeHorizontalRule: { + borderWidth: 1, + borderColor: theme.border, + ...spacing.mh3, + }, + + shortTermsRow: { + flexDirection: 'row', + padding: 12, + }, + + termsCenterRight: { + marginTop: 'auto', + marginBottom: 'auto', + }, + + shortTermsBoldHeadingSection: { + paddingRight: 12, + paddingLeft: 12, + marginTop: 12, + }, + + shortTermsHeadline: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + fontSize: variables.fontSizeXXXLarge, + lineHeight: variables.lineHeightXXXLarge, + }, + + longTermsRow: { + flexDirection: 'row', + marginTop: 20, + }, + + collapsibleSectionBorder: { + borderBottomWidth: 2, + borderBottomColor: theme.border, + }, + + communicationsLinkHeight: { + height: variables.communicationsLinkHeight, + }, + + floatingMessageCounterWrapper: { + position: 'absolute', + left: '50%', + top: 0, + zIndex: 100, + ...visibility.hidden, + }, + + floatingMessageCounterWrapperAndroid: { + left: 0, + width: '100%', + alignItems: 'center', + position: 'absolute', + top: 0, + zIndex: 100, + ...visibility.hidden, + }, + + floatingMessageCounterSubWrapperAndroid: { + left: '50%', + width: 'auto', + }, + + floatingMessageCounter: { + left: '-50%', + ...visibility.visible, + }, + + floatingMessageCounterTransformation: (translateY: AnimatableNumericValue) => + ({ + transform: [{translateY}], + } satisfies ViewStyle), + + confirmationAnimation: { + height: 180, + width: 180, + marginBottom: 20, + }, + + googleSearchTextInputContainer: { + flexDirection: 'column', + }, + + googleSearchSeparator: { + height: 1, + backgroundColor: theme.border, + }, + + googleSearchText: { + color: theme.text, + fontSize: variables.fontSizeNormal, + lineHeight: variables.fontSizeNormalHeight, + fontFamily: fontFamily.EXP_NEUE, + flex: 1, + }, + + threeDotsPopoverOffset: (windowWidth: number) => + ({ + ...getPopOverVerticalOffset(60), + horizontal: windowWidth - 60, + } satisfies AnchorPosition), + + threeDotsPopoverOffsetNoCloseButton: (windowWidth: number) => + ({ + ...getPopOverVerticalOffset(60), + horizontal: windowWidth - 10, + } satisfies AnchorPosition), + + threeDotsPopoverOffsetAttachmentModal: (windowWidth: number) => + ({ + ...getPopOverVerticalOffset(80), + horizontal: windowWidth - 140, + } satisfies AnchorPosition), + + iPhoneXSafeArea: { + backgroundColor: theme.inverse, + flex: 1, + }, + + transferBalancePayment: { + borderWidth: 1, + borderRadius: variables.componentBorderRadiusNormal, + borderColor: theme.border, + }, + + transferBalanceSelectedPayment: { + borderColor: theme.iconSuccessFill, + }, + + transferBalanceBalance: { + fontSize: 48, + }, + + imageCropContainer: { + overflow: 'hidden', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.imageCropBackgroundColor, + ...cursor.cursorMove, + }, + + sliderKnobTooltipView: { + height: variables.sliderKnobSize, + width: variables.sliderKnobSize, + borderRadius: variables.sliderKnobSize / 2, + }, + + sliderKnob: { + backgroundColor: theme.success, + position: 'absolute', + height: variables.sliderKnobSize, + width: variables.sliderKnobSize, + borderRadius: variables.sliderKnobSize / 2, + left: -(variables.sliderKnobSize / 2), + ...cursor.cursorPointer, + }, + + sliderBar: { + backgroundColor: theme.border, + height: variables.sliderBarHeight, + borderRadius: variables.sliderBarHeight / 2, + alignSelf: 'stretch', + justifyContent: 'center', + }, + + screenCenteredContainer: { + flex: 1, + justifyContent: 'center', + marginBottom: 40, + padding: 16, + }, + + inlineSystemMessage: { + color: theme.textSupporting, + fontSize: variables.fontSizeLabel, + fontFamily: fontFamily.EXP_NEUE, + marginLeft: 6, + }, + + fullScreen: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, + + invisibleOverlay: { + backgroundColor: theme.transparent, + zIndex: 1000, + }, + + reportDropOverlay: { + backgroundColor: theme.dropUIBG, + zIndex: 2, + }, + + receiptDropOverlay: { + backgroundColor: theme.receiptDropUIBG, + zIndex: 2, + }, + + receiptImageWrapper: (receiptImageTopPosition: number) => + ({ + position: 'absolute', + top: receiptImageTopPosition, + } satisfies ViewStyle), + + cardSection: { + backgroundColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusCard, + marginBottom: 20, + marginHorizontal: 16, + padding: 20, + width: 'auto', + textAlign: 'left', + }, + + cardSectionTitle: { + lineHeight: variables.lineHeightXXLarge, + }, + + cardMenuItem: { + paddingLeft: 0, + paddingRight: 0, + borderRadius: variables.buttonBorderRadius, + height: variables.componentSizeLarge, + alignItems: 'center', + }, + + transferBalance: { + paddingLeft: 20, + paddingRight: 20, + borderRadius: 0, + height: 64, + alignItems: 'center', + }, + + paymentMethod: { + paddingHorizontal: 20, + height: 64, + }, + + archivedReportFooter: { + borderRadius: variables.componentBorderRadius, + ...wordBreak.breakWord, + }, + + deeplinkWrapperContainer: { + padding: 20, + flex: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.appBG, + }, + + deeplinkWrapperMessage: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + + deeplinkWrapperFooter: { + paddingTop: 80, + paddingBottom: 45, + }, + + emojiReactionBubble: { + borderRadius: 28, + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'row', + alignSelf: 'flex-start', + }, + + emojiReactionListHeader: { + marginTop: 8, + paddingBottom: 20, + borderBottomColor: theme.border, + borderBottomWidth: 1, + marginHorizontal: 20, + }, + emojiReactionListHeaderBubble: { + paddingVertical: 2, + paddingHorizontal: 8, + borderRadius: 28, + backgroundColor: theme.border, + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'row', + alignSelf: 'flex-start', + marginRight: 4, + }, + + reactionListHeaderText: { + color: theme.textSupporting, + marginLeft: 8, + alignSelf: 'center', + }, + + miniQuickEmojiReactionText: { + fontSize: 15, + lineHeight: 20, + textAlignVertical: 'center', + }, + + emojiReactionBubbleText: { + textAlignVertical: 'center', + }, + + reactionCounterText: { + fontSize: 13, + marginLeft: 4, + fontWeight: 'bold', + }, + + fontColorReactionLabel: { + color: theme.tooltipSupportingText, + }, + + reactionEmojiTitle: { + fontSize: variables.iconSizeLarge, + lineHeight: variables.iconSizeXLarge, + }, + + textReactionSenders: { + color: theme.tooltipPrimaryText, + ...wordBreak.breakWord, + }, + + quickReactionsContainer: { + gap: 12, + flexDirection: 'row', + paddingHorizontal: 25, + paddingVertical: 12, + justifyContent: 'space-between', + }, + + reactionListContainer: { + maxHeight: variables.listItemHeightNormal * 5.75, + ...spacing.pv2, + }, + + reactionListContainerFixedWidth: { + maxWidth: variables.popoverWidth, + }, + + validateCodeDigits: { + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeXXLarge, + letterSpacing: 4, + }, + + footerWrapper: { + fontSize: variables.fontSizeNormal, + paddingTop: 64, + maxWidth: 1100, // Match footer across all Expensify platforms + }, + + footerColumnsContainer: { + flex: 1, + flexWrap: 'wrap', + marginBottom: 40, + marginHorizontal: -16, + }, + + footerTitle: { + fontSize: variables.fontSizeLarge, + color: theme.success, + marginBottom: 16, + }, + + footerRow: { + paddingVertical: 4, + marginBottom: 8, + color: theme.textLight, + fontSize: variables.fontSizeMedium, + }, + + footerBottomLogo: { + marginTop: 40, + width: '100%', + }, + + datePickerRoot: { + position: 'relative', + zIndex: 99, + }, + + datePickerPopover: { + backgroundColor: theme.appBG, + width: '100%', + alignSelf: 'center', + zIndex: 100, + marginTop: 8, + }, + + loginHeroHeader: { + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + color: theme.success, + fontWeight: '500', + textAlign: 'center', + }, + + newKansasLarge: { + ...headlineFont, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + }, + + eReceiptAmount: { + ...headlineFont, + fontSize: variables.fontSizeXXXLarge, + lineHeight: variables.lineHeightXXXLarge, + color: colors.green400, + }, + + eReceiptAmountLarge: { + ...headlineFont, + fontSize: variables.fontSizeEReceiptLarge, + lineHeight: variables.lineHeightXXLarge, + wordBreak: 'break-word', + textAlign: 'center', + }, + + eReceiptCurrency: { + ...headlineFont, + fontSize: variables.fontSizeXXLarge, + lineHeight: variables.lineHeightXXLarge, + wordBreak: 'break-all', + }, + + eReceiptMerchant: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + color: theme.text, + }, + + eReceiptWaypointTitle: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + color: colors.green400, + }, + + eReceiptWaypointAddress: { + fontFamily: fontFamily.MONOSPACE, + fontSize: variables.fontSizeNormal, + lineHeight: variables.lineHeightNormal, + color: theme.textColorfulBackground, + }, + + eReceiptGuaranteed: { + fontFamily: fontFamily.MONOSPACE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + color: theme.textColorfulBackground, + }, + + eReceiptBackground: { + ...sizing.w100, + borderRadius: 20, + position: 'absolute', + top: 0, + left: 0, + height: 540, + }, + + eReceiptPanel: { + ...spacing.p5, + ...spacing.pb8, + ...spacing.m5, + backgroundColor: colors.green800, + borderRadius: 20, + width: 335, + }, + + eReceiptBackgroundThumbnail: { + ...sizing.w100, + position: 'absolute', + aspectRatio: 335 / 540, + top: 0, + minWidth: 217, + }, + + eReceiptContainer: { + flex: 1, + width: 335, + minHeight: 540, + borderRadius: 20, + overflow: 'hidden', + }, + + loginHeroBody: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSignInHeroBody, + color: theme.textLight, + textAlign: 'center', + }, + + linkPreviewWrapper: { + marginTop: 16, + borderLeftWidth: 4, + borderLeftColor: theme.border, + paddingLeft: 12, + }, + + linkPreviewImage: { + flex: 1, + resizeMode: 'contain', + borderRadius: 8, + marginTop: 8, + }, + + linkPreviewLogoImage: { + height: 16, + width: 16, + }, + + contextMenuItemPopoverMaxWidth: { + maxWidth: 375, + }, + + formSpaceVertical: { + height: 20, + width: 1, + }, + + taskCheckbox: { + height: 16, + width: 16, + }, + + taskTitleMenuItem: { + ...writingDirection.ltr, + ...headlineFont, + ...flex.flexWrap, + ...flex.flex1, + fontSize: variables.fontSizeXLarge, + maxWidth: '100%', + ...wordBreak.breakWord, + }, + + taskDescriptionMenuItem: { + maxWidth: '100%', + ...wordBreak.breakWord, + }, + + taskTitleDescription: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.textSupporting, + lineHeight: variables.lineHeightNormal, + ...spacing.mb1, + }, + + taskMenuItemCheckbox: { + height: 27, + ...spacing.mr3, + }, + + reportHorizontalRule: { + borderColor: theme.border, + ...spacing.mh5, + }, + + assigneeTextStyle: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + minHeight: variables.avatarSizeSubscript, + }, + + taskRightIconContainer: { + width: variables.componentSizeNormal, + marginLeft: 'auto', + ...spacing.mt1, + ...pointerEventsAuto, + ...display.dFlex, + ...flex.alignItemsCenter, + }, + + shareCodePage: { + paddingHorizontal: 38.5, + }, + + shareCodeContainer: { + width: '100%', + alignItems: 'center', + paddingHorizontal: variables.qrShareHorizontalPadding, + paddingVertical: 20, + borderRadius: 20, + overflow: 'hidden', + borderColor: theme.borderFocus, + borderWidth: 2, + backgroundColor: theme.highlightBG, + }, + + splashScreenHider: { + backgroundColor: theme.splashBG, + alignItems: 'center', + justifyContent: 'center', + }, + + headerEnvBadge: { + marginLeft: 0, + marginBottom: 2, + height: 12, + paddingLeft: 4, + paddingRight: 4, + alignItems: 'center', + }, + + headerEnvBadgeText: { + fontSize: 7, + fontWeight: fontWeightBold, + lineHeight: undefined, + }, + + expensifyQrLogo: { + alignSelf: 'stretch', + height: 27, + marginBottom: 20, + }, + + qrShareTitle: { + marginTop: 15, + textAlign: 'center', + }, + + loginButtonRow: { + width: '100%', + gap: 12, + ...flex.flexRow, + ...flex.justifyContentCenter, + }, + + loginButtonRowSmallScreen: { + width: '100%', + gap: 12, + ...flex.flexRow, + ...flex.justifyContentCenter, + marginBottom: 10, + }, + + desktopSignInButtonContainer: { + width: 40, + height: 40, + }, + + signInIconButton: { + paddingVertical: 2, + }, + + googleButtonContainer: { + colorScheme: 'light', + width: 40, + height: 40, + alignItems: 'center', + overflow: 'hidden', + }, + + googlePillButtonContainer: { + colorScheme: 'light', + height: 40, + width: 219, + }, + + thirdPartyLoadingContainer: { + alignItems: 'center', + justifyContent: 'center', + height: 450, + }, + + tabSelectorButton: { + height: variables.tabSelectorButtonHeight, + padding: variables.tabSelectorButtonPadding, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + borderRadius: variables.buttonBorderRadius, + }, + + tabSelector: { + flexDirection: 'row', + paddingHorizontal: 20, + paddingBottom: 12, + }, + + tabText: (isSelected: boolean) => + ({ + marginLeft: 8, + fontFamily: isSelected ? fontFamily.EXP_NEUE_BOLD : fontFamily.EXP_NEUE, + fontWeight: isSelected ? fontWeightBold : '400', + color: isSelected ? theme.textLight : theme.textSupporting, + } satisfies TextStyle), + + tabBackground: (hovered: boolean, isFocused: boolean, background: string) => ({ + backgroundColor: hovered && !isFocused ? theme.highlightBG : background, + }), + + tabOpacity: (hovered: boolean, isFocused: boolean, activeOpacityValue: number, inactiveOpacityValue: number) => ({ + opacity: hovered && !isFocused ? inactiveOpacityValue : activeOpacityValue, + }), + + overscrollSpacer: (backgroundColor: string, height: number) => + ({ + backgroundColor, + height, + width: '100%', + position: 'absolute', + top: -height, + left: 0, + right: 0, + } satisfies ViewStyle), + + dualColorOverscrollSpacer: { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + zIndex: -1, + }, + + willChangeTransform: { + willChange: 'transform', + }, + + dropDownButtonCartIconContainerPadding: { + paddingRight: 0, + paddingLeft: 0, + }, + + dropDownButtonArrowContain: { + marginLeft: 12, + marginRight: 14, + }, + + dropDownButtonCartIconView: { + borderTopRightRadius: variables.buttonBorderRadius, + borderBottomRightRadius: variables.buttonBorderRadius, + ...flex.flexRow, + ...flex.alignItemsCenter, + }, + + emojiPickerButtonDropdown: { + justifyContent: 'center', + backgroundColor: theme.activeComponentBG, + width: 86, + height: 52, + borderRadius: 26, + alignItems: 'center', + paddingLeft: 10, + paddingRight: 4, + marginBottom: 32, + alignSelf: 'flex-start', + }, + + emojiPickerButtonDropdownIcon: { + fontSize: 30, + }, + + moneyRequestImage: { + height: 200, + borderRadius: 16, + margin: 20, + }, + + reportPreviewBox: { + backgroundColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusLarge, + maxWidth: variables.sideBarWidth, + width: '100%', + }, + + reportPreviewBoxHoverBorder: { + borderColor: theme.border, + backgroundColor: theme.border, + }, + + reportContainerBorderRadius: { + borderRadius: variables.componentBorderRadiusLarge, + }, + + reportPreviewBoxBody: { + padding: 16, + }, + + reportActionItemImages: { + flexDirection: 'row', + borderWidth: 4, + borderColor: theme.transparent, + borderTopLeftRadius: variables.componentBorderRadiusLarge, + borderTopRightRadius: variables.componentBorderRadiusLarge, + overflow: 'hidden', + height: variables.reportActionImagesSingleImageHeight, + }, + + reportActionItemImage: { + flex: 1, + width: '100%', + height: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, + + reportActionItemImageBorder: { + borderRightWidth: 4, + borderColor: theme.cardBG, + }, + + reportActionItemImagesMoreContainer: { + position: 'absolute', + bottom: 0, + right: 0, + display: 'flex', + }, + + reportActionItemImagesMore: { + borderTopLeftRadius: 12, + backgroundColor: theme.border, + width: 40, + height: 40, + }, + + reportActionItemImagesMoreHovered: { + backgroundColor: theme.cardBG, + }, + + reportActionItemImagesMoreText: { + position: 'absolute', + marginLeft: 20, + marginTop: 16, + color: theme.textSupporting, + }, + + reportActionItemImagesMoreCornerTriangle: { + position: 'absolute', + bottom: 0, + right: 0, + width: 0, + height: 0, + borderStyle: 'solid', + borderWidth: 0, + borderBottomWidth: 40, + borderLeftWidth: 40, + borderColor: 'transparent', + borderBottomColor: theme.cardBG, + }, + + reportActionItemImagesMoreCornerTriangleHighlighted: { + borderColor: 'transparent', + borderBottomColor: theme.border, + }, + + moneyRequestHeaderStatusBarBadge: { + paddingHorizontal: 8, + borderRadius: variables.componentBorderRadiusSmall, + height: variables.inputHeightSmall, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme.border, + marginRight: 12, + }, + + staticHeaderImage: { + minHeight: 240, + }, + + emojiPickerButtonDropdownContainer: { + flexDirection: 'row', + alignItems: 'center', + }, + + rotate90: { + transform: [{rotate: '90deg'}], + }, + + emojiStatusLHN: { + fontSize: 22, + }, + sidebarStatusAvatarContainer: { + height: 44, + width: 84, + backgroundColor: theme.componentBG, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + borderRadius: 42, + paddingHorizontal: 2, + marginVertical: -2, + marginRight: -2, + }, + sidebarStatusAvatar: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + + moneyRequestViewImage: { + ...spacing.mh5, + ...spacing.mv3, + overflow: 'hidden', + borderWidth: 2, + borderColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusLarge, + height: 200, + maxWidth: 400, + }, + + mapViewContainer: { + ...flex.flex1, + ...spacing.p4, + minHeight: 300, + maxHeight: 500, + }, + + mapView: { + flex: 1, + borderRadius: 16, + overflow: 'hidden', + }, + + mapViewOverlay: { + flex: 1, + position: 'absolute', + left: 0, + top: 0, + borderRadius: variables.componentBorderRadiusLarge, + overflow: 'hidden', + backgroundColor: theme.highlightBG, + ...sizing.w100, + ...sizing.h100, + }, + + confirmationListMapItem: { + ...spacing.mv2, + ...spacing.mh5, + height: 200, + }, + + mapDirection: { + lineColor: theme.success, + lineWidth: 7, + }, + + mapDirectionLayer: { + layout: {'line-join': 'round', 'line-cap': 'round'}, + paint: {'line-color': theme.success, 'line-width': 7}, + }, + + mapPendingView: { + backgroundColor: theme.highlightBG, + ...flex.flex1, + borderRadius: variables.componentBorderRadiusLarge, + }, + userReportStatusEmoji: { + flexShrink: 0, + fontSize: variables.fontSizeNormal, + marginRight: 4, + }, + draggableTopBar: { + height: 30, + width: '100%', + }, + + videoContainer: { + ...flex.flex1, + ...flex.alignItemsCenter, + ...flex.justifyContentCenter, + ...objectFit.oFCover, + }, + + globalNavigation: { + width: variables.globalNavigationWidth, + backgroundColor: theme.highlightBG, + }, + + globalNavigationMenuContainer: { + marginTop: 13, + }, + + globalAndSubNavigationContainer: { + backgroundColor: theme.highlightBG, + }, + + globalNavigationSelectionIndicator: (isFocused: boolean) => ({ + width: 4, + height: 52, + borderTopRightRadius: variables.componentBorderRadiusRounded, + borderBottomRightRadius: variables.componentBorderRadiusRounded, + backgroundColor: isFocused ? theme.iconMenu : theme.transparent, + }), + + globalNavigationMenuItem: (isFocused: boolean) => (isFocused ? {color: theme.text, fontWeight: fontWeightBold, fontFamily: fontFamily.EXP_NEUE_BOLD} : {color: theme.icon}), + + globalNavigationItemContainer: { + width: variables.globalNavigationWidth, + height: variables.globalNavigationWidth, + }, + + walletCard: { + borderRadius: variables.componentBorderRadiusLarge, + position: 'relative', + alignSelf: 'center', + overflow: 'hidden', + }, + + walletCardMenuItem: { + color: theme.text, + fontSize: variables.fontSizeNormal, + }, + + walletCardHolder: { + position: 'absolute', + left: 16, + bottom: 16, + width: variables.cardNameWidth, + color: theme.text, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightLarge, + }, + + walletBalance: { + lineHeight: undefined, + fontSize: 45, + paddingTop: 0, + paddingBottom: 0, + }, + + aspectRatioLottie: (source) => { + if (!source.uri && typeof source === 'object' && source.w && source.h) { + return {aspectRatio: source.w / source.h}; + } + return {}; + }, + + receiptDropHeaderGap: { + backgroundColor: theme.receiptDropUIBG, + }, + + checkboxWithLabelCheckboxStyle: { + marginLeft: -2, + }, + } satisfies Styles); + +// For now we need to export the styles function that takes the theme as an argument +// as something named different than "styles", because a lot of files import the "defaultStyles" +// as "styles", which causes ESLint to throw an error. +// TODO: Remove "stylesGenerator" and instead only return "styles" once the app is migrated to theme switching hooks and HOCs and "styles/theme/default.js" is not used anywhere anymore (GH issue: https://github.com/Expensify/App/issues/27337) +const stylesGenerator = styles; +const defaultStyles = styles(defaultTheme); + +export default defaultStyles; +export {stylesGenerator}; diff --git a/src/styles/themes/default.js b/src/styles/themes/default.ts similarity index 96% rename from src/styles/themes/default.js rename to src/styles/themes/default.ts index 0c257f773dba..aabe9140bf9d 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.ts @@ -1,6 +1,6 @@ -/* eslint-disable no-unused-vars */ -import colors from '../colors'; import SCREENS from '../../SCREENS'; +import colors from '../colors'; +import type {ThemeBase} from './types'; const darkTheme = { // Figma keys @@ -81,10 +81,10 @@ const darkTheme = { QRLogo: colors.green400, starDefaultBG: 'rgb(254, 228, 94)', loungeAccessOverlay: colors.blue800, - selectionListIndicatorColor: colors.white, mapAttributionText: colors.black, - walletPageBG: colors.green700, -}; + PAGE_BACKGROUND_COLORS: {}, + white: colors.white, +} satisfies ThemeBase; darkTheme.PAGE_BACKGROUND_COLORS = { [SCREENS.HOME]: darkTheme.sidebar, diff --git a/src/styles/themes/light.js b/src/styles/themes/light.ts similarity index 95% rename from src/styles/themes/light.js rename to src/styles/themes/light.ts index d9a9f33cae44..cd3079c3313f 100644 --- a/src/styles/themes/light.js +++ b/src/styles/themes/light.ts @@ -1,5 +1,6 @@ -import colors from '../colors'; import SCREENS from '../../SCREENS'; +import colors from '../colors'; +import type {ThemeDefault} from './types'; const lightTheme = { // Figma keys @@ -60,7 +61,7 @@ const lightTheme = { heroCard: colors.blue400, uploadPreviewActivityIndicator: colors.lightHighlightBackground, dropUIBG: 'rgba(252, 251, 249, 0.92)', - dropTransparentOverlay: 'rgba(255,255,255,0)', + receiptDropUIBG: 'rgba(3, 212, 124, 0.84)', checkBox: colors.green400, pickerOptionsTextColor: colors.lightPrimaryText, imageCropBackgroundColor: colors.lightIcons, @@ -81,7 +82,9 @@ const lightTheme = { starDefaultBG: 'rgb(254, 228, 94)', loungeAccessOverlay: colors.blue800, mapAttributionText: colors.black, -}; + PAGE_BACKGROUND_COLORS: {}, + white: colors.white, +} satisfies ThemeDefault; lightTheme.PAGE_BACKGROUND_COLORS = { [SCREENS.HOME]: lightTheme.sidebar, diff --git a/src/styles/themes/types.ts b/src/styles/themes/types.ts new file mode 100644 index 000000000000..40b8da361654 --- /dev/null +++ b/src/styles/themes/types.ts @@ -0,0 +1,8 @@ +import DeepRecord from '../../types/utils/DeepRecord'; +import defaultTheme from './default'; + +type ThemeBase = DeepRecord; + +type ThemeDefault = typeof defaultTheme; + +export type {ThemeBase, ThemeDefault}; diff --git a/src/styles/utilities/cursor/types.ts b/src/styles/utilities/cursor/types.ts index 9ca20c5ae123..e9cfc120b161 100644 --- a/src/styles/utilities/cursor/types.ts +++ b/src/styles/utilities/cursor/types.ts @@ -1,4 +1,4 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; type CursorStylesKeys = | 'cursorDefault' @@ -13,6 +13,6 @@ type CursorStylesKeys = | 'cursorInitial' | 'cursorText'; -type CursorStyles = Record>>; +type CursorStyles = Record>; export default CursorStyles; diff --git a/src/styles/utilities/display.ts b/src/styles/utilities/display.ts index 868c2bdb0e3b..f14a25d641b1 100644 --- a/src/styles/utilities/display.ts +++ b/src/styles/utilities/display.ts @@ -1,4 +1,3 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; /** @@ -20,15 +19,35 @@ export default { display: 'none', }, + /** + * Web-only style. + */ dInline: { - display: 'inline', + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". + display: 'inline' as ViewStyle['display'], }, + /** + * Web-only style. + */ dInlineFlex: { - display: 'inline-flex', + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". + display: 'inline-flex' as ViewStyle['display'], }, + /** + * Web-only style. + */ dBlock: { - display: 'block', + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". + display: 'block' as ViewStyle['display'], }, -} satisfies Record; + + /** + * Web-only style. + */ + dGrid: { + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". + display: 'grid' as ViewStyle['display'], + }, +} satisfies Record; diff --git a/src/styles/utilities/overflow.ts b/src/styles/utilities/overflow.ts index 48807283c9b4..9b4fa010af84 100644 --- a/src/styles/utilities/overflow.ts +++ b/src/styles/utilities/overflow.ts @@ -1,4 +1,4 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; import overflowAuto from './overflowAuto'; import overscrollBehaviorContain from './overscrollBehaviorContain'; @@ -27,4 +27,4 @@ export default { overscrollBehaviorContain, overflowAuto, -} satisfies Record; +} satisfies Record; diff --git a/src/styles/utilities/overflowAuto/index.ts b/src/styles/utilities/overflowAuto/index.ts index 0eb19068738f..1e7ac8ed8246 100644 --- a/src/styles/utilities/overflowAuto/index.ts +++ b/src/styles/utilities/overflowAuto/index.ts @@ -1,7 +1,12 @@ +import {ViewStyle} from 'react-native'; import OverflowAutoStyles from './types'; +/** + * Web-only style. + */ const overflowAuto: OverflowAutoStyles = { - overflow: 'auto', + // NOTE: asserting "overflow" to a valid type, because it isn't possible to augment "overflow". + overflow: 'auto' as ViewStyle['overflow'], }; export default overflowAuto; diff --git a/src/styles/utilities/overflowAuto/types.ts b/src/styles/utilities/overflowAuto/types.ts index faba7c2cbdb8..da7548d49e7b 100644 --- a/src/styles/utilities/overflowAuto/types.ts +++ b/src/styles/utilities/overflowAuto/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type OverflowAutoStyles = Pick; +type OverflowAutoStyles = Pick; export default OverflowAutoStyles; diff --git a/src/styles/utilities/positioning.ts b/src/styles/utilities/positioning.ts index 651d2a12f2ea..26e6198a5827 100644 --- a/src/styles/utilities/positioning.ts +++ b/src/styles/utilities/positioning.ts @@ -11,6 +11,14 @@ export default { pAbsolute: { position: 'absolute', }, + /** + * Web-only style. + */ + pFixed: { + // NOTE: asserting "position" to a valid type, because it isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], + }, + t0: { top: 0, }, diff --git a/src/styles/utilities/textUnderline/types.ts b/src/styles/utilities/textUnderline/types.ts index ecc09ed0fe09..f71d2bfdaa9a 100644 --- a/src/styles/utilities/textUnderline/types.ts +++ b/src/styles/utilities/textUnderline/types.ts @@ -1,8 +1,8 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; type TextUnderlineStyles = { - textUnderlinePositionUnder: Pick; - textDecorationSkipInkNone: Pick; + textUnderlinePositionUnder: Pick; + textDecorationSkipInkNone: Pick; }; export default TextUnderlineStyles; diff --git a/src/styles/utilities/userSelect/types.ts b/src/styles/utilities/userSelect/types.ts index 67a8c9c7b9b6..a177bac5a3e7 100644 --- a/src/styles/utilities/userSelect/types.ts +++ b/src/styles/utilities/userSelect/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; -type UserSelectStyles = Record<'userSelectText' | 'userSelectNone', Partial>>; +type UserSelectStyles = Record<'userSelectText' | 'userSelectNone', Pick>; export default UserSelectStyles; diff --git a/src/styles/utilities/visibility/types.ts b/src/styles/utilities/visibility/types.ts index 9dab3d7c752e..64bdbdd2cca6 100644 --- a/src/styles/utilities/visibility/types.ts +++ b/src/styles/utilities/visibility/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type VisibilityStyles = Record<'visible' | 'hidden', Partial>>; +type VisibilityStyles = Record<'visible' | 'hidden', Pick>; export default VisibilityStyles; diff --git a/src/styles/utilities/whiteSpace/types.ts b/src/styles/utilities/whiteSpace/types.ts index c42dc14080b1..b10671f04977 100644 --- a/src/styles/utilities/whiteSpace/types.ts +++ b/src/styles/utilities/whiteSpace/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; -type WhiteSpaceStyles = Record<'noWrap' | 'preWrap' | 'pre', Partial>>; +type WhiteSpaceStyles = Record<'noWrap' | 'preWrap' | 'pre', Pick>; export default WhiteSpaceStyles; diff --git a/src/styles/utilities/wordBreak/types.ts b/src/styles/utilities/wordBreak/types.ts index 003a5ca1c869..0ed520ae119d 100644 --- a/src/styles/utilities/wordBreak/types.ts +++ b/src/styles/utilities/wordBreak/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; -type WordBreakStyles = Record<'breakWord' | 'breakAll', Partial>>; +type WordBreakStyles = Record<'breakWord' | 'breakAll', Pick>; export default WordBreakStyles; diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index ebe0974db690..0659cc4e4b4a 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -1,8 +1,343 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +/* eslint-disable @typescript-eslint/no-empty-interface */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ +import {CSSProperties, FocusEventHandler, KeyboardEventHandler, MouseEventHandler, PointerEventHandler, UIEventHandler, WheelEventHandler} from 'react'; import 'react-native'; import {BootSplashModule} from '../../libs/BootSplash/types'; declare module 'react-native' { + // <------ REACT NATIVE WEB (0.19.0) ------> + // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js + type idRef = string; + type idRefList = idRef | idRef[]; + + // https://necolas.github.io/react-native-web/docs/accessibility/#accessibility-props-api + // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js + interface AccessibilityProps { + 'aria-activedescendant'?: idRef; + 'aria-atomic'?: boolean; + 'aria-autocomplete'?: 'none' | 'list' | 'inline' | 'both'; + 'aria-busy'?: boolean; + 'aria-checked'?: boolean | 'mixed'; + 'aria-colcount'?: number; + 'aria-colindex'?: number; + 'aria-colspan'?: number; + 'aria-controls'?: idRef; + 'aria-current'?: boolean | 'page' | 'step' | 'location' | 'date' | 'time'; + 'aria-describedby'?: idRef; + 'aria-details'?: idRef; + 'aria-disabled'?: boolean; + 'aria-errormessage'?: idRef; + 'aria-expanded'?: boolean; + 'aria-flowto'?: idRef; + 'aria-haspopup'?: 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | false; + 'aria-hidden'?: boolean; + 'aria-invalid'?: boolean; + 'aria-keyshortcuts'?: string[]; + 'aria-label'?: string; + 'aria-labelledby'?: idRef; + 'aria-level'?: number; + 'aria-live'?: 'assertive' | 'none' | 'polite'; + 'aria-modal'?: boolean; + 'aria-multiline'?: boolean; + 'aria-multiselectable'?: boolean; + 'aria-orientation'?: 'horizontal' | 'vertical'; + 'aria-owns'?: idRef; + 'aria-placeholder'?: string; + 'aria-posinset'?: number; + 'aria-pressed'?: boolean | 'mixed'; + 'aria-readonly'?: boolean; + 'aria-required'?: boolean; + 'aria-roledescription'?: string; + 'aria-rowcount'?: number; + 'aria-rowindex'?: number; + 'aria-rowspan'?: number; + 'aria-selected'?: boolean; + 'aria-setsize'?: number; + 'aria-sort'?: 'ascending' | 'descending' | 'none' | 'other'; + 'aria-valuemax'?: number; + 'aria-valuemin'?: number; + 'aria-valuenow'?: number; + 'aria-valuetext'?: string; + role?: string; + + // @deprecated + accessibilityActiveDescendant?: idRef; + accessibilityAtomic?: boolean; + accessibilityAutoComplete?: 'none' | 'list' | 'inline' | 'both'; + accessibilityBusy?: boolean; + accessibilityChecked?: boolean | 'mixed'; + accessibilityColumnCount?: number; + accessibilityColumnIndex?: number; + accessibilityColumnSpan?: number; + accessibilityControls?: idRefList; + accessibilityCurrent?: boolean | 'page' | 'step' | 'location' | 'date' | 'time'; + accessibilityDescribedBy?: idRefList; + accessibilityDetails?: idRef; + accessibilityDisabled?: boolean; + accessibilityErrorMessage?: idRef; + accessibilityExpanded?: boolean; + accessibilityFlowTo?: idRefList; + accessibilityHasPopup?: 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | false; + accessibilityHidden?: boolean; + accessibilityInvalid?: boolean; + accessibilityKeyShortcuts?: string[]; + accessibilityLabel?: string; + accessibilityLabelledBy?: idRefList; + accessibilityLevel?: number; + accessibilityLiveRegion?: 'assertive' | 'none' | 'polite'; + accessibilityModal?: boolean; + accessibilityMultiline?: boolean; + accessibilityMultiSelectable?: boolean; + accessibilityOrientation?: 'horizontal' | 'vertical'; + accessibilityOwns?: idRefList; + accessibilityPlaceholder?: string; + accessibilityPosInSet?: number; + accessibilityPressed?: boolean | 'mixed'; + accessibilityReadOnly?: boolean; + accessibilityRequired?: boolean; + accessibilityRole?: string; + accessibilityRoleDescription?: string; + accessibilityRowCount?: number; + accessibilityRowIndex?: number; + accessibilityRowSpan?: number; + accessibilitySelected?: boolean; + accessibilitySetSize?: number; + accessibilitySort?: 'ascending' | 'descending' | 'none' | 'other'; + accessibilityValueMax?: number; + accessibilityValueMin?: number; + accessibilityValueNow?: number; + accessibilityValueText?: string; + } + + // https://necolas.github.io/react-native-web/docs/interactions/#pointerevent-props-api + // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js + // Extracted types from @types/react, index.d.ts + interface PointerProps { + onAuxClick?: MouseEventHandler; + onClick?: MouseEventHandler; + onContextMenu?: MouseEventHandler; + onGotPointerCapture?: PointerEventHandler; + onLostPointerCapture?: PointerEventHandler; + onPointerCancel?: PointerEventHandler; + onPointerDown?: PointerEventHandler; + onPointerEnter?: PointerEventHandler; + onPointerMove?: PointerEventHandler; + onPointerLeave?: PointerEventHandler; + onPointerOut?: PointerEventHandler; + onPointerOver?: PointerEventHandler; + onPointerUp?: PointerEventHandler; + onMouseDown?: MouseEventHandler; + onMouseEnter?: MouseEventHandler; + onMouseLeave?: MouseEventHandler; + onMouseMove?: MouseEventHandler; + onMouseOver?: MouseEventHandler; + onMouseOut?: MouseEventHandler; + onMouseUp?: MouseEventHandler; + onScroll?: UIEventHandler; + onWheel?: WheelEventHandler; + } + + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderTouchHistoryStore.js + type TouchRecord = { + currentPageX: number; + currentPageY: number; + currentTimeStamp: number; + previousPageX: number; + previousPageY: number; + previousTimeStamp: number; + startPageX: number; + startPageY: number; + startTimeStamp: number; + touchActive: boolean; + }; + + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderTouchHistoryStore.js + type TouchHistory = Readonly<{ + indexOfSingleActiveTouch: number; + mostRecentTimeStamp: number; + numberActiveTouches: number; + touchBank: TouchRecord[]; + }>; + + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/createResponderEvent.js + type ResponderEvent = { + bubbles: boolean; + cancelable: boolean; + currentTarget?: unknown; // changed from "any" to "unknown" + defaultPrevented?: boolean; + dispatchConfig: { + registrationName?: string; + phasedRegistrationNames?: { + bubbled: string; + captured: string; + }; + }; + eventPhase?: number; + isDefaultPrevented: () => boolean; + isPropagationStopped: () => boolean; + isTrusted?: boolean; + preventDefault: () => void; + stopPropagation: () => void; + nativeEvent: TouchEvent; + persist: () => void; + target?: unknown; // changed from "any" to "unknown" + timeStamp: number; + touchHistory: TouchHistory; + }; + + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderSystem.js + interface ResponderProps { + // Direct responder events dispatched directly to responder. Do not bubble. + onResponderEnd?: (e: ResponderEvent) => void; + onResponderGrant?: (e: ResponderEvent) => void | boolean; + onResponderMove?: (e: ResponderEvent) => void; + onResponderRelease?: (e: ResponderEvent) => void; + onResponderReject?: (e: ResponderEvent) => void; + onResponderStart?: (e: ResponderEvent) => void; + onResponderTerminate?: (e: ResponderEvent) => void; + onResponderTerminationRequest?: (e: ResponderEvent) => boolean; + + // On pointer down, should this element become the responder? + onStartShouldSetResponder?: (e: ResponderEvent) => boolean; + onStartShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + + // On pointer move, should this element become the responder? + onMoveShouldSetResponder?: (e: ResponderEvent) => boolean; + onMoveShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + + // On scroll, should this element become the responder? Do no bubble + onScrollShouldSetResponder?: (e: ResponderEvent) => boolean; + onScrollShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + + // On text selection change, should this element become the responder? + onSelectionChangeShouldSetResponder?: (e: ResponderEvent) => boolean; + onSelectionChangeShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + } + + // https://necolas.github.io/react-native-web/docs/interactions/#focusevent-props-api + // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js + // Extracted types from @types/react, index.d.ts + interface FocusProps { + onBlur?: FocusEventHandler; + onFocus?: FocusEventHandler; + } + + // https://necolas.github.io/react-native-web/docs/interactions/#keyboardevent-props-api + // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js + // Extracted types from @types/react, index.d.ts + interface KeyboardProps { + onKeyDown?: KeyboardEventHandler; + onKeyDownCapture?: KeyboardEventHandler; + onKeyUp?: KeyboardEventHandler; + onKeyUpCapture?: KeyboardEventHandler; + } + + /** + * Shared props + * Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js + */ + interface WebSharedProps extends AccessibilityProps, PointerProps, ResponderProps, FocusProps, KeyboardProps { + dataSet?: Record; + href?: string; + hrefAttrs?: { + download?: boolean; + rel?: string; + target?: string; + }; + tabIndex?: 0 | -1; + lang?: string; + } + + /** + * View + * Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js + */ + interface WebViewProps extends WebSharedProps { + dir?: 'ltr' | 'rtl'; + } + interface ViewProps extends WebViewProps {} + + /** + * Text + * Extracted from react-native-web, packages/react-native-web/src/exports/Text/types.js + */ + interface WebTextProps extends WebSharedProps { + dir?: 'auto' | 'ltr' | 'rtl'; + } + interface TextProps extends WebTextProps {} + + /** + * TextInput + * Extracted from react-native-web, packages/react-native-web/src/exports/TextInput/types.js + */ + interface WebTextInputProps extends WebSharedProps { + dir?: 'auto' | 'ltr' | 'rtl'; + disabled?: boolean; + enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'; + readOnly?: boolean; + } + interface TextInputProps extends WebTextInputProps {} + + /** + * Image + * Extracted from react-native-web, packages/react-native-web/src/exports/Image/types.js + */ + interface WebImageProps extends WebSharedProps { + dir?: 'ltr' | 'rtl'; + draggable?: boolean; + } + interface ImageProps extends WebImageProps {} + + /** + * ScrollView + * Extracted from react-native-web, packages/react-native-web/src/exports/ScrollView/ScrollViewBase.js + */ + interface WebScrollViewProps extends WebSharedProps {} + interface ScrollViewProps extends WebScrollViewProps {} + + /** + * Pressable + */ + // https://necolas.github.io/react-native-web/docs/pressable/#interactionstate + // Extracted from react-native-web, packages/react-native-web/src/exports/Pressable/index.js + interface WebPressableStateCallbackType { + readonly focused: boolean; + readonly hovered: boolean; + readonly pressed: boolean; + } + interface PressableStateCallbackType extends WebPressableStateCallbackType {} + + // Extracted from react-native-web, packages/react-native-web/src/exports/Pressable/index.js + interface WebPressableProps extends WebSharedProps { + delayPressIn?: number; + delayPressOut?: number; + onPressMove?: null | ((event: GestureResponderEvent) => void); + onPressEnd?: null | ((event: GestureResponderEvent) => void); + } + interface PressableProps extends WebPressableProps {} + + /** + * Styles + */ + // We extend CSSProperties (alias to "csstype" library) which provides all CSS style properties for Web, + // but properties that are already defined on RN won't be overrided / augmented. + interface WebStyle extends CSSProperties { + // https://necolas.github.io/react-native-web/docs/styling/#non-standard-properties + // Exclusive to react-native-web, "pointerEvents" already included on RN + animationKeyframes?: string | Record; + writingDirection?: 'auto' | 'ltr' | 'rtl'; + } + + interface ViewStyle extends WebStyle {} + interface TextStyle extends WebStyle {} + interface ImageStyle extends WebStyle {} + // <------ REACT NATIVE WEB (0.19.0) ------> + interface TextInput { // Typescript type declaration is missing in React Native for setting text selection. setSelection: (start: number, end: number) => void; diff --git a/src/types/utils/DeepRecord.ts b/src/types/utils/DeepRecord.ts new file mode 100644 index 000000000000..fba14c75d679 --- /dev/null +++ b/src/types/utils/DeepRecord.ts @@ -0,0 +1,7 @@ +/** + * Represents a deeply nested record. It maps keys to values, + * and those values can either be of type `TValue` or further nested `DeepRecord` instances. + */ +type DeepRecord = {[key: string]: TValue | DeepRecord}; + +export default DeepRecord;