Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improved inspector positioning #21736

Merged
merged 22 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0ff60e1
feat improved inspector positioning
daibhin Apr 17, 2024
8a3e5d1
collapsible user list
daibhin Apr 17, 2024
4a19691
Merge branch 'master' into dn-feat/improved-inspector-positioning
daibhin Apr 18, 2024
a42b199
inspector on right or bottom
daibhin Apr 18, 2024
8cd9527
Merge branch 'master' into dn-feat/improved-inspector-positioning
daibhin Apr 19, 2024
e08610a
reorder actions and inspector
daibhin Apr 19, 2024
ca3ff0a
reposition meta links
daibhin Apr 19, 2024
4567496
toggle icon
daibhin Apr 19, 2024
4294381
Merge branch 'master' into dn-feat/improved-inspector-positioning
daibhin Apr 19, 2024
52fb7e1
inspector sizing
daibhin Apr 22, 2024
ef004af
Merge branch 'master' into dn-feat/improved-inspector-positioning
daibhin Apr 22, 2024
0cef0b7
remove unused selector
daibhin Apr 22, 2024
5cc899f
swap buttons back and better button collapsing to icons detection
daibhin Apr 22, 2024
2a1ab35
personal review
daibhin Apr 22, 2024
d5d9b4a
Merge branch 'master' into dn-feat/improved-inspector-positioning
daibhin Apr 23, 2024
c85b3f8
Update UI snapshots for `chromium` (2)
github-actions[bot] Apr 23, 2024
72e7039
Merge branch 'master' into dn-feat/improved-inspector-positioning
daibhin Apr 23, 2024
d090517
Merge branch 'master' into dn-feat/improved-inspector-positioning
daibhin Apr 23, 2024
9c9b7f4
Merge branch 'master' into dn-feat/improved-inspector-positioning
daibhin Apr 23, 2024
142738d
Merge branch 'master' into dn-feat/improved-inspector-positioning
daibhin Apr 23, 2024
bc965a2
fix linting issue
daibhin Apr 23, 2024
d9f2ec8
Merge branch 'master' into dn-feat/improved-inspector-positioning
daibhin Apr 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 }: { 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
Loading