Skip to content

Commit

Permalink
feat: ui updates
Browse files Browse the repository at this point in the history
  • Loading branch information
hermanwikner committed Sep 25, 2023
1 parent 74b0c4e commit 614f402
Show file tree
Hide file tree
Showing 18 changed files with 172 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react'
import styled from 'styled-components'
import {Avatar, AvatarProps} from '@sanity/ui'
import {User} from '@sanity/types'

const StyledAvatar = styled(Avatar)`
svg > ellipse {
stroke: transparent;
}
`

const SYMBOLS = /[^\p{Alpha}\p{White_Space}]/gu
const WHITESPACE = /\p{White_Space}+/u

function nameToInitials(fullName: string) {
const namesArray = fullName.replace(SYMBOLS, '').split(WHITESPACE)

if (namesArray.length === 1) {
return `${namesArray[0].charAt(0)}`.toUpperCase()
}

return `${namesArray[0].charAt(0)}${namesArray[namesArray.length - 1].charAt(0)}`
}

interface CommentsAvatarProps extends AvatarProps {
user: User | undefined | null
}

export function CommentsAvatar(props: CommentsAvatarProps) {
const {user: userProp, ...restProps} = props
const user = userProp as User
const initials = nameToInitials(user?.displayName || '')

const avatar = user ? (
<StyledAvatar src={user?.imageUrl} initials={initials} {...restProps} />
) : (
<StyledAvatar {...restProps} />
)

return avatar
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react'

const INLINE_STYLE: React.CSSProperties = {
minWidth: 25,
}

/**
* This component is used to as a spacer in situations where we want to align
* components without avatars with components that have avatars.
*/
export function SpacerAvatar() {
return <div style={INLINE_STYLE} />
}
2 changes: 2 additions & 0 deletions packages/sanity/src/core/comments/components/avatars/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './CommentsAvatar'
export * from './SpacerAvatar'
1 change: 0 additions & 1 deletion packages/sanity/src/core/comments/components/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {FlexProps} from '@sanity/ui'

export const FLEX_GAP: FlexProps['gap'] = 3
export const AVATAR_SIZE = 25
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export function CommentThreadLayout(props: CommentThreadLayoutProps) {
</ThreadCard>
)}

{children}
<Stack space={2}>{children}</Stack>
</Stack>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,15 +183,7 @@ export const CommentsList = forwardRef<CommentsListHandle, CommentsListProps>(fu
)}

