Skip to content

Commit

Permalink
feat(core): add release actions list
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrobonamin committed Dec 6, 2024
1 parent 28d81eb commit 21db4b8
Show file tree
Hide file tree
Showing 10 changed files with 336 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface ResizableProps {
minWidth: number
maxWidth: number
initialWidth: number
resizerPosition?: 'left' | 'right'
}

const Root = styled(Box)`
Expand All @@ -19,7 +20,15 @@ const Root = styled(Box)`
export function Resizable(
props: ResizableProps & BoxProps & Omit<HTMLProps<HTMLDivElement>, 'as'>,
) {
const {as: forwardedAs, children, minWidth, maxWidth, initialWidth, ...restProps} = props
const {
as: forwardedAs,
children,
minWidth,
maxWidth,
initialWidth,
resizerPosition = 'right',
...restProps
} = props
const [element, setElement] = useState<HTMLDivElement | null>(null)
const elementWidthRef = useRef<number>()
const [targetWidth, setTargetWidth] = useState<number>(initialWidth)
Expand All @@ -31,12 +40,14 @@ export function Resizable(
const handleResize = useCallback(
(deltaX: number) => {
const w = elementWidthRef.current

if (!w) return

setTargetWidth(Math.min(Math.max(w - deltaX, minWidth), maxWidth))
if (resizerPosition === 'right') {
setTargetWidth(Math.min(Math.max(w - deltaX, minWidth), maxWidth))
} else {
setTargetWidth(Math.min(Math.max(w + deltaX, minWidth), maxWidth))
}
},
[minWidth, maxWidth],
[minWidth, maxWidth, resizerPosition],
)

const style = useMemo(
Expand All @@ -46,8 +57,13 @@ export function Resizable(

return (
<Root as={forwardedAs} {...restProps} ref={setElement} style={style}>
{resizerPosition === 'left' && (
<Resizer onResize={handleResize} onResizeStart={handleResizeStart} position="left" />
)}
{children}
<Resizer onResize={handleResize} onResizeStart={handleResizeStart} position="right" />
{resizerPosition === 'right' && (
<Resizer onResize={handleResize} onResizeStart={handleResizeStart} position="right" />
)}
</Root>
)
}
1 change: 1 addition & 0 deletions packages/sanity/src/core/releases/i18n/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const releasesLocaleStrings = {
'created the <strong>{{releaseTitle}}</strong> release <ScheduleTarget>targeting </ScheduleTarget>',
/* The text for the activity event when a document is removed from a release */
'activity.event.discard-document': 'discarded a document version',
'activity.event.edit': 'set release time to <ScheduleTarget></ScheduleTarget>',
/* The text for the activity event when the release is published */
'activity.event.publish': 'published the <strong>{{releaseTitle}}</strong> release',
/* The text for the activity event when the release is scheduled */
Expand Down
1 change: 1 addition & 0 deletions packages/sanity/src/core/releases/store/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export type EditableReleaseDocument = Omit<
PartialExcept<ReleaseDocument, '_id'>,
'metadata' | '_type'
> & {
_id: string
metadata: Partial<ReleaseDocument['metadata']>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ const TableInner = <TableData, AdditionalRowTableData>({
height: `${datum.virtualRow.size}px`,
transform: `translateY(${datum.virtualRow.start - datum.index * datum.virtualRow.size}px)`,
paddingInline: `max(
calc((100vw - var(--maxInlineSize)) / 2),
calc((100% - var(--maxInlineSize)) / 2),
var(--paddingInline)
)`,
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const TableHeader = ({headers, searchDisabled}: TableHeaderProps) => {
as="tr"
style={{
paddingInline: `max(
calc((100vw - var(--maxInlineSize)) / 2),
calc((100% - var(--maxInlineSize)) / 2),
var(--paddingInline)
)`,
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {Box, Card, Flex, Stack, Text} from '@sanity/ui'
import {AnimatePresence, motion} from 'framer-motion'
import {useMemo} from 'react'
import {styled} from 'styled-components'

import {LoadingBlock} from '../../../components/loadingBlock/LoadingBlock'
import {RelativeTime} from '../../../components/RelativeTime'
import {UserAvatar} from '../../../components/userAvatar/UserAvatar'
import {Resizable} from '../../../form/studio/tree-editing/components/layout/resizer'
import {useDateTimeFormat} from '../../../hooks/useDateTimeFormat'
import {Translate, useTranslation} from '../../../i18n'
import {useDocumentPreviewValues} from '../../../tasks/hooks'
Expand All @@ -17,6 +19,7 @@ import {
isAddDocumentToReleaseEvent,
isCreateReleaseEvent,
isDiscardDocumentFromReleaseEvent,
isEditReleaseEvent,
isScheduleReleaseEvent,
type ReleaseEvent,
} from './activity/types'
Expand All @@ -40,27 +43,9 @@ const ACTIVITY_TEXT_118N: Record<ReleaseEvent['type'], string> = {
ScheduleRelease: 'activity.event.schedule',
UnarchiveRelease: 'activity.event.unarchive',
UnscheduleRelease: 'activity.event.unschedule',
releaseEditEvent: 'activity.event.edit',
}

// Missing:
// - ReleaseSetTargetDate
//
// /* <StatusText size={1}>
// set release time to{' '}
// {event.timing === 'future' ? (
// <em>{format(event.time || 0, `PPpp`)}</em>
// ) : event.timing === 'immediately' ? (
// <em>immediately</em>
// ) : (
// <em>never</em>
// )}{' '}
// &middot;{' '}
// <span title={format(event.timestamp, 'yyyy-MM-dd pp')}>
// {shortTimeSince(event.timestamp)}
// </span>
// </StatusText> */
//

const ReleaseEventDocumentPreview = ({
event,
release,
Expand Down Expand Up @@ -90,26 +75,48 @@ const ReleaseEventDocumentPreview = ({

const ScheduleTarget = ({children, event}: {children: React.ReactNode; event: ReleaseEvent}) => {
const dateTimeFormat = useDateTimeFormat({dateStyle: 'full', timeStyle: 'medium'})
const dateString =
isScheduleReleaseEvent(event) || isCreateReleaseEvent(event) ? event.publishAt : null

const formattedDate = useMemo(() => {
if (isEditReleaseEvent(event)) {
if (event.change.releaseType === 'asap') return 'immediately'
if (event.change.releaseType === 'undecided') return 'never'
}

let dateString: string | undefined
if (isScheduleReleaseEvent(event)) {
dateString = event.publishAt
} else if (isCreateReleaseEvent(event)) {
dateString = event.change?.intendedPublishDate
} else if (isEditReleaseEvent(event)) {
dateString = event.change.intendedPublishDate
}

if (!dateString) return null
return dateTimeFormat.format(new Date(dateString))
}, [dateString, dateTimeFormat])
}, [dateTimeFormat, event])

if (!formattedDate) return null
if (!formattedDate && isCreateReleaseEvent(event)) return null
return (
<span>
{children} <strong>{formattedDate}</strong>
{children} <strong>{formattedDate || '---'}</strong>
</span>
)
}
const FadeInCard = motion(Card)
const ActivityItem = ({event, release}: {event: ReleaseEvent; release: ReleaseDocument}) => {
const {t} = useTranslation(releasesLocaleNamespace)

return (
<Card paddingX={1} paddingY={1}>
<FadeInCard
paddingX={1}
paddingY={1}
overflow="hidden"
// This card animates on entrance when a new event is added, not for every event that is rendered.
// So if the user is seeing the list and a new event lands, the list will animate the new event. (That is by the <AnimatePresence initial=false> component wrapping the list)
initial={{height: 0, opacity: 0}}
animate={{height: 'auto', opacity: 1}}
transition={{type: 'spring', bounce: 0, duration: 0.4}}
>
<Flex align="flex-start" gap={2}>
<UserAvatar user={event.author} />
<Stack flex={1}>
Expand All @@ -133,35 +140,69 @@ const ActivityItem = ({event, release}: {event: ReleaseEvent; release: ReleaseDo
) : null}
</Stack>
</Flex>
</Card>
</FadeInCard>
)
}

interface ReleaseDashboardActivityPanelProps {
activity: ReleaseActivity
release: ReleaseDocument
show: boolean
}
const MotionFlex = motion(Flex)
const FillHeight = styled.div`
height: 100%;
display: flex;
flex-direction: column;
`
export function ReleaseDashboardActivityPanel({
activity,
release,
show,
}: ReleaseDashboardActivityPanelProps) {
const {t} = useTranslation(releasesLocaleNamespace)
return (
<Stack>
<Box padding={4}>
<Text size={1} weight="medium">
{t('activity.panel.title')}
</Text>
</Box>
{activity.loading && !activity.events.length && (
<LoadingBlock title={t('activity.panel.loading')} />
<AnimatePresence>
{show && (
<>
<Card flex="none" borderLeft marginY={2} style={{opacity: 0.6}} />
<motion.div
animate={{width: 'auto', opacity: 1}}
initial={{width: 0, opacity: 0}}
exit={{width: 0, opacity: 0}}
transition={{type: 'spring', bounce: 0, duration: 0.2}}
>
<Resizable
as={FillHeight}
minWidth={320}
maxWidth={800}
initialWidth={320}
resizerPosition="left"
style={{display: 'flex', flexDirection: 'column', flex: 'none', maxHeight: '100%'}}
>
<MotionFlex flex="none" height="fill" direction="column">
<Box padding={4}>
<Text size={1} weight="medium">
{t('activity.panel.title')}
</Text>
</Box>
{activity.loading && !activity.events.length && (
<LoadingBlock title={t('activity.panel.loading')} />
)}
{/* TODO: Virtualise this list and add scroll parent */}
<Stack space={1} paddingX={3} height="fill" style={{overflow: 'scroll'}}>
<AnimatePresence initial={false}>
{activity.events.map((event) => (
<ActivityItem key={event.id} event={event} release={release} />
))}
</AnimatePresence>
<Box paddingBottom={4} />
</Stack>
</MotionFlex>
</Resizable>
</motion.div>
</>
)}
{/* TODO: Virtualise this list and add scroll parent */}
<Stack space={1} paddingX={3}>
{activity.events.map((event) => (
<ActivityItem key={event.id} event={event} release={release} />
))}
</Stack>
</Stack>
</AnimatePresence>
)
}
15 changes: 6 additions & 9 deletions packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export const ReleaseDetail = () => {

if (releaseInDetail) {
return (
<Flex direction="column" flex={1} height="fill">
<Flex direction="column" flex={1} height="fill" overflow="hidden">
<Card flex="none" padding={3}>
<ReleaseDashboardHeader
release={releaseInDetail}
Expand All @@ -145,14 +145,11 @@ export const ReleaseDetail = () => {
/>
</Flex>

{inspector === 'activity' && (
<>
<Card flex="none" borderLeft marginY={2} style={{opacity: 0.6}} />
<Card flex="none" style={{width: 320}}>
<ReleaseDashboardActivityPanel activity={activity} release={releaseInDetail} />
</Card>
</>
)}
<ReleaseDashboardActivityPanel
activity={activity}
release={releaseInDetail}
show={inspector === 'activity'}
/>
</Flex>
</Flex>
)
Expand Down
Loading

0 comments on commit 21db4b8

Please sign in to comment.