Skip to content

Commit

Permalink
feat: Small heatmap UI fixes (#21849)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite authored Apr 26, 2024
1 parent 9bd7618 commit 20bc766
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 13 deletions.
32 changes: 30 additions & 2 deletions frontend/src/toolbar/elements/Heatmap.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import heatmapsJs, { Heatmap as HeatmapJS } from 'heatmap.js'
import { useValues } from 'kea'
import { MutableRefObject, useCallback, useEffect, useRef } from 'react'
import { MutableRefObject, useCallback, useEffect, useMemo, useRef } from 'react'

import { heatmapLogic } from '~/toolbar/elements/heatmapLogic'

Expand Down Expand Up @@ -49,10 +49,26 @@ function HeatmapMouseInfo({
}

export function Heatmap(): JSX.Element | null {
const { heatmapJsData, heatmapEnabled, heatmapFilters, windowWidth, windowHeight } = useValues(heatmapLogic)
const { heatmapJsData, heatmapEnabled, heatmapFilters, windowWidth, windowHeight, heatmapColorPalette } =
useValues(heatmapLogic)
const heatmapsJsRef = useRef<HeatmapJS<'value', 'x', 'y'>>()
const heatmapsJsContainerRef = useRef<HTMLDivElement | null>()

const heatmapJSColorGradient = useMemo((): Record<string, string> => {
switch (heatmapColorPalette) {
case 'blue':
return { '.0': 'rgba(0, 0, 255, 0)', '.100': 'rgba(0, 0, 255, 1)' }
case 'green':
return { '.0': 'rgba(0, 255, 0, 0)', '.100': 'rgba(0, 255, 0, 1)' }
case 'red':
return { '.0': 'rgba(255, 0, 0, 0)', '.100': 'rgba(255, 0, 0, 1)' }

default:
// Defaults taken from heatmap.js
return { '.25': 'rgb(0,0,255)', '0.55': 'rgb(0,255,0)', '0.85': 'yellow', '1.0': 'rgb(255,0,0)' }
}
}, [heatmapColorPalette])

const updateHeatmapData = useCallback((): void => {
try {
heatmapsJsRef.current?.setData(heatmapJsData)
Expand All @@ -69,6 +85,7 @@ export function Heatmap(): JSX.Element | null {

heatmapsJsRef.current = heatmapsJs.create({
container,
gradient: heatmapJSColorGradient,
})

updateHeatmapData()
Expand All @@ -78,6 +95,17 @@ export function Heatmap(): JSX.Element | null {
updateHeatmapData()
}, [heatmapJsData])

useEffect(() => {
if (!heatmapsJsContainerRef.current) {
return
}

heatmapsJsRef.current?.configure({
container: heatmapsJsContainerRef.current,
gradient: heatmapJSColorGradient,
})
}, [heatmapJSColorGradient])

if (!heatmapEnabled || !heatmapFilters.enabled || heatmapFilters.type === 'scrolldepth') {
return null
}
Expand Down
46 changes: 37 additions & 9 deletions frontend/src/toolbar/elements/ScrollDepth.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import clsx from 'clsx'
import { useValues } from 'kea'

import { heatmapLogic } from '~/toolbar/elements/heatmapLogic'
Expand All @@ -7,7 +8,7 @@ import { useMousePosition } from './useMousePosition'

function ScrollDepthMouseInfo(): JSX.Element | null {
const { posthog } = useValues(toolbarConfigLogic)
const { heatmapElements, rawHeatmapLoading } = useValues(heatmapLogic)
const { heatmapElements, rawHeatmapLoading, shiftPressed } = useValues(heatmapLogic)

const { y: mouseY } = useMousePosition()

Expand Down Expand Up @@ -35,8 +36,13 @@ function ScrollDepthMouseInfo(): JSX.Element | null {
transform: 'translateY(-50%)',
}}
>
<div className="border-b w-full" />
<div className="bg-border whitespace-nowrap text-default rounded p-2 font-semibold">
<div className="border-b border-default w-full opacity-75" />
<div
className={clsx(
'bg-default whitespace-nowrap text-white rounded p-2 font-semibold opacity-75 hover:opacity-100 transition-all',
!shiftPressed ? 'pointer-events-auto' : 'pointer-events-none'
)}
>
{rawHeatmapLoading ? (
<>Loading...</>
) : heatmapElements.length ? (
Expand All @@ -46,15 +52,16 @@ function ScrollDepthMouseInfo(): JSX.Element | null {
)}
</div>

<div className="border-b w-10" />
<div className="border-b border-default w-10 opacity-75" />
</div>
)
}

export function ScrollDepth(): JSX.Element | null {
const { posthog } = useValues(toolbarConfigLogic)

const { heatmapEnabled, heatmapFilters, heatmapElements, scrollDepthPosthogJsError } = useValues(heatmapLogic)
const { heatmapEnabled, heatmapFilters, heatmapElements, scrollDepthPosthogJsError, heatmapColorPalette } =
useValues(heatmapLogic)

if (!heatmapEnabled || !heatmapFilters.enabled || heatmapFilters.type !== 'scrolldepth') {
return null
Expand All @@ -71,11 +78,32 @@ export function ScrollDepth(): JSX.Element | null {

function color(count: number): string {
const value = 1 - count / maxCount
const safeValue = Math.max(0, Math.min(1, value))
const hue = Math.round(260 * safeValue)

// Return hsl color. You can adjust saturation and lightness to your liking
return `hsl(${hue}, 100%, 50%)`
if (heatmapColorPalette === 'default') {
const safeValue = Math.max(0, Math.min(1, value))
const hue = Math.round(260 * safeValue)

// Return hsl color. You can adjust saturation and lightness to your liking
return `hsl(${hue}, 100%, 50%)`
}

const rgba = [0, 0, 0, count / maxCount]

switch (heatmapColorPalette) {
case 'red':
rgba[0] = 255
break
case 'green':
rgba[1] = 255
break
case 'blue':
rgba[2] = 255
break
default:
break
}

return `rgba(${rgba.join(', ')})`
}

return (
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/toolbar/elements/heatmapLogic.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LemonSelectOption } from '@posthog/lemon-ui'
import { actions, afterMount, beforeUnmount, connect, kea, listeners, path, reducers, selectors } from 'kea'
import { loaders } from 'kea-loaders'
import { encodeParams } from 'kea-router'
Expand Down Expand Up @@ -57,6 +58,13 @@ export type HeatmapJsData = {
}
export type HeatmapFixedPositionMode = 'fixed' | 'relative' | 'hidden'

export const HEATMAP_COLOR_PALETTE_OPTIONS: LemonSelectOption<string>[] = [
{ value: 'default', label: 'Default (multicolor)' },
{ value: 'red', label: 'Red (monocolor)' },
{ value: 'green', label: 'Green (monocolor)' },
{ value: 'blue', label: 'Blue (monocolor)' },
]

export const heatmapLogic = kea<heatmapLogicType>([
path(['toolbar', 'elements', 'heatmapLogic']),
connect({
Expand Down Expand Up @@ -86,6 +94,7 @@ export const heatmapLogic = kea<heatmapLogicType>([
fetchHeatmapApi: (params: HeatmapRequestType) => ({ params }),
setHeatmapScrollY: (scrollY: number) => ({ scrollY }),
setHeatmapFixedPositionMode: (mode: HeatmapFixedPositionMode) => ({ mode }),
setHeatmapColorPalette: (Palette: string | null) => ({ Palette }),
}),
windowValues(() => ({
windowWidth: (window: Window) => window.innerWidth,
Expand Down Expand Up @@ -153,6 +162,14 @@ export const heatmapLogic = kea<heatmapLogicType>([
setHeatmapFixedPositionMode: (_, { mode }) => mode,
},
],

heatmapColorPalette: [
'default' as string | null,
{ persist: true },
{
setHeatmapColorPalette: (_, { Palette }) => Palette,
},
],
}),

loaders(({ values }) => ({
Expand Down
15 changes: 13 additions & 2 deletions frontend/src/toolbar/stats/HeatmapToolbarMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IconInfo, IconMagicWand } from '@posthog/icons'
import { LemonLabel, LemonSegmentedButton, LemonTag } from '@posthog/lemon-ui'
import { LemonLabel, LemonSegmentedButton, LemonSelect, LemonTag } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { CUSTOM_OPTION_KEY } from 'lib/components/DateFilter/types'
import { IconSync } from 'lib/lemon-ui/icons'
Expand All @@ -15,7 +15,7 @@ import React, { useState } from 'react'

import { ToolbarMenu } from '~/toolbar/bar/ToolbarMenu'
import { elementsLogic } from '~/toolbar/elements/elementsLogic'
import { heatmapLogic } from '~/toolbar/elements/heatmapLogic'
import { HEATMAP_COLOR_PALETTE_OPTIONS, heatmapLogic } from '~/toolbar/elements/heatmapLogic'
import { currentPageLogic } from '~/toolbar/stats/currentPageLogic'

import { toolbarConfigLogic } from '../toolbarConfigLogic'
Expand Down Expand Up @@ -143,6 +143,7 @@ export const HeatmapToolbarMenu = (): JSX.Element => {
elementStatsLoading,
clickmapsEnabled,
heatmapFixedPositionMode,
heatmapColorPalette,
} = useValues(heatmapLogic)
const {
setCommonFilters,
Expand All @@ -151,6 +152,7 @@ export const HeatmapToolbarMenu = (): JSX.Element => {
setMatchLinksByHref,
toggleClickmapsEnabled,
setHeatmapFixedPositionMode,
setHeatmapColorPalette,
} = useActions(heatmapLogic)
const { setHighlightElement, setSelectedElement } = useActions(elementsLogic)

Expand Down Expand Up @@ -321,6 +323,15 @@ export const HeatmapToolbarMenu = (): JSX.Element => {
</div>
</SectionSetting>

<SectionSetting title="Color palette">
<LemonSelect
size="small"
options={HEATMAP_COLOR_PALETTE_OPTIONS}
value={heatmapColorPalette}
onChange={setHeatmapColorPalette}
/>
</SectionSetting>

{heatmapFilters.type !== 'scrolldepth' && (
<SectionSetting
title="Fixed positioning calculation"
Expand Down

0 comments on commit 20bc766

Please sign in to comment.