Skip to content

Commit

Permalink
[desk-tool] Add TimelinePopover component and update timeline CSS
Browse files Browse the repository at this point in the history
  • Loading branch information
mariuslundgard authored and rexxars committed Oct 6, 2020
1 parent f48279a commit 8451cdd
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 212 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import {Doc} from '../types'
import {Controller} from './history/controller'
import {Timeline} from './history/timeline'

type TimelineMode = 'since' | 'rev' | 'closed'

export interface HistoryContextInstance {
timeline: Timeline
historyController: Controller
displayed: Doc | null
open(): void
close(): void
setRange(since: string | null, rev: string | null): void
timelineMode: TimelineMode
setTimelineMode: (mode: TimelineMode) => void
}

export const DocumentHistoryContext = createContext<HistoryContextInstance | null>(null)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useObservable} from '@sanity/react-hooks'
import client from 'part:@sanity/base/client'
import React, {useMemo, useCallback} from 'react'
import React, {useCallback, useMemo, useState} from 'react'
import {usePaneRouter} from '../../../contexts/PaneRouterContext'
import {Doc} from '../types'
import {DocumentHistoryContext} from './context'
Expand All @@ -9,25 +9,22 @@ import {Timeline} from './history/timeline'

interface DocumentHistoryProviderProps {
children: React.ReactNode
draft: Doc | null
documentId: string
published: Doc | null
value: Doc | null
}

declare const __DEV__: boolean

export function DocumentHistoryProvider(props: DocumentHistoryProviderProps) {
const {children, documentId, value} = props

const paneRouter = usePaneRouter()

const timeline = useMemo(
() =>
new Timeline({
publishedId: props.documentId,
enableTrace: __DEV__
}),
[props.documentId]
)
const [timelineMode, setTimelineMode] = useState<'since' | 'rev' | 'closed'>('closed')

const timeline = useMemo(() => new Timeline({publishedId: documentId, enableTrace: __DEV__}), [
documentId
])

// note: this emits sync so can never be null
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
Expand All @@ -36,10 +33,10 @@ export function DocumentHistoryProvider(props: DocumentHistoryProviderProps) {
() =>
createObservableController({
timeline,
documentId: props.documentId,
documentId,
client
}),
[props.documentId]
[documentId, timeline]
)
)!

Expand All @@ -65,7 +62,7 @@ export function DocumentHistoryProvider(props: DocumentHistoryProviderProps) {
[paneRouter]
)

let displayed = props.value
let displayed = value

if (historyController.onOlderRevision()) {
displayed = historyController.displayed()
Expand All @@ -79,10 +76,12 @@ export function DocumentHistoryProvider(props: DocumentHistoryProviderProps) {
historyController,
setRange,
close,
open
open,
timelineMode,
setTimelineMode
}}
>
{props.children}
{children}
</DocumentHistoryContext.Provider>
)
}
212 changes: 80 additions & 132 deletions packages/@sanity/desk-tool/src/panes/documentPane/documentPane.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import {Chunk} from '@sanity/field/diff'
import * as PathUtils from '@sanity/util/paths'
import classNames from 'classnames'
import {ClickOutside} from 'part:@sanity/components/click-outside'
import {MenuItemGroupType} from 'part:@sanity/components/menus/default'
import {Popover} from 'part:@sanity/components/popover'
import Snackbar from 'part:@sanity/components/snackbar/default'
import React, {useCallback, useRef, useState} from 'react'
import {usePaneRouter} from '../../contexts/PaneRouterContext'
Expand All @@ -15,7 +13,7 @@ import {DocumentOperationResults} from './documentOperationResults'
import {InspectDialog} from './inspectDialog'
import {DocumentActionShortcuts, isInspectHotkey, isPreviewHotkey} from './keyboardShortcuts'
import {DocumentStatusBar} from './statusBar'
import {Timeline, sinceTimelineProps, revTimelineProps} from './timeline'
import {TimelinePopover} from './timeline'
import {Doc, DocumentViewType} from './types'

