Skip to content

Commit

Permalink
[desk-tool] Add basic styling to Timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
mariuslundgard authored and rexxars committed Oct 6, 2020
1 parent 02be577 commit cbf7e45
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {format, isToday, isYesterday, differenceInHours, differenceInMinutes} from 'date-fns'

export function formatHoursAgo(date: Date) {
const now = Date.now()
const h = differenceInHours(now, date)

if (h) {
return `${h}h`
}

const m = differenceInMinutes(now, date)

if (m) {
return `${m}m`
}

return 'Just now'
}

export function formatDate(date: Date) {
if (isToday(date)) {
return formatHoursAgo(date)
}

if (isYesterday(date)) {
return `Yesterday at ${format(date, 'h:mma')}`
}

return format(date)
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,6 @@
.root {
padding: 0.25em 0;
padding: 0.25em;
overflow: auto;
height: 100%;
}

.item {
padding: 1em;
}

.enabled {
@nest &:hover {
background: rgba(0, 0, 0, 0.1);
}
}

.disabled {
color: gray;
}

.withinSelection {
background: #a6c8fd;
}

.selected {
background: #06f;
color: #fff;
}

.item__typeName {
/* display: block; */
}

.item__timestamp {
font-size: 13px;
line-height: 16px;
box-sizing: border-box;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* eslint-disable react/prop-types */

import React, {useCallback, forwardRef, useRef} from 'react'
import {format} from 'date-fns'
import {Chunk} from '@sanity/field/diff'
import {Timeline as TimelineModel} from '../documentHistory/history/timeline'
import VisibilityContainer from './visibilityContainer'
import {TimelineItem} from './timelineItem'
import {TimelineItemState} from './types'

import styles from './timeline.css'

Expand Down Expand Up @@ -46,7 +48,7 @@ export const Timeline = forwardRef<HTMLDivElement, TimelineProps>(
visibilityContainerRef.current?.recalculate()
}, [visibilityContainerRef.current])

let state: ItemState = disabledBeforeSelection ? 'disabled' : 'enabled'
let state: TimelineItemState = disabledBeforeSelection ? 'disabled' : 'enabled'

return (
<div className={styles.root} ref={ref} onScroll={handleScroll}>
Expand Down Expand Up @@ -90,32 +92,3 @@ export const Timeline = forwardRef<HTMLDivElement, TimelineProps>(
)

Timeline.displayName = 'Timeline'

type ItemState = 'enabled' | 'disabled' | 'withinSelection' | 'selected'

function TimelineItem(props: {
state: ItemState
title: string
onSelect: (chunk: Chunk) => void
chunk: Chunk
timestamp: Date
type: string
}) {
const {state, onSelect, timestamp, chunk, title, type} = props

const handleClick = useCallback(
(evt: React.MouseEvent<HTMLDivElement>) => {
evt.preventDefault()
evt.stopPropagation()
onSelect(chunk)
},
[onSelect, chunk]
)

return (
<div className={`${styles.item} ${styles[state]}`} title={title} onClick={handleClick}>
<div className={styles.item__typeName}>{type}</div>
<div className={styles.item__timestamp}>{format(timestamp)}</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
.root {
position: relative;
-webkit-font-smoothing: inherit;
appearance: none;
font: inherit;
border-radius: 4px;
padding: 0;
margin: 0;
width: 100%;
display: block;
box-sizing: border-box;
background: none;
border: 0;
text-align: left;
min-width: 244px;

@nest &[data-state='enabled'] {
@nest &:hover {
background: #f1f3f6;
}
}

@nest &[data-state='disabled'] {
color: gray;
}

@nest &[data-state='withinSelection'] {
background: #a6c8fd;
}

@nest &[data-state='selected'] {
background: #06f;
}

@nest &::before, &::after {
content: '';
position: absolute;
left: 24px;
border-left: 1px solid #cad1dc;
}

@nest &[data-state='selected']::before, &[data-state='selected']::after {
border-color: #fff;
}

@nest &::before {
top: 0;
bottom: 50%;
z-index: 1;
}

@nest &::after {
top: 50%;
bottom: 0;
z-index: 2;
}

@nest &:first-child::before {
display: none;
}

@nest &:last-child::after {
display: none;
}
}

.wrapper {
position: relative;
padding: 0.5em;
display: flex;
align-items: center;
z-index: 3;
}

.iconContainer {
width: 33px;
height: 33px;
border-radius: calc(33px / 2);
padding: 0.25em;
box-sizing: border-box;
box-shadow: 0 0 0 2px #fff;

@nest & > svg {
display: block;
font-size: 25px;
}

@nest [data-type='editDraft'] & {
background: #fdefb6;
color: #978023;
}

@nest [data-type='publish'] & {
background: #3ab667;
color: #fff;
}

@nest [data-type='unpublish'] & {
background: #f03e2f;
color: #fff;
}

@nest [data-type='initial'] & {
background: #7b8ca8;
color: #fff;
}
}

.text {
flex: 1;
min-width: 0;
margin-left: 0.75em;
color: #66758d;

@nest .root[data-state='selected'] & {
color: #fff;
}
}

.typeName {
font-size: 13px;
font-weight: 600;
line-height: 16px;
}

.timestamp {
font-size: 13px;
line-height: 16px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import EditIcon from 'part:@sanity/base/edit-icon'
import PlusIcon from 'part:@sanity/base/plus-icon'
import PublishIcon from 'part:@sanity/base/publish-icon'
import UnpublishIcon from 'part:@sanity/base/unpublish-icon'
import React, {useCallback, createElement} from 'react'
import {Chunk} from '@sanity/field/diff'
import {formatDate} from './helpers'
import {TimelineItemState} from './types'

import styles from './timelineItem.css'

const ICON_COMPONENTS: {[key: string]: React.ComponentType<{}>} = {
initial: PlusIcon,
editDraft: EditIcon,
publish: PublishIcon,
unpublish: UnpublishIcon
}

const LABELS: {[key: string]: string} = {
initial: 'Created',
editDraft: 'Edited',
publish: 'Published',
unpublish: 'Unpublished'
}

export function TimelineItem(props: {
state: TimelineItemState
title: string
onSelect: (chunk: Chunk) => void
chunk: Chunk
timestamp: Date
type: string
}) {
const {state, onSelect, timestamp, chunk, title, type} = props
const iconComponent = ICON_COMPONENTS[type]
const label = LABELS[type] || <code>{type}</code>

const handleClick = useCallback(
(evt: React.MouseEvent<HTMLButtonElement>) => {
evt.preventDefault()
evt.stopPropagation()
onSelect(chunk)
},
[onSelect, chunk]
)

return (
<button
className={styles.root}
data-state={state}
data-type={type}
onClick={handleClick}
title={title}
type="button"
>
<div className={styles.wrapper}>
<div className={styles.iconContainer}>{iconComponent && createElement(iconComponent)}</div>
<div className={styles.text}>
<div className={styles.typeName}>{label}</div>
<div className={styles.timestamp}>{formatDate(timestamp)}</div>
</div>
</div>
</button>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type TimelineItemState = 'enabled' | 'disabled' | 'withinSelection' | 'selected'

0 comments on commit cbf7e45

Please sign in to comment.