Skip to content

Commit acb6240

Browse files
committed
Add isTouchDevice state and touch-device specific zoom instructions
1 parent b371870 commit acb6240

3 files changed

Lines changed: 48 additions & 24 deletions

File tree

assets/js/dashboard/components/graph-tooltip.tsx

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { ReactNode, useLayoutEffect, useRef, useState } from 'react'
2+
import { Transition } from '@headlessui/react'
23

34
export const GraphTooltipWrapper = ({
45
x,
@@ -9,7 +10,8 @@ export const GraphTooltipWrapper = ({
910
totalBuckets,
1011
children,
1112
className,
12-
onClick
13+
onClick,
14+
isTouchDevice
1315
}: {
1416
x: number
1517
y: number
@@ -20,6 +22,7 @@ export const GraphTooltipWrapper = ({
2022
children: ReactNode
2123
className?: string
2224
onClick?: () => void
25+
isTouchDevice?: boolean
2326
}) => {
2427
const ref = useRef<HTMLDivElement>(null)
2528
// distance from cursor to tooltip edge
@@ -65,18 +68,29 @@ export const GraphTooltipWrapper = ({
6568
}, [x, maxX, bucketIndex])
6669

6770
return (
68-
<div
69-
ref={ref}
70-
className={className}
71-
onClick={onClick}
72-
style={{
73-
minWidth,
74-
left: tooltipLeft,
75-
top: y,
76-
transform: `translateY(-100%) translateY(-${offsetFromCursor}px)`
77-
}}
71+
<Transition
72+
as={React.Fragment}
73+
appear
74+
show
75+
// enter delay on mobile is needed to prevent the tooltip from entering when the user starts to y-pan
76+
// but the y-pan is not yet certain
77+
enter={isTouchDevice ? 'transition-opacity duration-0 delay-150' : ''}
78+
enterFrom={isTouchDevice ? 'opacity-0' : ''}
79+
enterTo={isTouchDevice ? 'opacity-100' : ''}
7880
>
79-
{children}
80-
</div>
81+
<div
82+
ref={ref}
83+
className={className}
84+
onClick={onClick}
85+
style={{
86+
minWidth,
87+
left: tooltipLeft,
88+
top: y,
89+
transform: `translateY(-100%) translateY(-${offsetFromCursor}px)`
90+
}}
91+
>
92+
{children}
93+
</div>
94+
</Transition>
8195
)
8296
}

assets/js/dashboard/stats/graph/main-graph.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export const MainGraph = ({
6262
const { mode } = useTheme()
6363
const navigate = useAppNavigate()
6464
const { primaryGradient, secondaryGradient } = paletteByTheme[mode]
65+
const [isTouchDevice, setIsTouchDevice] = useState(false)
6566
const [tooltip, setTooltip] = useState<{
6667
x: number
6768
y: number
@@ -190,7 +191,10 @@ export const MainGraph = ({
190191
)
191192

192193
const onPointerMove = useCallback<PointerHandler>(
193-
({ inHoverableArea, closestIndex, x, y }) => {
194+
({ inHoverableArea, closestIndex, x, y, event }) => {
195+
if (event instanceof PointerEvent) {
196+
setIsTouchDevice(event.pointerType === 'touch')
197+
}
194198
if (!inHoverableArea) {
195199
setTooltip({ selectedIndex: null, x: 0, y: 0 })
196200
} else {
@@ -265,6 +269,7 @@ export const MainGraph = ({
265269
datum={remappedData[selectedIndex]}
266270
bucketIndex={selectedIndex}
267271
totalBuckets={remappedData.length}
272+
isTouchDevice={isTouchDevice}
268273
/>
269274
)}
270275
</Graph>
@@ -283,7 +288,8 @@ const MainGraphTooltip = ({
283288
datum,
284289
showZoomToPeriod,
285290
bucketIndex,
286-
totalBuckets
291+
totalBuckets,
292+
isTouchDevice
287293
}: {
288294
metric: FormattableMetric
289295
interval: string
@@ -297,8 +303,10 @@ const MainGraphTooltip = ({
297303
bucketIndex: number
298304
totalBuckets: number
299305
maxX: number
306+
isTouchDevice: boolean
300307
}) => {
301308
const formatter = MetricFormatterShort[metric]
309+
302310
return (
303311
<GraphTooltipWrapper
304312
x={x}
@@ -307,6 +315,7 @@ const MainGraphTooltip = ({
307315
maxX={maxX}
308316
bucketIndex={bucketIndex}
309317
totalBuckets={totalBuckets}
318+
isTouchDevice={isTouchDevice}
310319
className={
311320
'absolute select-none pointer-events-none bg-gray-800 dark:bg-gray-950 py-3 px-4 rounded-md z-[100] shadow shadow-gray-200 dark:shadow-gray-850'
312321
}
@@ -378,7 +387,9 @@ const MainGraphTooltip = ({
378387
<>
379388
<hr className="border-gray-600 dark:border-gray-800 my-1" />
380389
<span className="text-gray-300 dark:text-gray-400 text-xs">
381-
Click to view {interval}
390+
{isTouchDevice
391+
? `Release to view ${interval}`
392+
: `Click to view ${interval}`}
382393
</span>
383394
</>
384395
)}
@@ -610,7 +621,7 @@ const remapAndFillData = (
610621
if (isPartial) {
611622
startOfLastPartialSlice = index
612623
} else {
613-
// if there is a full period after a partial slice,
624+
// if there is a full period after a partial slice,
614625
// it's not a partial slice anchored at the end of the series
615626
startOfLastPartialSlice = null
616627
}

assets/js/dashboard/stats/graph/visitor-graph.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,11 @@ export default function VisitorGraph({
140140
] as const,
141141
queryFn: async ({ queryKey }) => {
142142
const [_, opts] = queryKey
143-
const oldDataSource =
144-
window.location.hostname === 'localhost'
145-
? 'http://localhost:8000'
146-
: window.location.hostname.match(/pr-\d+\.review\.plausible\.io/)
147-
? 'https://staging.plausible.io'
148-
: ''
143+
const oldDataSource = window.location.hostname.match(
144+
/pr-\d+\.review\.plausible\.io/
145+
)
146+
? 'https://staging.plausible.io'
147+
: `http://${window.location.hostname}:8000`
149148
const [dataOld, dataNew] = await Promise.all([
150149
api
151150
.get(
@@ -157,7 +156,7 @@ export default function VisitorGraph({
157156
}
158157
)
159158
.then((res) => ({ ...res, interval: opts.interval }))
160-
.catch(() => undefined),
159+
.catch(console.error),
161160
fetchMainGraph(site, opts.dashboardState, opts.metric, opts.interval)
162161
.then((res) => ({
163162
...res,

0 commit comments

Comments
 (0)