import styles from './documentPane.css'
Expand Down Expand Up @@ -69,11 +67,10 @@ export function DocumentPane(props: DocumentPaneProps) {
} = props
const rootRef = useRef<HTMLDivElement | null>(null)
const features = useDeskToolFeatures()
const {setRange, timeline, historyController} = useDocumentHistory()
const {historyController, setTimelineMode, timelineMode} = useDocumentHistory()
const historyState = historyController.selectionState
const [showValidationTooltip, setShowValidationTooltip] = useState<boolean>(false)
const paneRouter = usePaneRouter()
const [timelineMode, setTimelineMode] = useState<'since' | 'rev' | 'closed'>('closed')
const activeViewId = paneRouter.params.view || (views[0] && views[0].id)
const initialFocusPath = paneRouter.params.path
? PathUtils.fromString(paneRouter.params.path)
Expand Down Expand Up @@ -134,31 +131,6 @@ export function DocumentPane(props: DocumentPaneProps) {
const changesSinceSelectRef = useRef<HTMLDivElement | null>(null)
const versionSelectRef = useRef<HTMLDivElement | null>(null)

const selectRev = useCallback(
(revChunk: Chunk) => {
const [sinceId, revId] = historyController.findRangeForNewRev(revChunk)
setTimelineMode('closed')
setRange(sinceId, revId)
},
[historyController, setRange, setTimelineMode]
)

const selectSince = useCallback(
(sinceChunk: Chunk) => {
const [sinceId, revId] = historyController.findRangeForNewSince(sinceChunk)
setTimelineMode('closed')
setRange(sinceId, revId)
},
[historyController, setRange, setTimelineMode]
)

const loadMoreHistory = useCallback(
(state: boolean) => {
historyController.setLoadMore(state)
},
[historyController]
)

const handleTimelineClose = useCallback(() => {
setTimelineMode('closed')
}, [setTimelineMode])
Expand All @@ -174,117 +146,93 @@ export function DocumentPane(props: DocumentPaneProps) {
const isChangesOpen = historyController.changesPanelActive()
const isTimelineOpen = timelineMode !== 'closed'

const popoverContent = (
<ClickOutside onClickOutside={handleTimelineClose}>
{ref =>
timelineMode === 'rev' ? (
<Timeline
ref={ref as any}
timeline={timeline}
onSelect={selectRev}
onLoadMore={loadMoreHistory}
{...revTimelineProps(historyController.realRevChunk)}
/>
) : (
<Timeline
ref={ref as any}
timeline={timeline}
onSelect={selectSince}
onLoadMore={loadMoreHistory}
{...sinceTimelineProps(historyController.sinceTime!, historyController.realRevChunk)}
/>
)
}
</ClickOutside>
)

return (
<Popover
content={popoverContent as any}
open={isTimelineOpen}
placement="bottom"
targetElement={
timelineMode === 'rev' ? versionSelectRef.current : changesSinceSelectRef.current
}
<DocumentActionShortcuts
id={documentIdRaw}
type={documentType}
onKeyUp={handleKeyUp}
className={classNames([
styles.root,
isCollapsed && styles.isCollapsed,
isSelected ? styles.isActive : styles.isDisabled
])}
rootRef={rootRef}
>
<DocumentActionShortcuts
id={documentIdRaw}
type={documentType}
onKeyUp={handleKeyUp}
className={classNames([
styles.root,
isCollapsed && styles.isCollapsed,
isSelected ? styles.isActive : styles.isDisabled
])}
rootRef={rootRef}
>
<div className={styles.documentAndChangesContainer}>
<div className={styles.documentContainer}>
{isInspectOpen && <InspectDialog value={value} onClose={handleInspectClose} />}
<div className={styles.documentAndChangesContainer}>
<div className={styles.documentContainer}>
{isInspectOpen && <InspectDialog value={value} onClose={handleInspectClose} />}

<DocumentPanel
activeViewId={activeViewId}
documentId={documentId}
documentType={documentType}
draft={draft}
idPrefix={paneKey}
initialFocusPath={initialFocusPath}
initialValue={initialValue}
isClosable={isClosable}
isCollapsed={isCollapsed}
isHistoryOpen={isChangesOpen}
isTimelineOpen={isTimelineOpen}
markers={markers}
menuItemGroups={menuItemGroups}
onChange={onChange}
onCloseView={handleClosePane}
onCollapse={onCollapse}
onExpand={onExpand}
onSetActiveView={handleSetActiveView}
onSplitPane={handleSplitPane}
onTimelineOpen={handleTimelineRev}
paneTitle={paneTitle}
published={published}
rootElement={rootRef.current}
schemaType={schemaType}
timelineMode={timelineMode}
toggleInspect={toggleInspect}
value={value}
versionSelectRef={versionSelectRef}
views={views}
/>
</div>

<DocumentPanel
activeViewId={activeViewId}
{features.reviewChanges && !isCollapsed && isChangesOpen && (
<div className={styles.changesContainer}>
<ChangesPanel
changesSinceSelectRef={changesSinceSelectRef}
documentId={documentId}
documentType={documentType}
draft={draft}
idPrefix={paneKey}
initialFocusPath={initialFocusPath}
initialValue={initialValue}
isClosable={isClosable}
isCollapsed={isCollapsed}
isHistoryOpen={isChangesOpen}
isTimelineOpen={isTimelineOpen}
markers={markers}
menuItemGroups={menuItemGroups}
onChange={onChange}
onCloseView={handleClosePane}
onCollapse={onCollapse}
onExpand={onExpand}
onSetActiveView={handleSetActiveView}
onSplitPane={handleSplitPane}
onTimelineOpen={handleTimelineRev}
paneTitle={paneTitle}
published={published}
rootElement={rootRef.current}
loading={historyState === 'loading'}
onTimelineOpen={handleTimelineSince}
schemaType={schemaType}
since={historyController.sinceTime}
timelineMode={timelineMode}
toggleInspect={toggleInspect}
value={value}
versionSelectRef={versionSelectRef}
views={views}
/>
</div>

{features.reviewChanges && !isCollapsed && isChangesOpen && (
<div className={styles.changesContainer}>
<ChangesPanel
changesSinceSelectRef={changesSinceSelectRef}
documentId={documentId}
isTimelineOpen={isTimelineOpen}
loading={historyState === 'loading'}
onTimelineOpen={handleTimelineSince}
schemaType={schemaType}
since={historyController.sinceTime}
timelineMode={timelineMode}
/>
</div>
)}
</div>

<div className={styles.footerContainer}>
<DocumentStatusBar
id={documentId}
type={documentType}
lastUpdated={value && value._updatedAt}
/>
</div>

{connectionState === 'reconnecting' && (
<Snackbar kind="warning" isPersisted title="Connection lost. Reconnecting…" />
)}

<DocumentOperationResults id={documentId} type={documentType} />
</DocumentActionShortcuts>
</Popover>
</div>

<div className={styles.footerContainer}>
<DocumentStatusBar
id={documentId}
type={documentType}
lastUpdated={value && value._updatedAt}
/>
</div>

{connectionState === 'reconnecting' && (
<Snackbar kind="warning" isPersisted title="Connection lost. Reconnecting…" />
)}

<DocumentOperationResults id={documentId} type={documentType} />

<TimelinePopover
onClose={handleTimelineClose}
open={isTimelineOpen}
placement="bottom"
targetElement={
timelineMode === 'rev' ? versionSelectRef.current : changesSinceSelectRef.current
}
/>
</DocumentActionShortcuts>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ interface Props {
type: string
id: string
title: string
options: {}
options: Record<string, unknown>
component: React.ComponentType<any>
}[]
initialValue?: Doc
Expand Down Expand Up @@ -101,12 +101,7 @@ export const DocumentPaneProvider = withInitialValue((props: Props) => {
const value = editState.draft || editState.published || initialValue

return (
<DocumentHistoryProvider
documentId={documentId}
draft={editState.draft}
published={editState.published}
value={value}
>
<DocumentHistoryProvider documentId={documentId} value={value}>
<DocumentPane
connectionState={connectionState}
documentId={documentId}
Expand Down

0 comments on commit 8451cdd

Please sign in to comment.