Skip to content

Commit

Permalink
feat: improved inspector positioning (#21736)
Browse files Browse the repository at this point in the history
  • Loading branch information
daibhin authored Apr 24, 2024
1 parent 212431e commit 4d1e602
Show file tree
Hide file tree
Showing 16 changed files with 224 additions and 196 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 5 additions & 6 deletions frontend/src/scenes/session-recordings/player/PlayerMeta.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import './PlayerMeta.scss'

import { IconEllipsis, IconTrash } from '@posthog/icons'
import { IconDownload, IconMagic, IconSearch } from '@posthog/icons'
import { IconDownload, IconEllipsis, IconMagic, IconSearch, IconTrash } from '@posthog/icons'
import { LemonButton, LemonDialog, LemonMenu, LemonMenuItems, Link } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'
Expand Down Expand Up @@ -68,7 +67,7 @@ function URLOrScreen({ lastUrl }: { lastUrl: string | undefined }): JSX.Element
)
}

export function PlayerMeta(): JSX.Element {
export function PlayerMeta({ linkIconsOnly = false }: { linkIconsOnly?: boolean }): JSX.Element {
const { sessionRecordingId, logicProps, isFullScreen } = useValues(sessionRecordingPlayerLogic)

const {
Expand Down Expand Up @@ -181,10 +180,10 @@ export function PlayerMeta(): JSX.Element {
</div>

{sessionRecordingId && (
<>
<PlayerMetaLinks />
<div className="flex items-center gap-0.5">
<PlayerMetaLinks iconsOnly={linkIconsOnly} />
{mode === SessionRecordingPlayerMode.Standard && <MenuActions />}
</>
</div>
)}
</div>
<div
Expand Down
41 changes: 25 additions & 16 deletions frontend/src/scenes/session-recordings/player/PlayerMetaLinks.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { IconNotebook, IconPin, IconPinFilled } from '@posthog/icons'
import { IconNotebook, IconPin, IconPinFilled, IconShare } from '@posthog/icons'
import { useActions, useValues } from 'kea'
import { FEATURE_FLAGS } from 'lib/constants'
import { IconComment, IconLink } from 'lib/lemon-ui/icons'
import { IconComment } from 'lib/lemon-ui/icons'
import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { Fragment } from 'react'
import { useNotebookNode } from 'scenes/notebooks/Nodes/NotebookNodeContext'
import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton'
import {
Expand All @@ -18,7 +19,13 @@ import { NotebookNodeType } from '~/types'
import { sessionPlayerModalLogic } from './modal/sessionPlayerModalLogic'
import { PlaylistPopoverButton } from './playlist-popover/PlaylistPopover'

function PinToPlaylistButton(): JSX.Element {
function PinToPlaylistButton({
buttonContent,
...buttonProps
}: {
buttonContent: (label: string) => JSX.Element
buttonProps?: Partial<LemonButtonProps>
}): JSX.Element {
const { logicProps } = useValues(sessionRecordingPlayerLogic)
const { maybePersistRecording } = useActions(sessionRecordingPlayerLogic)
const nodeLogic = useNotebookNode()
Expand All @@ -34,6 +41,7 @@ function PinToPlaylistButton(): JSX.Element {

return logicProps.setPinned ? (
<LemonButton
{...buttonProps}
onClick={() => {
if (nodeLogic && !logicProps.pinned) {
// If we are in a node, then pinning should persist the recording
Expand All @@ -42,19 +50,16 @@ function PinToPlaylistButton(): JSX.Element {

logicProps.setPinned?.(!logicProps.pinned)
}}
size="small"
tooltip={tooltip}
data-attr={logicProps.pinned ? 'unpin-from-this-list' : 'pin-to-this-list'}
icon={logicProps.pinned ? <IconPinFilled /> : <IconPin />}
/>
) : (
<PlaylistPopoverButton size="small">
<span>{description}</span>
</PlaylistPopoverButton>
<PlaylistPopoverButton {...buttonProps}>{buttonContent(description)}</PlaylistPopoverButton>
)
}

export function PlayerMetaLinks(): JSX.Element {
export function PlayerMetaLinks({ iconsOnly }: { iconsOnly: boolean }): JSX.Element {
const { sessionRecordingId, logicProps } = useValues(sessionRecordingPlayerLogic)
const { setPause, setIsFullScreen } = useActions(sessionRecordingPlayerLogic)
const nodeLogic = useNotebookNode()
Expand All @@ -79,14 +84,18 @@ export function PlayerMetaLinks(): JSX.Element {
size: 'small',
}

const buttonContent = (label: string): JSX.Element => {
return !iconsOnly ? <span>{label}</span> : <Fragment />
}

const mode = logicProps.mode ?? SessionRecordingPlayerMode.Standard

return (
<div className="flex flex-row gap-1 items-center justify-end">
<>
{![SessionRecordingPlayerMode.Sharing].includes(mode) ? (
<>
<NotebookSelectButton
size="small"
{...commonProps}
icon={<IconComment />}
resource={{
type: NotebookNodeType.Recording,
Expand All @@ -111,17 +120,17 @@ export function PlayerMetaLinks(): JSX.Element {
personsModalLogic.findMounted()?.actions.closeModal()
}}
>
Comment
{buttonContent('Comment')}
</NotebookSelectButton>

<LemonButton icon={<IconLink />} onClick={onShare} {...commonProps}>
<span>Share</span>
<LemonButton icon={<IconShare />} onClick={onShare} {...commonProps}>
{buttonContent('Share')}
</LemonButton>

{nodeLogic?.props.nodeType === NotebookNodeType.RecordingPlaylist ? (
<LemonButton
{...commonProps}
icon={<IconNotebook />}
size="small"
onClick={() => {
nodeLogic.actions.insertAfter({
type: NotebookNodeType.Recording,
Expand All @@ -131,9 +140,9 @@ export function PlayerMetaLinks(): JSX.Element {
/>
) : null}

<PinToPlaylistButton />
<PinToPlaylistButton buttonContent={buttonContent} {...commonProps} />
</>
) : null}
</div>
</>
)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
@import '../../../styles/mixins';

.SessionRecordingPlayer {
--inspector-min-width: 24rem;

position: relative;
display: flex;
flex-direction: row;
Expand All @@ -29,7 +27,6 @@

.SessionRecordingPlayer__main {
flex: 1;
padding-right: 2.5rem;
}

&--fullscreen {
Expand Down Expand Up @@ -62,46 +59,24 @@
}

.SessionRecordingPlayer__inspector {
position: absolute;
top: 0;
right: 0;
bottom: 0;
z-index: 10;
position: relative;
flex-shrink: 0;
min-width: var(--inspector-min-width);
max-width: 95%;

&--collapsed {
--inspector-min-width: 2.5rem;
}

.PlayerInspectorPreview {
position: absolute;
inset: 0;
z-index: 1;
cursor: pointer;
transition: opacity 0.2s ease-in-out;
}
min-width: 20rem;
max-width: 50%;
}

&--widescreen {
.SessionRecordingPlayer__main {
padding-right: 0;
}

&--wide {
.SessionRecordingPlayer__inspector {
position: relative;
max-width: 75%;
min-width: 26rem;
}
}

&--inspector-hidden {
.SessionRecordingPlayer__main {
padding-right: 0;
}
&--stacked-vertically {
flex-direction: column;

.SessionRecordingPlayer__inspector {
display: none;
min-width: 100%;
max-width: 100%;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { HotkeysInterface, useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotke
import { usePageVisibility } from 'lib/hooks/usePageVisibility'
import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver'
import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useMemo, useRef, useState } from 'react'
import { useNotebookDrag } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook'
import { PlayerController } from 'scenes/session-recordings/player/controller/PlayerController'
import { PlayerInspector } from 'scenes/session-recordings/player/inspector/PlayerInspector'
Expand Down Expand Up @@ -35,6 +35,11 @@ export interface SessionRecordingPlayerProps extends SessionRecordingPlayerLogic
matchingEventsMatchType?: MatchingEventsMatchType
}

enum InspectorStacking {
Vertical = 'vertical',
Horizontal = 'horizontal',
}

export const createPlaybackSpeedKey = (action: (val: number) => void): HotkeysInterface => {
return PLAYBACK_SPEEDS.map((x, i) => ({ key: `${i}`, value: x })).reduce(
(acc, x) => ({ ...acc, [x.key]: { action: () => action(x.value) } }),
Expand All @@ -59,6 +64,7 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX.
} = props

const playerRef = useRef<HTMLDivElement>(null)
const playerMainRef = useRef<HTMLDivElement>(null)

const logicProps: SessionRecordingPlayerLogicProps = {
sessionRecordingId,
Expand Down Expand Up @@ -130,26 +136,34 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX.

const { size } = useResizeBreakpoints(
{
0: 'tiny',
400: 'small',
1000: 'medium',
0: 'small',
1050: 'medium',
1500: 'wide',
},
{
ref: playerRef,
}
)
const { size: playerMainSize } = useResizeBreakpoints(
{
0: 'small',
650: 'medium',
},
{
ref: playerMainRef,
}
)

const isWidescreen = !isFullScreen && size === 'medium'
const isWidescreen = !isFullScreen && size === 'wide'

const [inspectorExpanded, setInspectorExpanded] = useState(isWidescreen)
const [preferredInspectorStacking, setPreferredInspectorStacking] = useState(InspectorStacking.Horizontal)

const { draggable, elementProps } = useNotebookDrag({ href: urls.replaySingle(sessionRecordingId) })
const compactLayout = size === 'small'
const layoutStacking = compactLayout ? InspectorStacking.Vertical : preferredInspectorStacking
const isVerticallyStacked = layoutStacking === InspectorStacking.Vertical

useEffect(() => {
if (isWidescreen) {
setInspectorExpanded(true)
}
}, [isWidescreen])
const { draggable, elementProps } = useNotebookDrag({ href: urls.replaySingle(sessionRecordingId) })

if (isNotFound) {
return (
Expand All @@ -163,42 +177,52 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX.
<BindLogic logic={sessionRecordingPlayerLogic} props={logicProps}>
<div
ref={playerRef}
className={clsx('SessionRecordingPlayer', {
'SessionRecordingPlayer--fullscreen': isFullScreen,
'SessionRecordingPlayer--no-border': noBorder,
'SessionRecordingPlayer--widescreen': isWidescreen,
'SessionRecordingPlayer--inspector-focus': inspectorExpanded || isWidescreen,
'SessionRecordingPlayer--inspector-hidden': noInspector || size === 'tiny',
'SessionRecordingPlayer--buffering': isBuffering,
})}
className={clsx(
'SessionRecordingPlayer',
{
'SessionRecordingPlayer--fullscreen': isFullScreen,
'SessionRecordingPlayer--no-border': noBorder,
'SessionRecordingPlayer--buffering': isBuffering,
'SessionRecordingPlayer--stacked-vertically': isVerticallyStacked,
},
`SessionRecordingPlayer--${size}`
)}
onClick={incrementClickCount}
>
<FloatingContainerContext.Provider value={playerRef}>
{explorerMode ? (
<SessionRecordingPlayerExplorer {...explorerMode} onClose={() => closeExplorer()} />
) : (
<>
<div
className="SessionRecordingPlayer__main"
onClick={() => {
if (!isWidescreen) {
setInspectorExpanded(false)
}
}}
>
{(!noMeta || isFullScreen) && size !== 'tiny' ? <PlayerMeta /> : null}
<div ref={playerMainRef} className="SessionRecordingPlayer__main">
{!noMeta || isFullScreen ? (
<PlayerMeta linkIconsOnly={playerMainSize === 'small'} />
) : null}

<div className="SessionRecordingPlayer__body" draggable={draggable} {...elementProps}>
<PlayerFrame />
<PlayerFrameOverlay />
</div>
<LemonDivider className="my-0" />
<PlayerController />
<PlayerController
inspectorExpanded={inspectorExpanded}
toggleInspectorExpanded={() => setInspectorExpanded(!inspectorExpanded)}
/>
</div>
{!noInspector && (
{!noInspector && inspectorExpanded && (
<PlayerInspector
inspectorExpanded={inspectorExpanded}
setInspectorExpanded={setInspectorExpanded}
onClose={setInspectorExpanded}
isVerticallyStacked={isVerticallyStacked}
toggleLayoutStacking={
compactLayout
? undefined
: () =>
setPreferredInspectorStacking(
preferredInspectorStacking === InspectorStacking.Vertical
? InspectorStacking.Horizontal
: InspectorStacking.Vertical
)
}
/>
)}
</>
Expand Down
Loading

0 comments on commit 4d1e602

Please sign in to comment.