{showComments && (
<Stack
flex={1}
overflow="auto"
paddingBottom={6}
paddingX={3}
paddingY={4}
sizing="border"
space={4}
>
<Stack flex={1} overflow="auto" padding={3} paddingBottom={6} sizing="border" space={4}>
<BoundaryElementProvider element={boundaryElement}>
{groupedComments.map(([fieldPath, group]) => {
const parentComments = group.filter((c) => !c.parentCommentId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
CommentStatus,
} from '../../types'
import {MentionOptionsHookValue} from '../../hooks'
import {AVATAR_SIZE} from '../constants'
import {SpacerAvatar} from '../avatars'
import {CommentsListItemLayout} from './CommentsListItemLayout'

const EMPTY_ARRAY: [] = []
Expand Down Expand Up @@ -123,6 +123,12 @@ export function CommentsListItem(props: CommentsListItemProps) {
return replies.length > MAX_COLLAPSED_REPLIES
}, [replies])

const expandButtonText = useMemo(() => {
return `${replies?.length - MAX_COLLAPSED_REPLIES} more ${
replies?.length - MAX_COLLAPSED_REPLIES === 1 ? 'comment' : 'comments'
}`
}, [replies?.length])

useEffect(() => {
if (replies.length > MAX_COLLAPSED_REPLIES && !didExpand.current) {
setCollapsed(true)
Expand All @@ -149,11 +155,7 @@ export function CommentsListItem(props: CommentsListItemProps) {

{showCollapseButton && !didExpand.current && (
<Flex gap={1} paddingY={1} sizing="border">
<div
style={{
width: AVATAR_SIZE,
}}
/>
<SpacerAvatar />

<ExpandButton
fontSize={1}
Expand All @@ -162,9 +164,7 @@ export function CommentsListItem(props: CommentsListItemProps) {
onClick={handleExpand}
padding={2}
space={2}
text={`${replies?.length - MAX_COLLAPSED_REPLIES} more ${
replies?.length - MAX_COLLAPSED_REPLIES === 1 ? 'comment' : 'comments'
}`}
text={expandButtonText}
/>
</Flex>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
EyeOpenIcon,
} from '@sanity/icons'
import {
Avatar,
TextSkeleton,
Flex,
Button,
Expand All @@ -28,17 +27,17 @@ import {CurrentUser, Path} from '@sanity/types'
import styled, {css} from 'styled-components'
import {format} from 'date-fns'
import * as PathUtils from '@sanity/util/paths'
import {UserAvatar} from '../../../components'
import {TimeAgoOpts, useTimeAgo} from '../../../hooks'
import {useUser} from '../../../store'
import {CommentMessageSerializer} from '../pte'
import {CommentInput} from '../pte/comment-input'
import {CommentDocument, CommentEditPayload, CommentMessage, CommentStatus} from '../../types'
import {AVATAR_SIZE, FLEX_GAP} from '../constants'
import {FLEX_GAP} from '../constants'
import {useDidUpdate} from '../../../form'
import {useCommentHasChanged} from '../../helpers'
import {MentionOptionsHookValue} from '../../hooks'
import {TextTooltip} from '../TextTooltip'
import {CommentsAvatar, SpacerAvatar} from '../avatars'

const FloatingLayer = styled(Layer)(({theme}) => {
const {space} = theme.sanity
Expand Down Expand Up @@ -126,9 +125,8 @@ export function CommentsListItemLayout(props: CommentsListItemLayoutProps) {

const hasChanges = useCommentHasChanged(value)

const _date = lastEditedAt || _createdAt
const date = _date ? new Date(_date) : new Date()
const timeAgo = useTimeAgo(date, TIME_AGO_OPTS)
const createdDate = _createdAt ? new Date(_createdAt) : new Date()
const createdTimeAgo = useTimeAgo(createdDate, TIME_AGO_OPTS)

const handleMenuOpen = useCallback(() => setMenuOpen(true), [])
const handleMenuClose = useCallback(() => setMenuOpen(false), [])
Expand Down Expand Up @@ -181,7 +179,7 @@ export function CommentsListItemLayout(props: CommentsListItemLayoutProps) {
}
}, [rootElement])

const avatar = user ? <UserAvatar user={user} /> : <Avatar />
const avatar = <CommentsAvatar user={user} />

const name = user?.displayName ? (
<Text size={1} weight="semibold" textOverflow="ellipsis" title={user.displayName}>
Expand All @@ -207,20 +205,34 @@ export function CommentsListItemLayout(props: CommentsListItemLayoutProps) {
<Flex align="flex-end" gap={2} flex={1}>
<Box flex={1}>{name}</Box>

<Text size={0} muted title={format(date, 'PPPPp')} style={{minWidth: 'max-content'}}>
{timeAgo} {lastEditedAt && <>(edited)</>}
</Text>
<Flex align="center" gap={1}>
<Text
size={0}
muted
title={format(createdDate, 'PPPPp')}
style={{minWidth: 'max-content'}}
>
{createdTimeAgo}
</Text>

{lastEditedAt && (
<Text
size={0}
muted
title={format(new Date(lastEditedAt), 'PPPPp')}
style={{minWidth: 'max-content'}}
>
(edited)
</Text>
)}
</Flex>
</Flex>
</Flex>
</Flex>

{isEditing && (
<Flex align="flex-start" gap={2} ref={setRootElement}>
<div
style={{
minWidth: AVATAR_SIZE,
}}
/>
<SpacerAvatar />

<Stack flex={1}>
<CommentInput
Expand Down Expand Up @@ -319,7 +331,7 @@ export function CommentsListItemLayout(props: CommentsListItemLayoutProps) {
</TooltipDelayGroupProvider>

<Flex gap={FLEX_GAP}>
<div style={{minWidth: AVATAR_SIZE}} aria-hidden="true" />
<SpacerAvatar />

<CommentMessageSerializer blocks={message} />
</Flex>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {Avatar, TextSkeleton, Flex, Text, Card, Box, Badge} from '@sanity/ui'
import {TextSkeleton, Flex, Text, Card, Box, Badge} from '@sanity/ui'
import React, {useCallback} from 'react'
import styled from 'styled-components'
import {MentionOptionUser} from '../../types'
import {UserAvatar} from '../../../components'
import {useUser} from '../../../store'
import {CommentsAvatar} from '../avatars/CommentsAvatar'

const InnerFlex = styled(Flex)``

Expand All @@ -16,10 +16,8 @@ export function MentionsMenuItem(props: MentionsItemProps) {
const {user, onSelect} = props
const [loadedUser] = useUser(user.id)

const avatar = loadedUser ? (
<UserAvatar user={loadedUser} status={user.canBeMentioned ? undefined : 'inactive'} />
) : (
<Avatar />
const avatar = (
<CommentsAvatar user={loadedUser} status={user.canBeMentioned ? undefined : 'inactive'} />
)

const text = loadedUser ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const PortableTextWrap = styled(Stack)(() => {
`
})

const EMPTY_ARRAY: [] = []

const components: PortableTextComponents = {
block: {
normal: ({children}) => <NormalBlock>{children}</NormalBlock>,
Expand All @@ -30,9 +32,16 @@ const components: PortableTextComponents = {
h6: ({children}) => <NormalBlock>{children}</NormalBlock>,
blockquote: ({children}) => <NormalBlock>{children}</NormalBlock>,
code: ({children}) => <NormalBlock>{children}</NormalBlock>,
ul: ({children}) => <NormalBlock>{children}</NormalBlock>,
ol: ({children}) => <NormalBlock>{children}</NormalBlock>,
li: ({children}) => <NormalBlock>{children}</NormalBlock>,
},
list: {
bullet: ({children}) => <>{children}</>,
number: ({children}) => <>{children}</>,
checkmarks: ({children}) => <>{children}</>,
},
listItem: {
bullet: ({children}) => <NormalBlock>{children}</NormalBlock>,
number: ({children}) => <NormalBlock>{children}</NormalBlock>,
checkmarks: ({children}) => <NormalBlock>{children}</NormalBlock>,
},
marks: {
// Since we do not offer any formatting options, we can just use the normal block for all of these.
Expand All @@ -41,6 +50,7 @@ const components: PortableTextComponents = {
code: ({children}) => <>{children}</>,
underline: ({children}) => <>{children}</>,
strikeThrough: ({children}) => <>{children}</>,
link: ({children}) => <>{children}</>,
},
types: {
mention: (props) => {
Expand All @@ -49,20 +59,20 @@ const components: PortableTextComponents = {
},
}

interface PortableTextSerializerProps {
interface CommentMessageSerializerProps {
blocks: CommentMessage
}

/**
* @beta
* @hidden
*/
export function CommentMessageSerializer(props: PortableTextSerializerProps) {
export function CommentMessageSerializer(props: CommentMessageSerializerProps) {
const {blocks} = props

return (
<PortableTextWrap>
<PortableText value={blocks || []} components={components} />
<PortableText value={blocks || EMPTY_ARRAY} components={components} />
</PortableTextWrap>
)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react'
import {Tooltip, Flex, Text} from '@sanity/ui'
import styled, {css} from 'styled-components'
import {UserAvatar} from '../../../../components'
import {useCurrentUser, useUser} from '../../../../store'
import {CommentsAvatar} from '../../avatars/CommentsAvatar'

const Span = styled.span(({theme}) => {
const {regular} = theme.sanity.fonts?.text.weights
Expand Down Expand Up @@ -42,7 +42,7 @@ export function MentionInlineBlock(props: MentionInlineBlockProps) {
content={
<Flex align="center" padding={2} gap={1}>
<Flex>
<UserAvatar user={user} />
<CommentsAvatar user={user} />
</Flex>

<Text size={1}>{user.displayName}</Text>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, {useCallback, useRef, useState} from 'react'
import {Avatar, Flex, Button, MenuDivider, Box, Card, Stack} from '@sanity/ui'
import React, {useCallback, useState} from 'react'
import {Flex, Button, MenuDivider, Box, Card, Stack} from '@sanity/ui'
import styled, {css} from 'styled-components'
import {CurrentUser} from '@sanity/types'
import {ArrowUpIcon, CloseIcon} from '@sanity/icons'
import {CloseIcon} from '@sanity/icons'
import {useUser} from '../../../../store'
import {UserAvatar} from '../../../../components'
import {MentionIcon} from '../../icons'
import {MentionIcon, SendIcon} from '../../icons'
import {CommentsAvatar} from '../../avatars/CommentsAvatar'
import {useCommentInput} from './useCommentInput'
import {Editable} from './Editable'

Expand Down Expand Up @@ -82,7 +82,7 @@ export function CommentInputInner(props: CommentInputInnerProps) {
const [user] = useUser(currentUser.id)
const {openMentions, focused, expandOnFocus, canSubmit, hasChanges} = useCommentInput()

const avatar = user ? <UserAvatar size={0} user={user} /> : <Avatar size={0} />
const avatar = <CommentsAvatar user={user} />

const handleDiscardEdit = useCallback(() => {
onEditDiscard()
Expand Down Expand Up @@ -134,7 +134,7 @@ export function CommentInputInner(props: CommentInputInnerProps) {
aria-label="Send comment"
disabled={!canSubmit || !hasChanges}
fontSize={1}
icon={ArrowUpIcon}
icon={SendIcon}
mode={hasChanges && canSubmit ? 'default' : 'bleed'}
onClick={onSubmit}
padding={2}
Expand Down
2 changes: 1 addition & 1 deletion packages/sanity/src/core/comments/components/pte/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './Serializer'
export * from './CommentMessageSerializer'
export * from './comment-input'
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ export function useCommentOperations(
const handleCreate = useCallback(
async (comment: CommentCreatePayload) => {
const nextComment = {
_type: 'comment',
_id: uuid(),
_type: 'comment',
authorId: authorId || '', // improve
lastEditedAt: undefined,
message: comment.message,
status: comment.status,
parentCommentId: comment.parentCommentId,
status: comment.status,
threadId: comment.threadId,
context: {
payload: {
Expand Down
Loading

0 comments on commit 614f402

Please sign in to comment.