From 876b9b55d8e051399edf90656c9fe71ad9a3d6ad Mon Sep 17 00:00:00 2001 From: Faizan Shoukat Abbasi Date: Thu, 11 Jun 2026 04:55:00 +0500 Subject: [PATCH 01/10] 92969: Add 'expand' button overlay to charts for full-screen mode --- src/CONST/index.ts | 1 + .../BaseVictoryChartRenderer.tsx | 20 ++++++-- .../components/VictoryChartExpandButton.tsx | 51 +++++++++++++++++++ .../components/VictoryChartExpandModal.tsx | 49 ++++++++++++++++++ src/languages/de.ts | 1 + src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/languages/fr.ts | 1 + src/languages/it.ts | 1 + src/languages/ja.ts | 1 + src/languages/nl.ts | 1 + src/languages/pl.ts | 1 + src/languages/pt-BR.ts | 1 + src/languages/zh-hans.ts | 1 + src/styles/index.ts | 8 +++ 15 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx create mode 100644 src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 876527f53e85..afca14802c96 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -7980,6 +7980,7 @@ const CONST = { HTML_RENDERER: { IMAGE: 'HTMLRenderer-Image', PRE: 'HTMLRenderer-Pre', + VICTORY_CHART_EXPAND_BUTTON: 'HTMLRenderer-VictoryChartExpandButton', }, RECEIPT: { IMAGE: 'Receipt-Image', diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx index 7b1545a31f52..fd8cf20da183 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx @@ -1,9 +1,12 @@ -import React from 'react'; +import React, {useState} from 'react'; +import {View} from 'react-native'; import {ChartFontsProvider} from '@components/Charts/hooks'; import useChartFonts from '@components/Charts/hooks/useChartFonts'; import Log from '@libs/Log'; import VictoryChartContainer from './components/VictoryChartContainer'; import VictoryChartContent from './components/VictoryChartContent'; +import VictoryChartExpandButton from './components/VictoryChartExpandButton'; +import VictoryChartExpandModal from './components/VictoryChartExpandModal'; import {VictoryChartProvider} from './context/VictoryChartContext'; import processVictoryChartTree from './parsers/processVictoryChartTree'; import type {VictoryChartRendererProps} from './types'; @@ -11,6 +14,7 @@ import resolveVictoryChartType from './utils/resolveVictoryChartType'; function BaseVictoryChartRenderer({tnode}: VictoryChartRendererProps) { const fonts = useChartFonts(); + const [isExpanded, setIsExpanded] = useState(false); let processedResult; try { @@ -34,9 +38,17 @@ function BaseVictoryChartRenderer({tnode}: VictoryChartRendererProps) { processedResult={processedResult} type={type} > - - - + {/* Wrapper anchors the absolutely-positioned expand button to the chart's corner. */} + + + + + setIsExpanded(true)} /> + + setIsExpanded(false)} + /> ); diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx new file mode 100644 index 000000000000..8864949d4e8d --- /dev/null +++ b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import Icon from '@components/Icon'; +import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; +import Tooltip from '@components/Tooltip'; +import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; +import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; + +type VictoryChartExpandButtonProps = { + /** Called when the user presses the expand button */ + onPress: () => void; +}; + +function VictoryChartExpandButton({onPress}: VictoryChartExpandButtonProps) { + const styles = useThemeStyles(); + const theme = useTheme(); + const {translate} = useLocalize(); + const icons = useMemoizedLazyExpensifyIcons(['Expand']); + + return ( + + + + + + ); +} + +VictoryChartExpandButton.displayName = 'VictoryChartExpandButton'; + +export default VictoryChartExpandButton; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx new file mode 100644 index 000000000000..d9e0d303bf95 --- /dev/null +++ b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import {View} from 'react-native'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Modal from '@components/Modal'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; +import VictoryChartContent from './VictoryChartContent'; + +type VictoryChartExpandModalProps = { + /** Whether the modal is visible */ + isVisible: boolean; + + /** Called when the modal should close */ + onClose: () => void; +}; + +/** + * Centered full-screen modal that re-renders the current chart at full viewport width. + * Must be rendered inside a VictoryChartProvider so VictoryChartContent can read the parsed chart context. + */ +function VictoryChartExpandModal({isVisible, onClose}: VictoryChartExpandModalProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + return ( + + + {/* No explicitSize, so the chart renders fluidly and fills the modal viewport width. */} + + + + + ); +} + +VictoryChartExpandModal.displayName = 'VictoryChartExpandModal'; + +export default VictoryChartExpandModal; diff --git a/src/languages/de.ts b/src/languages/de.ts index be7b8650872e..6fc789e3a1be 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -422,6 +422,7 @@ const translations: TranslationDeepObject = { print: 'Drucken', help: 'Hilfe', collapsed: 'Eingeklappt', + expand: 'Erweitern', expanded: 'Ausgeklappt', expenseReport: 'Spesenabrechnung', rateOutOfPolicy: 'Satz außerhalb der Richtlinie', diff --git a/src/languages/en.ts b/src/languages/en.ts index 26ae1454b7e2..83b57bc2a96c 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -446,6 +446,7 @@ const translations = { print: 'Print', help: 'Help', collapsed: 'Collapsed', + expand: 'Expand', expanded: 'Expanded', expenseReport: 'Expense Report', // @context Rate as a noun, not a verb diff --git a/src/languages/es.ts b/src/languages/es.ts index 11b92e9a72c7..cb073a29acb2 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -376,6 +376,7 @@ const translations: TranslationDeepObject = { print: 'Imprimir', help: 'Ayuda', collapsed: 'Contraído', + expand: 'Expandir', expanded: 'Expandido', expenseReport: 'Informe de Gastos', rateOutOfPolicy: 'Tasa fuera de póliza', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index f03a28b7e9f9..c67e5592bbbc 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -422,6 +422,7 @@ const translations: TranslationDeepObject = { print: 'Imprimer', help: 'Aide', collapsed: 'Réduit', + expand: 'Agrandir', expanded: 'Développé', expenseReport: 'Note de frais', rateOutOfPolicy: 'Taux hors politique', diff --git a/src/languages/it.ts b/src/languages/it.ts index dc8d0857f0ef..b27816eb1dc9 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -422,6 +422,7 @@ const translations: TranslationDeepObject = { print: 'Stampa', help: 'Aiuto', collapsed: 'Comprresso', + expand: 'Espandi', expanded: 'Espanso', expenseReport: 'Nota spese', rateOutOfPolicy: 'Tariffa fuori dalla policy', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 8c9c16dc9019..e3af0da13a9a 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -421,6 +421,7 @@ const translations: TranslationDeepObject = { print: '印刷', help: 'ヘルプ', collapsed: '折りたたみ', + expand: '展開', expanded: '展開', expenseReport: '経費精算書', rateOutOfPolicy: 'ポリシー外の料率', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index cdb5a2677d4f..cad198d5bcc1 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -421,6 +421,7 @@ const translations: TranslationDeepObject = { print: 'Afdrukken', help: 'Help', collapsed: 'Ingeklapt', + expand: 'Uitklappen', expanded: 'Uitgeklapt', expenseReport: 'Declaratie', rateOutOfPolicy: 'Tarief buiten beleid', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 43c68a1bf970..f8c7dc484e23 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -421,6 +421,7 @@ const translations: TranslationDeepObject = { print: 'Drukuj', help: 'Pomoc', collapsed: 'Zwinięte', + expand: 'Rozwiń', expanded: 'Rozwinięte', expenseReport: 'Raport wydatków', rateOutOfPolicy: 'Stawka poza zasadami', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 99b4aeeb40c1..5465dfc1589f 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -421,6 +421,7 @@ const translations: TranslationDeepObject = { print: 'Imprimir', help: 'Ajuda', collapsed: 'Recolhido', + expand: 'Expandir', expanded: 'Expandido', expenseReport: 'Relatório de despesas', rateOutOfPolicy: 'Tarifa fora da política', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index da0426c53ace..e4b6d0c6fc66 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -421,6 +421,7 @@ const translations: TranslationDeepObject = { print: '打印', help: '帮助', collapsed: '已折叠', + expand: '展开', expanded: '已展开', expenseReport: '报销报告', rateOutOfPolicy: '超出政策的费率', diff --git a/src/styles/index.ts b/src/styles/index.ts index 2d434a7a6f2c..1e948e5fae71 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -6148,6 +6148,14 @@ const staticStyles = (theme: ThemeColors) => chartContainer: { borderRadius: variables.componentBorderRadiusLarge, }, + chartExpandButton: { + backgroundColor: theme.buttonDefaultBG, + borderRadius: variables.componentBorderRadiusSmall, + padding: 4, + }, + chartExpandButtonHovered: { + backgroundColor: theme.buttonHoveredBG, + }, chartContent: { height: CHART_CONTENT_MIN_HEIGHT, }, From a01e219c157a13ab83130b5d8981165bcb2ccd5c Mon Sep 17 00:00:00 2001 From: Faizan Shoukat Abbasi Date: Sat, 4 Jul 2026 02:48:39 +0500 Subject: [PATCH 02/10] fix: scale expanded chart to fit modal and constrain inline chart wrapper width --- .../BaseVictoryChartRenderer.tsx | 8 ++- .../components/VictoryChartExpandModal.tsx | 51 ++++++++++++++++--- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx index 98b74aa6d48a..f51b88252277 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx @@ -3,6 +3,7 @@ import {View} from 'react-native'; import {ChartFontsProvider} from '@components/Charts/hooks'; import useChartFonts from '@components/Charts/hooks/useChartFonts'; import getVictoryChartTreeTypeface from '@components/Charts/utils/getVictoryChartTreeTypeface'; +import useThemeStyles from '@hooks/useThemeStyles'; import Log from '@libs/Log'; import VictoryChartContainer from './components/VictoryChartContainer'; import VictoryChartContent from './components/VictoryChartContent'; @@ -15,6 +16,7 @@ import resolveVictoryChartType from './utils/resolveVictoryChartType'; function BaseVictoryChartRenderer({tnode}: VictoryChartRendererProps) { const fonts = useChartFonts(); + const styles = useThemeStyles(); const [isExpanded, setIsExpanded] = useState(false); let processedResult; @@ -39,8 +41,10 @@ function BaseVictoryChartRenderer({tnode}: VictoryChartRendererProps) { processedResult={processedResult} type={type} > - {/* Wrapper anchors the absolutely-positioned expand button to the chart's corner. */} - + {/* Wrapper anchors the absolutely-positioned expand button to the chart's corner. + mw100 keeps it from sizing to the chart's design width, so the responsive + container's onLayout measures the real available width (e.g. in the side panel). */} + diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx index d9e0d303bf95..0d8c8c591d05 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx @@ -1,6 +1,8 @@ -import React from 'react'; +import React, {useState} from 'react'; +import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {useVictoryChartContext} from '@components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/context/VictoryChartContext'; import Modal from '@components/Modal'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -16,19 +18,41 @@ type VictoryChartExpandModalProps = { }; /** - * Centered full-screen modal that re-renders the current chart at full viewport width. + * Centered full-screen modal that re-renders the current chart scaled up to the viewport. * Must be rendered inside a VictoryChartProvider so VictoryChartContent can read the parsed chart context. + * + * The chart is rendered at its design size and uniformly transform-scaled to fit the modal — + * the same technique the inline scaled container uses to shrink charts. This keeps the canvas + * and the absolutely-positioned label/legend overlays (whose coordinates are design-based) + * perfectly aligned, so the expanded chart looks identical to the inline one, only larger. + * Rendering fluidly instead would resize only the canvas and leave labels at design coordinates, + * misplacing them (and potentially overlaying the header, blocking the back button). */ function VictoryChartExpandModal({isVisible, onClose}: VictoryChartExpandModalProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); + const {chartContentStyles} = useVictoryChartContext(); + const [availableSize, setAvailableSize] = useState({width: 0, height: 0}); + + const onContainerLayout = (event: LayoutChangeEvent) => { + const {width, height} = event.nativeEvent.layout; + // Avoid re-render churn when the layout callback fires without an actual size change. + setAvailableSize((prev) => (prev.width === width && prev.height === height ? prev : {width, height})); + }; + + const designWidth = typeof chartContentStyles.width === 'number' ? chartContentStyles.width : undefined; + const designHeight = typeof chartContentStyles.height === 'number' ? chartContentStyles.height : undefined; + const hasDesignDimensions = !!designWidth && !!designHeight; + const isMeasured = availableSize.width > 0 && availableSize.height > 0; + + // Uniform scale that fits the chart's design box inside the available modal area (may be > 1). + const scale = hasDesignDimensions && isMeasured ? Math.min(availableSize.width / designWidth, availableSize.height / designHeight) : 1; return ( - {/* No explicitSize, so the chart renders fluidly and fills the modal viewport width. */} - - + + {isMeasured && + (hasDesignDimensions ? ( + + {/* Fixed design-size box so the fluid chart renders at design size, then scaled uniformly. */} + + + + + ) : ( + // Charts without design dimensions have no design-based label coordinates, so fluid rendering is safe. + + + + ))} ); From 71565b95a55c767b677590f90ae957326c96cc0d Mon Sep 17 00:00:00 2001 From: Faizan Shoukat Abbasi Date: Sat, 4 Jul 2026 03:02:04 +0500 Subject: [PATCH 03/10] fixed FMT issues --- .../components/VictoryChartExpandButton.tsx | 5 ++++- .../components/VictoryChartExpandModal.tsx | 11 ++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx index 8864949d4e8d..81f3554c0540 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx @@ -1,13 +1,16 @@ -import React from 'react'; import Icon from '@components/Icon'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import Tooltip from '@components/Tooltip'; + import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; + import CONST from '@src/CONST'; +import React from 'react'; + type VictoryChartExpandButtonProps = { /** Called when the user presses the expand button */ onPress: () => void; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx index 0d8c8c591d05..cbd182c3b413 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandModal.tsx @@ -1,12 +1,17 @@ -import React, {useState} from 'react'; -import type {LayoutChangeEvent} from 'react-native'; -import {View} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import {useVictoryChartContext} from '@components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/context/VictoryChartContext'; import Modal from '@components/Modal'; + import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; + import CONST from '@src/CONST'; + +import type {LayoutChangeEvent} from 'react-native'; + +import React, {useState} from 'react'; +import {View} from 'react-native'; + import VictoryChartContent from './VictoryChartContent'; type VictoryChartExpandModalProps = { From 45f1e90e99906b811d57f3f35990e8f62958f7e3 Mon Sep 17 00:00:00 2001 From: Faizan Shoukat Abbasi Date: Mon, 6 Jul 2026 20:52:01 +0500 Subject: [PATCH 04/10] fix: use standard icon button for chart expand and address review feedback --- .../components/VictoryChartExpandButton.tsx | 34 +++++++------------ .../components/VictoryChartExpandModal.tsx | 23 +++++++++++-- src/styles/index.ts | 9 ++--- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx index 81f3554c0540..6f4ecb840a88 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/components/VictoryChartExpandButton.tsx @@ -1,5 +1,4 @@ -import Icon from '@components/Icon'; -import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; +import Button from '@components/Button'; import Tooltip from '@components/Tooltip'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; @@ -10,6 +9,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; import React from 'react'; +import {View} from 'react-native'; type VictoryChartExpandButtonProps = { /** Called when the user presses the expand button */ @@ -24,27 +24,19 @@ function VictoryChartExpandButton({onPress}: VictoryChartExpandButtonProps) { return ( - - + ); From 159724af2facb2f5fc9e6029c812fabb7ed1e770 Mon Sep 17 00:00:00 2001 From: Faizan Shoukat Abbasi Date: Tue, 7 Jul 2026 02:30:37 +0500 Subject: [PATCH 06/10] implement hover button --- .../BaseVictoryChartRenderer.tsx | 16 ++++++++++++++-- .../components/VictoryChartExpandButton.tsx | 7 +++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx index dff11506b149..00d1972745ed 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/VictoryChartRenderer/BaseVictoryChartRenderer.tsx @@ -2,8 +2,10 @@ import {ChartFontsProvider} from '@components/Charts/hooks'; import useChartFonts from '@components/Charts/hooks/useChartFonts'; import getVictoryChartTreeTypeface from '@components/Charts/utils/getVictoryChartTreeTypeface'; +import useHover from '@hooks/useHover'; import useThemeStyles from '@hooks/useThemeStyles'; +import {hasHoverSupport} from '@libs/DeviceCapabilities'; import Log from '@libs/Log'; import React, {useState} from 'react'; @@ -23,6 +25,8 @@ function BaseVictoryChartRenderer({tnode}: VictoryChartRendererProps) { const fonts = useChartFonts(); const styles = useThemeStyles(); const [isExpanded, setIsExpanded] = useState(false); + const {hovered, bind: hoverBind} = useHover(); + const deviceHasHoverSupport = hasHoverSupport(); let processedResult; try { @@ -49,11 +53,19 @@ function BaseVictoryChartRenderer({tnode}: VictoryChartRendererProps) { {/* Wrapper anchors the absolutely-positioned expand button to the chart's corner. mw100 keeps it from sizing to the chart's design width, so the responsive container's onLayout measures the real available width (e.g. in the side panel). */} - + - setIsExpanded(true)} /> + {/* Shown on hover only (like receipt actions) on devices with hover support; always shown on touch devices. */} + setIsExpanded(true)} + shouldShow={hovered || !deviceHasHoverSupport} + /> void; + + /** Whether the button should be visible. It stays mounted (hidden via opacity) so hover in/out doesn't reflow the chart. */ + shouldShow: boolean; }; -function VictoryChartExpandButton({onPress}: VictoryChartExpandButtonProps) { +function VictoryChartExpandButton({onPress, shouldShow}: VictoryChartExpandButtonProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const icons = useMemoizedLazyExpensifyIcons(['Expand']); @@ -25,7 +28,7 @@ function VictoryChartExpandButton({onPress}: VictoryChartExpandButtonProps) { {/* Overlays the chart's top-right corner without affecting the chart's layout. The button intentionally captures pointer events in that corner, suppressing any underlying chart gesture layer — acceptable since charts render nothing interactive there. */} - +