mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
[OMN-134] Update library cards based on new design
This commit is contained in:
commit
f7396d0462
6 changed files with 163 additions and 140 deletions
|
|
@ -23,7 +23,7 @@ export function Label(props: LabelProps): JSX.Element {
|
|||
color: props.color,
|
||||
padding: '4px 8px 4px 8px',
|
||||
border: `1px solid rgba(${color[0]}, ${color[1]}, ${color[2]}, 0.40)`,
|
||||
backgroundColor: `rgba(${color[0]}, ${color[1]}, ${color[2]}, 0.20)`,
|
||||
backgroundColor: `rgba(${color[0]}, ${color[1]}, ${color[2]}, 0.05)`,
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { DotsThree, DotsThreeVertical } from 'phosphor-react'
|
||||
|
||||
type Orientation = 'horizontal' | 'vertical'
|
||||
|
||||
type MoreOptionsIconProps = {
|
||||
|
|
@ -7,48 +9,9 @@ type MoreOptionsIconProps = {
|
|||
}
|
||||
|
||||
export function MoreOptionsIcon(props: MoreOptionsIconProps): JSX.Element {
|
||||
const dots =
|
||||
props.orientation == 'horizontal' ? (
|
||||
<>
|
||||
<circle
|
||||
cx="7"
|
||||
cy="12"
|
||||
r="1.5"
|
||||
transform="rotate(-90 7 12)"
|
||||
fill={props.strokeColor}
|
||||
/>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="1.5"
|
||||
transform="rotate(-90 12 12)"
|
||||
fill={props.strokeColor}
|
||||
/>
|
||||
<circle
|
||||
cx="17"
|
||||
cy="12"
|
||||
r="1.5"
|
||||
transform="rotate(-90 17 12)"
|
||||
fill={props.strokeColor}
|
||||
/>
|
||||
</>
|
||||
return props.orientation == 'horizontal' ? (
|
||||
<DotsThree size={props.size} color={props.strokeColor}/>
|
||||
) : (
|
||||
<>
|
||||
<circle cx="12" cy="6" r="1.5" fill={props.strokeColor} />
|
||||
<circle cx="12" cy="12" r="1.5" fill={props.strokeColor} />
|
||||
<circle cx="12" cy="18" r="1.5" fill={props.strokeColor} />
|
||||
</>
|
||||
<DotsThreeVertical size={props.size} color={props.strokeColor}/>
|
||||
)
|
||||
|
||||
return (
|
||||
<svg
|
||||
width={props.size}
|
||||
height={props.size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
{dots}
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ function articleSubtitle(url: string, author?: string): string {
|
|||
}
|
||||
|
||||
export function authoredByText(author: string): string {
|
||||
return `By ${removeHTMLTags(author)}`
|
||||
return `by ${removeHTMLTags(author)}`
|
||||
}
|
||||
|
||||
function removeHTMLTags(str: string | null | undefined): string {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { theme } from './../tokens/stitches.config'
|
|||
import { CardMenu } from './../patterns/CardMenu'
|
||||
import { LayoutType } from '../templates/homeFeed/HomeFeedContainer'
|
||||
import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery'
|
||||
import { Label } from './../elements/Label'
|
||||
|
||||
export type LinkedItemCardAction =
|
||||
| 'showDetail'
|
||||
|
|
@ -45,6 +46,23 @@ export function LinkedItemCard(props: LinkedItemCardProps): JSX.Element {
|
|||
}
|
||||
}
|
||||
|
||||
// Dummy Data
|
||||
const Labels = [
|
||||
{
|
||||
text: 'Tag 1',
|
||||
color: "#B20042"
|
||||
},
|
||||
{
|
||||
text: 'Tag 2',
|
||||
color: "#01A800"
|
||||
},
|
||||
{
|
||||
text: 'Tag 3',
|
||||
color: "#007E9E"
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
export function GridLinkedItemCard(props: LinkedItemCardProps): JSX.Element {
|
||||
const originText = siteName(props.item.originalArticleUrl, props.item.url)
|
||||
|
||||
|
|
@ -63,13 +81,33 @@ export function GridLinkedItemCard(props: LinkedItemCardProps): JSX.Element {
|
|||
overflow: 'clip',
|
||||
border: '1px solid $grayBorder',
|
||||
boxShadow: '0px 3px 11px rgba(32, 31, 29, 0.04)',
|
||||
position: 'relative',
|
||||
}}
|
||||
alignment='start'
|
||||
distribution='start'
|
||||
onClick={() => {
|
||||
props.handleAction('showDetail')
|
||||
}}
|
||||
className="grid-item-card"
|
||||
>
|
||||
<Box css={{
|
||||
position: 'absolute',
|
||||
top: '1px',
|
||||
left: '1px',
|
||||
width: 'calc(100% - 2px)',
|
||||
'& > div': {
|
||||
borderRadius: '100vmax 100vmax 0 0',
|
||||
}
|
||||
}}>
|
||||
<ProgressBar
|
||||
fillPercentage={props.item.readingProgressPercent}
|
||||
fillColor={theme.colors.highlight.toString()}
|
||||
backgroundColor={theme.colors.grayTextContrast.toString()}
|
||||
borderRadius={props.item.readingProgressPercent===100 ?
|
||||
'0' : '0px 8px 8px 0px'
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
<VStack
|
||||
distribution="start"
|
||||
alignment="start"
|
||||
|
|
@ -79,9 +117,21 @@ export function GridLinkedItemCard(props: LinkedItemCardProps): JSX.Element {
|
|||
pl: '$1',
|
||||
}}
|
||||
>
|
||||
<HStack alignment='start' distribution='between' css={{ width: '100%', p: '0px', mr: '-12px' }}>
|
||||
<StyledText style="caption" css={{ my: '$1' }}>
|
||||
{originText}
|
||||
<HStack alignment='start' distribution='between' css={{ width: '100%', p: '0px', mr: '-12px', mt: '15px'}}>
|
||||
<StyledText
|
||||
style="listTitle"
|
||||
css={{
|
||||
mt: '0',
|
||||
mb:'0',
|
||||
fontWeight: '700',
|
||||
textAlign: 'left',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
width: '100%',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
{props.item.title}
|
||||
</StyledText>
|
||||
<Box
|
||||
css={{ alignSelf: 'end', alignItems: 'start', height: '100%' }}
|
||||
|
|
@ -98,61 +148,74 @@ export function GridLinkedItemCard(props: LinkedItemCardProps): JSX.Element {
|
|||
<MoreOptionsIcon
|
||||
size={24}
|
||||
strokeColor={theme.colors.grayTextContrast.toString()}
|
||||
orientation="vertical"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
}
|
||||
actionHandler={props.handleAction}
|
||||
/>
|
||||
</Box>
|
||||
</HStack>
|
||||
<StyledText
|
||||
style="listTitle"
|
||||
css={{ mt: '0px', mb: '$1', textAlign: 'left', pr: '24px' }}
|
||||
>
|
||||
{props.item.title}
|
||||
</StyledText>
|
||||
<HStack alignment='start' distribution='between'>
|
||||
<StyledText style="caption" css={{ my: '0', mt:'-$2' }}>
|
||||
{props.item.author && (
|
||||
<SpanBox css={{ mr: '8px' }}>
|
||||
{authoredByText(props.item.author)}
|
||||
</SpanBox>
|
||||
)
|
||||
}
|
||||
<SpanBox css={{ textDecorationLine: 'underline' }}>
|
||||
{originText}
|
||||
</SpanBox>
|
||||
</StyledText>
|
||||
</HStack>
|
||||
|
||||
</VStack>
|
||||
<HStack alignment='start' distribution='between' css={{
|
||||
width: '100%',
|
||||
pt: '$2',
|
||||
px: '$1',
|
||||
pr: '12px',
|
||||
mt: '7px',
|
||||
flexGrow: '1',
|
||||
}}>
|
||||
<StyledText
|
||||
style="caption"
|
||||
css={{
|
||||
m: 0,
|
||||
py: '0px',
|
||||
mr: '$2',
|
||||
fontStyle: 'normal',
|
||||
fontWeight: '400',
|
||||
fontSize: '14px',
|
||||
lineHeight: '125%',
|
||||
color: '$grayTextContrast',
|
||||
flexGrow: '4',
|
||||
overflow: 'hidden',
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 5,
|
||||
WebkitBoxOrient: "vertical",
|
||||
}}
|
||||
>
|
||||
{props.item.author && (
|
||||
<SpanBox css={{ my: '$1' }}>
|
||||
{authoredByText(props.item.author)}
|
||||
{props.item.description ? ' \u2013 ' : ''}
|
||||
</SpanBox>
|
||||
)}
|
||||
{props.item.description?.substring(0, 300)}
|
||||
{props.item.description}
|
||||
</StyledText>
|
||||
{props.item.image && (
|
||||
{/* {props.item.image && ( */}
|
||||
<CoverImage
|
||||
src={props.item.image}
|
||||
// src={props.item.image}
|
||||
src={"https://www.industrialempathy.com/img/remote/ZiClJf-1920w.jpg"}
|
||||
alt="Link Preview Image"
|
||||
width={88}
|
||||
height={88}
|
||||
css={{ ml: '8px', mb: '8px', mt: '8px' }}
|
||||
width={135}
|
||||
height={90}
|
||||
css={{ ml: '10px', mb: '8px', borderRadius: '3px' }}
|
||||
onError={(e) => {
|
||||
;(e.target as HTMLElement).style.display = 'none'
|
||||
// (e.target as HTMLElement).style.display = 'none'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{/* )} */}
|
||||
</HStack>
|
||||
<HStack css={{ mt: '8px' }}>
|
||||
{
|
||||
Labels.map(({text, color}, index) => <Label key={index} text={text} color={color} />)
|
||||
}
|
||||
</HStack>
|
||||
<ProgressBar
|
||||
fillPercentage={props.item.readingProgressPercent}
|
||||
fillColor={theme.colors.highlight.toString()}
|
||||
backgroundColor={theme.colors.grayTextContrast.toString()}
|
||||
/>
|
||||
</VStack>
|
||||
// </Link>
|
||||
)
|
||||
|
|
@ -163,30 +226,26 @@ export function ListLinkedItemCard(props: LinkedItemCardProps): JSX.Element {
|
|||
|
||||
return (
|
||||
// <Link href={`/${username}/${props.item.slug}`} passHref={true}>
|
||||
<VStack
|
||||
css={{
|
||||
p: '$2',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
maxWidth: '100%',
|
||||
borderRadius: 0,
|
||||
cursor: 'pointer',
|
||||
wordBreak: 'break-word',
|
||||
borderTop: '1px solid $grayBorder',
|
||||
boxShadow: '0px 3px 11px rgba(32, 31, 29, 0.04)',
|
||||
}}
|
||||
onClick={() => {
|
||||
props.handleAction('showDetail')
|
||||
}}
|
||||
>
|
||||
<HStack
|
||||
distribution="start"
|
||||
alignment="start"
|
||||
css={{ width: '100%', justifySelf: 'start' }}
|
||||
css={{
|
||||
p: '$3',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
maxWidth: '100%',
|
||||
borderRadius: 0,
|
||||
cursor: 'pointer',
|
||||
wordBreak: 'break-word',
|
||||
border: '1px solid $grayBorder',
|
||||
borderBottom: 'none',
|
||||
alignItems: "center",
|
||||
}}
|
||||
onClick={() => {
|
||||
props.handleAction('showDetail')
|
||||
}}
|
||||
>
|
||||
<VStack
|
||||
<HStack
|
||||
distribution="start"
|
||||
alignment="start"
|
||||
alignment="end"
|
||||
css={{
|
||||
px: '$2',
|
||||
flexGrow: 1,
|
||||
|
|
@ -195,21 +254,46 @@ export function ListLinkedItemCard(props: LinkedItemCardProps): JSX.Element {
|
|||
>
|
||||
<StyledText
|
||||
style="listTitle"
|
||||
css={{ mt: '0px', mb: '$1', textAlign: 'left' }}
|
||||
css={{ mt: '0px', mb: '$1', textAlign: 'left', lineHeight: 'normal' }}
|
||||
>
|
||||
{props.item.title}
|
||||
</StyledText>
|
||||
{props.item.author && (
|
||||
<StyledText style="caption" css={{ my: '$1' }}>
|
||||
<StyledText style="caption" css={{ my: '$1', ml: '8px' }}>
|
||||
{authoredByText(props.item.author)}
|
||||
</StyledText>
|
||||
)}
|
||||
<StyledText style="caption" css={{ my: '$1' }}>
|
||||
<StyledText style="caption" css={{ my: '$1', ml: '8px', textDecorationLine: 'underline' }}>
|
||||
{originText}
|
||||
</StyledText>
|
||||
</VStack>
|
||||
</HStack>
|
||||
<Box
|
||||
css={{ alignSelf: 'end', alignItems: 'start', height: '100%' }}
|
||||
css={{
|
||||
width: '40px',
|
||||
height: '8px',
|
||||
mr: '$2',
|
||||
backgroundColor: "$grayBase",
|
||||
display: 'grid',
|
||||
placeItems: 'center',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid $grayBorder',
|
||||
px: '1px'
|
||||
}}
|
||||
>
|
||||
<ProgressBar
|
||||
fillPercentage={props.item.readingProgressPercent}
|
||||
fillColor={theme.colors.highlight.toString()}
|
||||
backgroundColor={theme.colors.grayTextContrast.toString()}
|
||||
borderRadius={'8px'}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
css={{
|
||||
alignSelf: 'end',
|
||||
alignItems: 'center',
|
||||
display: 'grid',
|
||||
placeItems: 'center',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
// This is here to prevent menu click events from bubbling
|
||||
// up and causing us to "click" on the link item.
|
||||
|
|
@ -223,19 +307,13 @@ export function ListLinkedItemCard(props: LinkedItemCardProps): JSX.Element {
|
|||
<MoreOptionsIcon
|
||||
size={24}
|
||||
strokeColor={theme.colors.grayTextContrast.toString()}
|
||||
orientation="vertical"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
}
|
||||
actionHandler={props.handleAction}
|
||||
/>
|
||||
</Box>
|
||||
</HStack>
|
||||
<ProgressBar
|
||||
fillPercentage={props.item.readingProgressPercent}
|
||||
fillColor={theme.colors.highlight.toString()}
|
||||
backgroundColor={theme.colors.grayTextContrast.toString()}
|
||||
/>
|
||||
</VStack>
|
||||
// </Link>
|
||||
)
|
||||
}
|
||||
|
|
@ -244,6 +322,7 @@ type ProgressBarProps = {
|
|||
fillPercentage: number
|
||||
fillColor: string
|
||||
backgroundColor: string
|
||||
borderRadius: string
|
||||
}
|
||||
|
||||
function ProgressBar(props: ProgressBarProps): JSX.Element {
|
||||
|
|
@ -253,9 +332,7 @@ function ProgressBar(props: ProgressBarProps): JSX.Element {
|
|||
height: '4px',
|
||||
width: '100%',
|
||||
borderRadius: '$1',
|
||||
backgroundColor: props.backgroundColor,
|
||||
overflow: 'hidden',
|
||||
mt: '$1',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
|
|
@ -263,6 +340,7 @@ function ProgressBar(props: ProgressBarProps): JSX.Element {
|
|||
height: '100%',
|
||||
width: `${props.fillPercentage}%`,
|
||||
backgroundColor: props.fillColor,
|
||||
borderRadius: props.borderRadius,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { Box, HStack, VStack } from './../../elements/LayoutPrimitives'
|
||||
import { useGetLibraryItemsQuery } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { isDarkTheme } from '../../../lib/themeUpdater'
|
||||
import type {
|
||||
LibraryItem,
|
||||
LibraryItemsQueryInput,
|
||||
} from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { useGetLibraryItemsQuery } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import {
|
||||
LinkedItemCard,
|
||||
LinkedItemCardAction,
|
||||
|
|
@ -532,15 +533,11 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
|||
gridAutoRows: 'auto',
|
||||
borderRadius: '8px',
|
||||
gridGap: layout == 'LIST_LAYOUT' ? '0' : '$3',
|
||||
border:
|
||||
props.hasData && layout == 'LIST_LAYOUT'
|
||||
? '1px solid $grayBorder'
|
||||
: 'none',
|
||||
marginTop: layout == 'LIST_LAYOUT' ? '21px' : '0',
|
||||
marginBottom: '0px',
|
||||
paddingTop: layout == 'LIST_LAYOUT' ? '2px' : '21px',
|
||||
paddingTop: layout == 'LIST_LAYOUT' ? '0' : '21px',
|
||||
paddingBottom: layout == 'LIST_LAYOUT' ? '0px' : '21px',
|
||||
overflow: 'visible',
|
||||
overflow: 'hidden',
|
||||
'@smDown': {
|
||||
border: 'unset',
|
||||
width: layout == 'LIST_LAYOUT' ? '100vw' : undefined,
|
||||
|
|
@ -556,24 +553,6 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
|||
},
|
||||
}}
|
||||
>
|
||||
{props.hasData && layout === 'LIST_LAYOUT' && (
|
||||
// list view gets a title
|
||||
<Box
|
||||
css={{
|
||||
height: '42px',
|
||||
paddingLeft: '9px',
|
||||
paddingBottom: '8px',
|
||||
'@smDown': { height: '20px' },
|
||||
}}
|
||||
>
|
||||
<StyledText
|
||||
style="caption"
|
||||
css={{ '@smDown': { visibility: 'collapse' } }}
|
||||
>
|
||||
{props.totalItems} links
|
||||
</StyledText>
|
||||
</Box>
|
||||
)}
|
||||
{props.items.map((linkedItem) => (
|
||||
<Box
|
||||
className="linkedItemCard"
|
||||
|
|
@ -581,18 +560,19 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
|||
tabIndex={0}
|
||||
key={linkedItem.node.id}
|
||||
css={{
|
||||
width: '100%',
|
||||
'&> div': {
|
||||
bg: '$grayBg',
|
||||
},
|
||||
'&:focus': {
|
||||
'> div': {
|
||||
bg: '$grayBgActive',
|
||||
},
|
||||
boxShadow: isDarkTheme() ? '$cardItemDarkShadow' : '$cardItemLightShadow',
|
||||
}
|
||||
},
|
||||
'&:hover': {
|
||||
'> div': {
|
||||
bg: '$grayBgActive',
|
||||
},
|
||||
'> div.grid-item-card': {
|
||||
boxShadow: isDarkTheme() ? '$cardItemDarkShadow' : '$cardItemLightShadow',
|
||||
}
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -111,6 +111,8 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
|
|||
borderStyles: {},
|
||||
shadows: {
|
||||
panelShadow: '0px 4px 18px rgba(120, 123, 134, 0.12)',
|
||||
cardItemLightShadow: '0px 8px 32px rgba(32, 31, 29, 0.33)',
|
||||
cardItemDarkShadow: '0px 8px 32px rgba(255, 255, 255, 0.33)',
|
||||
},
|
||||
zIndices: {},
|
||||
transitions: {},
|
||||
|
|
|
|||
Loading…
Reference in a new issue