mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #2571 from omnivore-app/fix/web-library-grid-card-progress
Add progress to library grid cards
This commit is contained in:
commit
aab8f459b3
24 changed files with 2048 additions and 325 deletions
|
|
@ -1400,7 +1400,7 @@
|
|||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 1.28.0;
|
||||
MARKETING_VERSION = 1.29.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app;
|
||||
|
|
@ -1435,7 +1435,7 @@
|
|||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 1.28.0;
|
||||
MARKETING_VERSION = 1.29.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
@ -1490,7 +1490,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.28.0;
|
||||
MARKETING_VERSION = 1.29.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app;
|
||||
PRODUCT_NAME = Omnivore;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1831,7 +1831,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.28.0;
|
||||
MARKETING_VERSION = 1.29.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app;
|
||||
PRODUCT_NAME = Omnivore;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
|
|||
|
|
@ -275,6 +275,14 @@ struct WebReaderContainerView: View {
|
|||
Spacer()
|
||||
#endif
|
||||
|
||||
Button(
|
||||
action: { showLabelsModal = true },
|
||||
label: {
|
||||
Image.label
|
||||
}
|
||||
)
|
||||
.padding(.trailing, 4)
|
||||
|
||||
Button(
|
||||
action: { showNotebookView = true },
|
||||
label: {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export function LabelChip(props: LabelChipProps): JSX.Element {
|
|||
const isDark = isDarkTheme()
|
||||
|
||||
const selectedBorder = isDark ? '#FFEA9F' : 'black'
|
||||
const unSelectedBorder = isDark ? '#2A2A2A' : '#D9D9D9'
|
||||
const unSelectedBorder = isDark ? '#2A2A2A' : '#F5F5F5'
|
||||
|
||||
return (
|
||||
<SpanBox
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@ export function ProgressBar(props: ProgressBarProps): JSX.Element {
|
|||
return (
|
||||
<Box
|
||||
css={{
|
||||
height: '4px',
|
||||
height: '5px',
|
||||
width: '100%',
|
||||
borderRadius: '$1',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: props.backgroundColor,
|
||||
}}
|
||||
|
|
@ -23,7 +22,6 @@ export function ProgressBar(props: ProgressBarProps): JSX.Element {
|
|||
height: '100%',
|
||||
width: `${props.fillPercentage}%`,
|
||||
backgroundColor: props.fillColor,
|
||||
borderRadius: props.borderRadius,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
import { Box } from './../elements/LayoutPrimitives'
|
||||
|
||||
type ProgressBarVProps = {
|
||||
fillPercentage: number
|
||||
fillColor: string
|
||||
backgroundColor: string
|
||||
borderRadius: string
|
||||
height?: string
|
||||
}
|
||||
|
||||
export function ProgressBarVertical(props: ProgressBarVProps): JSX.Element {
|
||||
return (
|
||||
<Box
|
||||
css={{
|
||||
height: props.height ?? '100%',
|
||||
width: '2px',
|
||||
borderRadius: '$1',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: props.backgroundColor,
|
||||
mr: '12px',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
css={{
|
||||
height: `${props.fillPercentage}%`,
|
||||
width: '100%',
|
||||
backgroundColor: props.fillColor,
|
||||
borderRadius: props.borderRadius,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
29
packages/web/components/elements/icons/CaretDownIcon.tsx
Normal file
29
packages/web/components/elements/icons/CaretDownIcon.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from './IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class CaretDownIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const size = (this.props.size || 26).toString()
|
||||
const color = (this.props.color || '#2A2A2A').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 9 9"
|
||||
fill="none"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M7.0618 1.19531C7.21941 1.19641 7.37452 1.23475 7.51449 1.30719C7.65446 1.37964 7.77533 1.48414 7.86723 1.61218C7.95913 1.74022 8.01947 1.88817 8.04332 2.04396C8.06717 2.19975 8.05386 2.35898 8.00447 2.50865L7.9818 2.57031L7.96114 2.61231L5.59781 6.68998C5.51439 6.84091 5.39337 6.9677 5.24649 7.05804C5.09961 7.14839 4.93186 7.19921 4.75954 7.20559C4.58721 7.21196 4.41617 7.17366 4.26302 7.09442C4.10986 7.01517 3.9798 6.89766 3.88547 6.75331L3.85114 6.69565L1.48447 2.61231L1.46414 2.57065C1.40411 2.42286 1.3803 2.26285 1.39471 2.10399C1.40912 1.94513 1.46132 1.79202 1.54696 1.65744C1.6326 1.52287 1.74919 1.41073 1.887 1.3304C2.02481 1.25006 2.17984 1.20386 2.33914 1.19565L2.35847 1.19531L2.37847 1.19598L2.3928 1.19531H7.0618Z"
|
||||
fill={color}
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { LayoutType } from '../../templates/homeFeed/HomeFeedContainer'
|
||||
import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import type { LibraryItemNode } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { MultiSelectMode } from '../../templates/homeFeed/LibraryHeader'
|
||||
|
||||
export type LinkedItemCardAction =
|
||||
| 'showDetail'
|
||||
|
|
@ -23,9 +24,10 @@ export type LinkedItemCardProps = {
|
|||
|
||||
handleAction: (action: LinkedItemCardAction) => void
|
||||
|
||||
inMultiSelect: boolean
|
||||
isChecked: boolean
|
||||
setIsChecked: (itemId: string, set: boolean) => void
|
||||
|
||||
multiSelectMode: MultiSelectMode
|
||||
|
||||
isHovered?: boolean
|
||||
}
|
||||
|
|
|
|||
1575
packages/web/components/patterns/LibraryCards/FallbackImage.tsx
Normal file
1575
packages/web/components/patterns/LibraryCards/FallbackImage.tsx
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +1,8 @@
|
|||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { useMemo } from 'react'
|
||||
import { ChangeEvent, useMemo } from 'react'
|
||||
import { LibraryItemNode } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { Box, SpanBox } from '../../elements/LayoutPrimitives'
|
||||
import { Box, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ export const MenuStyle = {
|
|||
export const MetaStyle = {
|
||||
width: '100%',
|
||||
color: '$thTextSubtle3',
|
||||
fontSize: '13px',
|
||||
fontSize: '12px',
|
||||
fontWeight: '400',
|
||||
fontFamily: '$display',
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ export const TitleStyle = {
|
|||
color: '$thTextContrast2',
|
||||
fontSize: '16px',
|
||||
fontWeight: '700',
|
||||
lineHeight: '1.25',
|
||||
lineHeight: '1',
|
||||
fontFamily: '$display',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
|
|
@ -67,11 +67,11 @@ export const AuthorInfoStyle = {
|
|||
whiteSpace: 'nowrap',
|
||||
maxWidth: '240px',
|
||||
overflow: 'hidden',
|
||||
height: '21px',
|
||||
color: '$thTextSubtle3',
|
||||
fontSize: '12px',
|
||||
fontWeight: '400',
|
||||
fontFamily: '$display',
|
||||
lineHeight: '1',
|
||||
}
|
||||
|
||||
export const timeAgo = (date: string | undefined): string => {
|
||||
|
|
@ -134,14 +134,6 @@ export function LibraryItemMetadata(
|
|||
Math.round((props.item.wordsCount ?? 0) / 235)
|
||||
)} min read`
|
||||
: null}
|
||||
{(props.showProgress && props.item.readingProgressPercent) ?? 0 > 0 ? (
|
||||
<>
|
||||
{` • `}
|
||||
<SpanBox css={{ color: '#55B938' }}>
|
||||
{`${Math.round(props.item.readingProgressPercent)}%`}
|
||||
</SpanBox>
|
||||
</>
|
||||
) : null}
|
||||
{highlightCount > 0
|
||||
? ` • ${highlightCount} highlight${highlightCount > 1 ? 's' : ''}`
|
||||
: null}
|
||||
|
|
@ -156,10 +148,19 @@ type CardCheckBoxProps = {
|
|||
|
||||
export function CardCheckbox(props: CardCheckBoxProps): JSX.Element {
|
||||
return (
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={props.isChecked}
|
||||
onChange={props.handleChanged}
|
||||
></input>
|
||||
<form
|
||||
// This prevents us from propogating up the the <a element on cards
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={props.isChecked}
|
||||
onChange={(event) => {
|
||||
props.handleChanged()
|
||||
}}
|
||||
></input>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import Link from 'next/link'
|
|||
import {
|
||||
AuthorInfoStyle,
|
||||
CardCheckbox,
|
||||
DescriptionStyle,
|
||||
LibraryItemMetadata,
|
||||
MetaStyle,
|
||||
siteName,
|
||||
|
|
@ -29,39 +28,11 @@ import {
|
|||
import { CardMenu } from '../CardMenu'
|
||||
import { DotsThree } from 'phosphor-react'
|
||||
import { isTouchScreenDevice } from '../../../lib/deviceType'
|
||||
import { ProgressBarOverlay } from './LibraryListCard'
|
||||
import { FallbackImage } from './FallbackImage'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
type ProgressBarProps = {
|
||||
fillPercentage: number
|
||||
fillColor: string
|
||||
backgroundColor: string
|
||||
borderRadius: string
|
||||
}
|
||||
|
||||
export function ProgressBar(props: ProgressBarProps): JSX.Element {
|
||||
return (
|
||||
<Box
|
||||
css={{
|
||||
height: '4px',
|
||||
width: '100%',
|
||||
borderRadius: '$1',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: props.backgroundColor,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
css={{
|
||||
height: '100%',
|
||||
width: `${props.fillPercentage}%`,
|
||||
backgroundColor: props.fillColor,
|
||||
borderRadius: props.borderRadius,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element {
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
|
@ -115,38 +86,32 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element {
|
|||
setIsHovered(false)
|
||||
}}
|
||||
>
|
||||
{props.inMultiSelect ? (
|
||||
<LibraryGridCardContent {...props} isHovered={isHovered} />
|
||||
) : (
|
||||
<>
|
||||
{!isTouchScreenDevice() && (
|
||||
<Box
|
||||
ref={refs.setFloating}
|
||||
style={{ ...floatingStyles, zIndex: 10 }}
|
||||
{...getFloatingProps()}
|
||||
>
|
||||
<LibraryHoverActions
|
||||
item={props.item}
|
||||
viewer={props.viewer}
|
||||
handleAction={props.handleAction}
|
||||
isHovered={isHovered ?? false}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<Link
|
||||
href={`${props.viewer.profile.username}/${props.item.slug}`}
|
||||
passHref
|
||||
>
|
||||
<a
|
||||
href={`${props.viewer.profile.username}/${props.item.slug}`}
|
||||
style={{ textDecoration: 'unset', width: '100%', height: '100%' }}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<LibraryGridCardContent {...props} isHovered={isHovered} />
|
||||
</a>
|
||||
</Link>
|
||||
</>
|
||||
{!isTouchScreenDevice() && (
|
||||
<Box
|
||||
ref={refs.setFloating}
|
||||
style={{ ...floatingStyles, zIndex: 10 }}
|
||||
{...getFloatingProps()}
|
||||
>
|
||||
<LibraryHoverActions
|
||||
item={props.item}
|
||||
viewer={props.viewer}
|
||||
handleAction={props.handleAction}
|
||||
isHovered={isHovered ?? false}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<Link
|
||||
href={`${props.viewer.profile.username}/${props.item.slug}`}
|
||||
passHref
|
||||
>
|
||||
<a
|
||||
href={`${props.viewer.profile.username}/${props.item.slug}`}
|
||||
style={{ textDecoration: 'unset', width: '100%', height: '100%' }}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<LibraryGridCardContent {...props} isHovered={isHovered} />
|
||||
</a>
|
||||
</Link>
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
|
@ -189,25 +154,43 @@ const Fallback = (props: FallbackProps): JSX.Element => {
|
|||
type GridImageProps = {
|
||||
src?: string
|
||||
title?: string
|
||||
readingProgress?: number
|
||||
}
|
||||
|
||||
const GridImage = (props: GridImageProps): JSX.Element => {
|
||||
const [displayFallback, setDisplayFallback] = useState(props.src == undefined)
|
||||
|
||||
return displayFallback ? (
|
||||
<Fallback title={props.title ?? 'Omnivore Fallback'} />
|
||||
) : (
|
||||
<CoverImage
|
||||
src={props.src}
|
||||
width="100%"
|
||||
height={100}
|
||||
css={{
|
||||
bg: '$thBackground',
|
||||
}}
|
||||
onError={(e) => {
|
||||
setDisplayFallback(true)
|
||||
}}
|
||||
/>
|
||||
return (
|
||||
<>
|
||||
{(props.readingProgress ?? 0) > 0 && (
|
||||
<ProgressBarOverlay
|
||||
width="100%"
|
||||
top={95}
|
||||
value={props.readingProgress ?? 0}
|
||||
bottomRadius={'0px'}
|
||||
/>
|
||||
)}
|
||||
{displayFallback ? (
|
||||
<FallbackImage
|
||||
title={props.title ?? 'Omnivore Fallback'}
|
||||
width="100%"
|
||||
height="100px"
|
||||
fontSize="128px"
|
||||
/>
|
||||
) : (
|
||||
<CoverImage
|
||||
src={props.src}
|
||||
width="100%"
|
||||
height={100}
|
||||
css={{
|
||||
bg: '$thBackground',
|
||||
}}
|
||||
onError={(e) => {
|
||||
setDisplayFallback(true)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -217,45 +200,55 @@ const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => {
|
|||
const originText = siteName(props.item.originalArticleUrl, props.item.url)
|
||||
|
||||
const handleCheckChanged = useCallback(() => {
|
||||
setIsChecked(item.id, !isChecked)
|
||||
}, [setIsChecked, isChecked])
|
||||
const newValue = !isChecked
|
||||
setIsChecked(item.id, newValue)
|
||||
}, [setIsChecked, isChecked, props])
|
||||
|
||||
return (
|
||||
<VStack css={{ p: '0px', m: '0px', width: '100%' }}>
|
||||
<Box css={{ position: 'relative', width: '100%' }}>
|
||||
<GridImage src={props.item.image} title={props.item.title} />
|
||||
{props.inMultiSelect ? (
|
||||
<SpanBox css={{ position: 'absolute', top: 0, left: 0, m: '5px' }}>
|
||||
<CardCheckbox
|
||||
isChecked={props.isChecked}
|
||||
handleChanged={handleCheckChanged}
|
||||
/>
|
||||
</SpanBox>
|
||||
) : (
|
||||
<Box
|
||||
css={{
|
||||
...MenuStyle,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
m: '5px',
|
||||
visibility: menuOpen ? 'visible' : 'hidden',
|
||||
'@media (hover: none)': {
|
||||
visibility: 'unset',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CardMenu
|
||||
item={props.item}
|
||||
viewer={props.viewer}
|
||||
onOpenChange={(open) => setMenuOpen(open)}
|
||||
actionHandler={props.handleAction}
|
||||
triggerElement={
|
||||
<DotsThree size={25} weight="bold" color="#ADADAD" />
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<GridImage
|
||||
src={props.item.image}
|
||||
title={props.item.title}
|
||||
readingProgress={item.readingProgressPercent}
|
||||
/>
|
||||
<SpanBox
|
||||
css={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
m: '10px',
|
||||
lineHeight: '1',
|
||||
}}
|
||||
>
|
||||
<CardCheckbox
|
||||
isChecked={isChecked}
|
||||
handleChanged={handleCheckChanged}
|
||||
/>
|
||||
</SpanBox>
|
||||
<Box
|
||||
css={{
|
||||
...MenuStyle,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
m: '5px',
|
||||
visibility: menuOpen ? 'visible' : 'hidden',
|
||||
'@media (hover: none)': {
|
||||
visibility: 'unset',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CardMenu
|
||||
item={props.item}
|
||||
viewer={props.viewer}
|
||||
onOpenChange={(open) => setMenuOpen(open)}
|
||||
actionHandler={props.handleAction}
|
||||
triggerElement={
|
||||
<DotsThree size={25} weight="bold" color="#ADADAD" />
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<HStack
|
||||
|
|
@ -268,7 +261,7 @@ const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => {
|
|||
}}
|
||||
distribution="start"
|
||||
>
|
||||
<LibraryItemMetadata item={props.item} />
|
||||
<LibraryItemMetadata item={props.item} showProgress={true} />
|
||||
</HStack>
|
||||
|
||||
<VStack
|
||||
|
|
@ -299,7 +292,7 @@ const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => {
|
|||
<HStack
|
||||
distribution="start"
|
||||
alignment="start"
|
||||
css={{ width: '100%', minHeight: '50px' }}
|
||||
css={{ width: '100%', minHeight: '50px', pb: '10px' }}
|
||||
>
|
||||
<HStack
|
||||
css={{
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ export function LibraryHighlightGridCard(
|
|||
showErrorToast('Error navigating to highlight')
|
||||
return
|
||||
}
|
||||
console.log('pushing user: ', props.viewer, 'slug: ', props.item.slug)
|
||||
router.push(
|
||||
{
|
||||
pathname: '/[username]/[slug]',
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ type LibraryHoverActionsProps = {
|
|||
export const LibraryHoverActions = (props: LibraryHoverActionsProps) => {
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
|
||||
console.log(' props.isHovered || menuOpen', props.isHovered, menuOpen)
|
||||
|
||||
return (
|
||||
<Box
|
||||
css={{
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ import {
|
|||
import { CardMenu } from '../CardMenu'
|
||||
import { DotsThree } from 'phosphor-react'
|
||||
import { isTouchScreenDevice } from '../../../lib/deviceType'
|
||||
import { CoverImage } from '../../elements/CoverImage'
|
||||
import { ProgressBar } from '../../elements/ProgressBar'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { FallbackImage } from './FallbackImage'
|
||||
|
||||
export function LibraryListCard(props: LinkedItemCardProps): JSX.Element {
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
|
|
@ -55,6 +59,7 @@ export function LibraryListCard(props: LinkedItemCardProps): JSX.Element {
|
|||
{...getReferenceProps()}
|
||||
css={{
|
||||
px: '20px',
|
||||
pl: '10px',
|
||||
pt: '20px',
|
||||
pb: '20px',
|
||||
height: '100%',
|
||||
|
|
@ -92,42 +97,112 @@ export function LibraryListCard(props: LinkedItemCardProps): JSX.Element {
|
|||
setIsHovered(false)
|
||||
}}
|
||||
>
|
||||
{props.inMultiSelect ? (
|
||||
<LibraryListCardContent {...props} isHovered={isHovered} />
|
||||
) : (
|
||||
<>
|
||||
{!isTouchScreenDevice() && (
|
||||
<Box
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
{...getFloatingProps()}
|
||||
>
|
||||
<LibraryHoverActions
|
||||
item={props.item}
|
||||
viewer={props.viewer}
|
||||
handleAction={props.handleAction}
|
||||
isHovered={isHovered ?? false}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<Link
|
||||
href={`${props.viewer.profile.username}/${props.item.slug}`}
|
||||
passHref
|
||||
>
|
||||
<a
|
||||
href={`${props.viewer.profile.username}/${props.item.slug}`}
|
||||
style={{ textDecoration: 'unset', width: '100%', height: '100%' }}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<LibraryListCardContent {...props} isHovered={isHovered} />
|
||||
</a>
|
||||
</Link>
|
||||
</>
|
||||
{!isTouchScreenDevice() && (
|
||||
<Box
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
{...getFloatingProps()}
|
||||
>
|
||||
<LibraryHoverActions
|
||||
item={props.item}
|
||||
viewer={props.viewer}
|
||||
handleAction={props.handleAction}
|
||||
isHovered={isHovered ?? false}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<Link
|
||||
href={`${props.viewer.profile.username}/${props.item.slug}`}
|
||||
passHref
|
||||
>
|
||||
<a
|
||||
href={`${props.viewer.profile.username}/${props.item.slug}`}
|
||||
style={{ textDecoration: 'unset', width: '100%', height: '100%' }}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<LibraryListCardContent {...props} isHovered={isHovered} />
|
||||
</a>
|
||||
</Link>
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
||||
type ProgressBarOverlayProps = {
|
||||
top: number
|
||||
width: string
|
||||
value: number
|
||||
bottomRadius: string
|
||||
}
|
||||
|
||||
export const ProgressBarOverlay = (
|
||||
props: ProgressBarOverlayProps
|
||||
): JSX.Element => {
|
||||
return (
|
||||
<Box
|
||||
css={{
|
||||
position: 'absolute',
|
||||
width: props.width,
|
||||
top: props.top,
|
||||
borderBottomLeftRadius: props.bottomRadius,
|
||||
borderBottomRightRadius: props.bottomRadius,
|
||||
overflow: 'clip',
|
||||
zIndex: 20,
|
||||
}}
|
||||
>
|
||||
<ProgressBar
|
||||
fillPercentage={props.value}
|
||||
fillColor={theme.colors.thProgressFg.toString()}
|
||||
backgroundColor="rgba(217, 217, 217, 0.65)"
|
||||
borderRadius={'2px'}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
type ListImageProps = {
|
||||
src?: string
|
||||
title?: string
|
||||
readingProgress?: number
|
||||
}
|
||||
|
||||
const ListImage = (props: ListImageProps): JSX.Element => {
|
||||
const [displayFallback, setDisplayFallback] = useState(props.src == undefined)
|
||||
|
||||
return (
|
||||
<>
|
||||
{(props.readingProgress ?? 0) > 0 && (
|
||||
<ProgressBarOverlay
|
||||
width="55px"
|
||||
top={50}
|
||||
bottomRadius="4px"
|
||||
value={props.readingProgress ?? 0}
|
||||
/>
|
||||
)}
|
||||
{displayFallback ? (
|
||||
<FallbackImage
|
||||
title={props.title ?? 'Omnivore Fallback'}
|
||||
width="55px"
|
||||
height="55px"
|
||||
fontSize="36pt"
|
||||
/>
|
||||
) : (
|
||||
<CoverImage
|
||||
src={props.src}
|
||||
width={55}
|
||||
height={55}
|
||||
css={{
|
||||
bg: '$thBackground',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
onError={() => {
|
||||
setDisplayFallback(true)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function LibraryListCardContent(
|
||||
props: LinkedItemCardProps
|
||||
): JSX.Element {
|
||||
|
|
@ -140,57 +215,96 @@ export function LibraryListCardContent(
|
|||
}, [isChecked, setIsChecked, item])
|
||||
|
||||
return (
|
||||
<>
|
||||
<HStack css={MetaStyle} distribution="start">
|
||||
<LibraryItemMetadata item={props.item} showProgress={true} />
|
||||
{props.inMultiSelect ? (
|
||||
<SpanBox css={{ marginLeft: 'auto' }}>
|
||||
<CardCheckbox
|
||||
isChecked={props.isChecked}
|
||||
handleChanged={handleCheckChanged}
|
||||
/>
|
||||
</SpanBox>
|
||||
) : (
|
||||
<Box
|
||||
css={{
|
||||
...MenuStyle,
|
||||
visibility: menuOpen ? 'visible' : 'hidden',
|
||||
'@media (hover: none)': {
|
||||
visibility: 'unset',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CardMenu
|
||||
item={props.item}
|
||||
viewer={props.viewer}
|
||||
onOpenChange={(open) => setMenuOpen(open)}
|
||||
actionHandler={props.handleAction}
|
||||
triggerElement={
|
||||
<DotsThree size={25} weight="bold" color="#ADADAD" />
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</HStack>
|
||||
<HStack css={{ gap: '15px', width: '100%' }}>
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'flex',
|
||||
m: '0px',
|
||||
mt: '0px',
|
||||
p: '0px',
|
||||
ml: '4px',
|
||||
lineHeight: '1',
|
||||
'> input': {
|
||||
p: '0px',
|
||||
m: '0px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CardCheckbox
|
||||
isChecked={props.isChecked}
|
||||
handleChanged={handleCheckChanged}
|
||||
/>
|
||||
</SpanBox>
|
||||
<Box css={{ position: 'relative', width: '55px' }}>
|
||||
<ListImage
|
||||
src={props.item.image}
|
||||
title={props.item.title}
|
||||
readingProgress={item.readingProgressPercent}
|
||||
/>
|
||||
|
||||
<Box
|
||||
css={{
|
||||
...MenuStyle,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
m: '5px',
|
||||
visibility: menuOpen ? 'visible' : 'hidden',
|
||||
'@media (hover: none)': {
|
||||
visibility: 'unset',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CardMenu
|
||||
item={props.item}
|
||||
viewer={props.viewer}
|
||||
onOpenChange={(open) => setMenuOpen(open)}
|
||||
actionHandler={props.handleAction}
|
||||
triggerElement={
|
||||
<DotsThree size={25} weight="bold" color="#ADADAD" />
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<VStack
|
||||
alignment="start"
|
||||
distribution="start"
|
||||
css={{ height: '100%', width: '100%' }}
|
||||
css={{ height: '100%', width: '100%', lineHeight: 1, gap: '5px' }}
|
||||
>
|
||||
<Box css={{ ...TitleStyle, fontSize: '18px', width: '80%' }}>
|
||||
{props.item.title}
|
||||
</Box>
|
||||
<SpanBox
|
||||
<VStack
|
||||
alignment="start"
|
||||
distribution="center"
|
||||
css={{
|
||||
mt: '5px',
|
||||
...AuthorInfoStyle,
|
||||
maxWidth: '90%',
|
||||
height: '55px',
|
||||
width: '100%',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
{props.item.author}
|
||||
{props.item.author && originText && ' | '}
|
||||
<SpanBox css={{ textDecoration: 'underline' }}>{originText}</SpanBox>
|
||||
</SpanBox>
|
||||
<HStack
|
||||
css={{
|
||||
...MetaStyle,
|
||||
color: '$grayText',
|
||||
}}
|
||||
distribution="start"
|
||||
>
|
||||
<LibraryItemMetadata item={props.item} showProgress={true} />
|
||||
</HStack>
|
||||
|
||||
<Box css={{ ...TitleStyle, width: '80%' }}>{props.item.title}</Box>
|
||||
<SpanBox
|
||||
css={{
|
||||
...AuthorInfoStyle,
|
||||
maxWidth: '90%',
|
||||
minHeight: '12px',
|
||||
}}
|
||||
>
|
||||
{props.item.author}
|
||||
{props.item.author && originText && ' | '}
|
||||
<SpanBox css={{ textDecoration: 'underline' }}>
|
||||
{originText}
|
||||
</SpanBox>
|
||||
</SpanBox>
|
||||
</VStack>
|
||||
|
||||
<HStack
|
||||
distribution="start"
|
||||
|
|
@ -209,6 +323,6 @@ export function LibraryListCardContent(
|
|||
</HStack>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</>
|
||||
</HStack>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,6 @@ export function UploadModal(props: UploadModalProps): JSX.Element {
|
|||
|
||||
Papa.parse(file.file, {
|
||||
step: function (row, parser) {
|
||||
console.log('row: ', row)
|
||||
if (Array.isArray(row.data)) {
|
||||
try {
|
||||
if (row.data[0].trim().length < 1) {
|
||||
|
|
@ -211,7 +210,6 @@ export function UploadModal(props: UploadModalProps): JSX.Element {
|
|||
;(async () => {
|
||||
for (const file of addedFiles) {
|
||||
try {
|
||||
console.log('using content type: ', file.file.type)
|
||||
const uploadInfo = await uploadSignedUrlForFile(file)
|
||||
if (!uploadInfo.uploadSignedUrl) {
|
||||
showErrorToast('No upload URL available')
|
||||
|
|
|
|||
|
|
@ -192,7 +192,6 @@ export function Article(props: ArticleProps): JSX.Element {
|
|||
const img = element as HTMLImageElement
|
||||
const width = Number(img.getAttribute('data-omnivore-width'))
|
||||
const height = Number(img.getAttribute('data-omnivore-height'))
|
||||
console.log('width and height: ', width, height)
|
||||
|
||||
if (!isNaN(width) && !isNaN(height) && width < 100 && height < 100) {
|
||||
img.style.setProperty('width', `${width}px`)
|
||||
|
|
|
|||
|
|
@ -348,7 +348,9 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
|
|||
default: styles.maxWidthPercentage
|
||||
? `${styles.maxWidthPercentage}%`
|
||||
: 1024 - styles.margin,
|
||||
small: styles.maxWidthPercentage ? `${styles.maxWidthPercentage}%` : `${120 - Math.round((styles.margin * 10) / 100)}%`,
|
||||
small: styles.maxWidthPercentage
|
||||
? `${styles.maxWidthPercentage}%`
|
||||
: `${120 - Math.round((styles.margin * 10) / 100)}%`,
|
||||
}
|
||||
|
||||
const recommendationsWithNotes = useMemo(() => {
|
||||
|
|
|
|||
|
|
@ -297,7 +297,6 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
|||
const { target, pageX, pageY } = event
|
||||
|
||||
if (!target || (target as Node)?.nodeType !== Node.ELEMENT_NODE) {
|
||||
console.log(' -- returning early from page tap')
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -375,7 +374,6 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
|||
highlightIdAttribute
|
||||
)
|
||||
const highlight = highlights.find(($0) => $0.id === id)
|
||||
console.log('double tapped highlight: ', highlight)
|
||||
setFocusedHighlight(highlight)
|
||||
|
||||
openNoteModal({
|
||||
|
|
@ -387,8 +385,6 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
|||
highlightNoteIdAttribute
|
||||
)
|
||||
const highlight = highlights.find(($0) => $0.id === id)
|
||||
console.log('double tapped highlight with note: ', highlight)
|
||||
|
||||
setFocusedHighlight(highlight)
|
||||
|
||||
openNoteModal({
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element {
|
|||
)
|
||||
|
||||
const createNote = useCallback((text: string) => {
|
||||
console.log('creating note: ', newNoteId, noteState.current.isCreating)
|
||||
noteState.current.isCreating = true
|
||||
noteState.current.createStarted = new Date()
|
||||
;(async () => {
|
||||
|
|
|
|||
|
|
@ -84,11 +84,13 @@ export function HomeFeedContainer(): JSX.Element {
|
|||
|
||||
const gridContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [labelsTarget, setLabelsTarget] =
|
||||
useState<LibraryItem | undefined>(undefined)
|
||||
const [labelsTarget, setLabelsTarget] = useState<LibraryItem | undefined>(
|
||||
undefined
|
||||
)
|
||||
|
||||
const [notebookTarget, setNotebookTarget] =
|
||||
useState<LibraryItem | undefined>(undefined)
|
||||
const [notebookTarget, setNotebookTarget] = useState<LibraryItem | undefined>(
|
||||
undefined
|
||||
)
|
||||
|
||||
const [showAddLinkModal, setShowAddLinkModal] = useState(false)
|
||||
const [showEditTitleModal, setShowEditTitleModal] = useState(false)
|
||||
|
|
@ -372,15 +374,12 @@ export function HomeFeedContainer(): JSX.Element {
|
|||
const [multiSelectMode, setMultiSelectMode] = useState<MultiSelectMode>('off')
|
||||
|
||||
const selectActiveArticle = useCallback(() => {
|
||||
console.log('selecting article: ', activeItem)
|
||||
if (activeItem) {
|
||||
if (multiSelectMode === 'off') {
|
||||
console.log('setting ')
|
||||
setMultiSelectMode('some')
|
||||
}
|
||||
const itemId = activeItem.node.id
|
||||
const isChecked = itemIsChecked(itemId)
|
||||
console.log('setting is checked: ', isChecked, itemId)
|
||||
setIsChecked(itemId, !isChecked)
|
||||
}
|
||||
}, [activeItem, multiSelectMode, checkedItems])
|
||||
|
|
@ -493,7 +492,6 @@ export function HomeFeedContainer(): JSX.Element {
|
|||
handleCardAction('set-labels', activeItem)
|
||||
break
|
||||
case 'openNotebook':
|
||||
console.log('openNotebook: ', notebookTarget)
|
||||
handleCardAction('open-notebook', activeItem)
|
||||
break
|
||||
case 'sortDescending':
|
||||
|
|
@ -553,7 +551,6 @@ export function HomeFeedContainer(): JSX.Element {
|
|||
name: 'Mark item as read',
|
||||
shortcut: ['m', 'r'],
|
||||
perform: () => {
|
||||
console.log('mark read action')
|
||||
handleCardAction('mark-read', activeItem)
|
||||
},
|
||||
}),
|
||||
|
|
@ -612,8 +609,16 @@ export function HomeFeedContainer(): JSX.Element {
|
|||
checkedItems.splice(checkedItems.indexOf(itemId), 1)
|
||||
setCheckedItems([...checkedItems])
|
||||
}
|
||||
|
||||
if (set && multiSelectMode == 'off') {
|
||||
setMultiSelectMode('some')
|
||||
}
|
||||
|
||||
if (checkedItems.length < 1) {
|
||||
setMultiSelectMode('off')
|
||||
}
|
||||
},
|
||||
[checkedItems]
|
||||
[checkedItems, multiSelectMode, setMultiSelectMode]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -806,10 +811,12 @@ type HomeFeedContentProps = {
|
|||
item: LibraryItem | undefined
|
||||
) => Promise<void>
|
||||
|
||||
multiSelectMode: MultiSelectMode
|
||||
setIsChecked: (itemId: string, set: boolean) => void
|
||||
itemIsChecked: (itemId: string) => boolean
|
||||
|
||||
multiSelectMode: MultiSelectMode
|
||||
setMultiSelectMode: (mode: MultiSelectMode) => void
|
||||
|
||||
numItemsSelected: number
|
||||
|
||||
performMultiSelectAction: (action: BulkAction, labelIds?: string[]) => void
|
||||
|
|
@ -861,7 +868,6 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
|||
setShowAddLinkModal={props.setShowAddLinkModal}
|
||||
searchTerm={props.searchTerm}
|
||||
applySearchQuery={(searchQuery: string) => {
|
||||
console.log('searching with searchQuery: ', searchQuery)
|
||||
props.applySearchQuery(searchQuery)
|
||||
}}
|
||||
showFilterMenu={showFilterMenu}
|
||||
|
|
@ -880,7 +886,6 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
|||
<LibraryItemsLayout
|
||||
viewer={viewerData?.me}
|
||||
layout={layout}
|
||||
inMultiSelect={props.multiSelectMode !== 'off'}
|
||||
isChecked={props.itemIsChecked}
|
||||
{...props}
|
||||
/>
|
||||
|
|
@ -897,7 +902,9 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
|||
type LibraryItemsLayoutProps = {
|
||||
layout: LayoutType
|
||||
viewer?: UserBasicData
|
||||
inMultiSelect: boolean
|
||||
|
||||
multiSelectMode: MultiSelectMode
|
||||
setMultiSelectMode: (mode: MultiSelectMode) => void
|
||||
|
||||
isChecked: (itemId: string) => boolean
|
||||
setIsChecked: (itemId: string, set: boolean) => void
|
||||
|
|
@ -970,7 +977,7 @@ function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element {
|
|||
setLinkToUnsubscribe={props.setLinkToUnsubscribe}
|
||||
setShowRemoveLinkConfirmation={setShowRemoveLinkConfirmation}
|
||||
actionHandler={props.actionHandler}
|
||||
inMultiSelect={props.inMultiSelect}
|
||||
multiSelectMode={props.multiSelectMode}
|
||||
/>
|
||||
)}
|
||||
<HStack
|
||||
|
|
@ -1024,7 +1031,7 @@ function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element {
|
|||
item={props.linkToRemove?.node}
|
||||
viewer={props.viewer}
|
||||
layout="GRID_LAYOUT"
|
||||
inMultiSelect={false}
|
||||
multiSelectMode={props.multiSelectMode}
|
||||
isChecked={false}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
setIsChecked={() => {}}
|
||||
|
|
@ -1095,9 +1102,9 @@ type LibraryItemsProps = {
|
|||
setLinkToUnsubscribe: (set: LibraryItem | undefined) => void
|
||||
setShowRemoveLinkConfirmation: (show: true) => void
|
||||
|
||||
inMultiSelect: boolean
|
||||
isChecked: (itemId: string) => boolean
|
||||
setIsChecked: (itemId: string, set: boolean) => void
|
||||
multiSelectMode: MultiSelectMode
|
||||
|
||||
actionHandler: (
|
||||
action: LinkedItemCardAction,
|
||||
|
|
@ -1186,7 +1193,7 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element {
|
|||
viewer={props.viewer}
|
||||
isChecked={props.isChecked(linkedItem.node.id)}
|
||||
setIsChecked={props.setIsChecked}
|
||||
inMultiSelect={props.inMultiSelect}
|
||||
multiSelectMode={props.multiSelectMode}
|
||||
handleAction={(action: LinkedItemCardAction) => {
|
||||
if (action === 'delete') {
|
||||
props.setShowRemoveLinkConfirmation(true)
|
||||
|
|
|
|||
|
|
@ -203,6 +203,12 @@ function Labels(props: LibraryFilterMenuProps): JSX.Element {
|
|||
initialValue: false,
|
||||
})
|
||||
|
||||
const sortedLabels = useMemo(() => {
|
||||
return labels.sort((left: Label, right: Label) =>
|
||||
left.name.localeCompare(right.name)
|
||||
)
|
||||
}, [labels])
|
||||
|
||||
return (
|
||||
<MenuPanel
|
||||
title="Labels"
|
||||
|
|
@ -212,7 +218,7 @@ function Labels(props: LibraryFilterMenuProps): JSX.Element {
|
|||
window.location.href = '/settings/labels'
|
||||
}}
|
||||
>
|
||||
{labels.slice(0, viewAll ? undefined : 4).map((item) => {
|
||||
{sortedLabels.slice(0, viewAll ? undefined : 4).map((item) => {
|
||||
return <LabelButton key={item.id} label={item} {...props} />
|
||||
})}
|
||||
<ViewAllButton state={viewAll} setState={setViewAll} />
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import { TrashIcon } from '../../elements/icons/TrashIcon'
|
|||
import { LabelIcon } from '../../elements/icons/LabelIcon'
|
||||
import { ListViewIcon } from '../../elements/icons/ListViewIcon'
|
||||
import { GridViewIcon } from '../../elements/icons/GridViewIcon'
|
||||
import { CaretDownIcon } from '../../elements/icons/CaretDownIcon'
|
||||
|
||||
export type MultiSelectMode = 'off' | 'none' | 'some' | 'visible' | 'search'
|
||||
|
||||
|
|
@ -377,9 +378,7 @@ type ControlButtonBoxProps = {
|
|||
applySearchQuery: (searchQuery: string) => void
|
||||
}
|
||||
|
||||
function MultiSelectControlButtonBox(
|
||||
props: ControlButtonBoxProps
|
||||
): JSX.Element {
|
||||
function MultiSelectControls(props: ControlButtonBoxProps): JSX.Element {
|
||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
||||
const [showLabelsModal, setShowLabelsModal] = useState(false)
|
||||
|
||||
|
|
@ -498,7 +497,7 @@ function SearchControlButtonBox(
|
|||
)
|
||||
}
|
||||
|
||||
function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
||||
const MuliSelectControl = (props: ControlButtonBoxProps): JSX.Element => {
|
||||
const [isChecked, setIsChecked] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -507,6 +506,69 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
|||
}
|
||||
}, [props.multiSelectMode])
|
||||
|
||||
return (
|
||||
<Box
|
||||
css={{
|
||||
display: 'flex',
|
||||
padding: '10px',
|
||||
height: '38px',
|
||||
maxWidth: '521px',
|
||||
bg: '$thLibrarySearchbox',
|
||||
borderRadius: '6px',
|
||||
|
||||
boxShadow: '0px 1px 2px 0px rgba(0, 0, 0, 0.05);',
|
||||
'@mdDown': {
|
||||
mx: '5px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<SpanBox
|
||||
css={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
gap: '5px',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<CardCheckbox
|
||||
isChecked={isChecked}
|
||||
handleChanged={() => {
|
||||
const newValue = !isChecked
|
||||
props.setMultiSelectMode(newValue ? 'visible' : 'off')
|
||||
setIsChecked(newValue)
|
||||
}}
|
||||
/>
|
||||
<SpanBox css={{ display: 'flex', pb: '2px' }}>
|
||||
<Dropdown
|
||||
triggerElement={
|
||||
<CaretDownIcon
|
||||
size={9}
|
||||
color={theme.colors.graySolid.toString()}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DropdownOption
|
||||
onSelect={() => {
|
||||
setIsChecked(true)
|
||||
props.setMultiSelectMode('visible')
|
||||
}}
|
||||
title="All"
|
||||
/>
|
||||
{/* <DropdownOption
|
||||
onSelect={() => {
|
||||
setIsChecked(true)
|
||||
props.setMultiSelectMode('search')
|
||||
}}
|
||||
title="All matching search"
|
||||
/> */}
|
||||
</Dropdown>
|
||||
</SpanBox>
|
||||
</SpanBox>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<HStack
|
||||
|
|
@ -519,7 +581,7 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
|||
},
|
||||
width: '95%',
|
||||
'@media (min-width: 930px)': {
|
||||
width: '640px',
|
||||
width: '660px',
|
||||
},
|
||||
'@media (min-width: 1280px)': {
|
||||
width: '1000px',
|
||||
|
|
@ -529,6 +591,7 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
|||
},
|
||||
}}
|
||||
>
|
||||
<MuliSelectControl {...props} />
|
||||
{props.multiSelectMode !== 'off' && (
|
||||
<SpanBox
|
||||
css={{
|
||||
|
|
@ -541,42 +604,9 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
|||
},
|
||||
}}
|
||||
>
|
||||
<CardCheckbox
|
||||
isChecked={isChecked}
|
||||
handleChanged={() => {
|
||||
const newValue = !isChecked
|
||||
props.setMultiSelectMode(newValue ? 'visible' : 'none')
|
||||
setIsChecked(newValue)
|
||||
}}
|
||||
/>
|
||||
<SpanBox css={{ pt: '2px' }}>
|
||||
<Dropdown
|
||||
triggerElement={
|
||||
<CaretDown
|
||||
size={15}
|
||||
color={theme.colors.graySolid.toString()}
|
||||
weight="fill"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DropdownOption
|
||||
onSelect={() => {
|
||||
setIsChecked(true)
|
||||
props.setMultiSelectMode('visible')
|
||||
}}
|
||||
title="All"
|
||||
/>
|
||||
<DropdownOption
|
||||
onSelect={() => {
|
||||
setIsChecked(true)
|
||||
props.setMultiSelectMode('search')
|
||||
}}
|
||||
title="All matching search"
|
||||
/>
|
||||
</Dropdown>
|
||||
</SpanBox>
|
||||
<SpanBox
|
||||
css={{
|
||||
color: '#55B938',
|
||||
paddingLeft: '5px',
|
||||
fontSize: '12px',
|
||||
fontWeight: '600',
|
||||
|
|
@ -588,7 +618,9 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
|||
>
|
||||
{props.numItemsSelected}{' '}
|
||||
<SpanBox
|
||||
css={{ '@media (max-width: 1280px)': { display: 'none' } }}
|
||||
css={{
|
||||
'@media (max-width: 1280px)': { display: 'none' },
|
||||
}}
|
||||
>
|
||||
selected
|
||||
</SpanBox>
|
||||
|
|
@ -597,7 +629,7 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
|||
)}
|
||||
{props.multiSelectMode !== 'off' ? (
|
||||
<>
|
||||
<MultiSelectControlButtonBox {...props} />
|
||||
<MultiSelectControls {...props} />
|
||||
<SpanBox css={{ flex: 1 }}></SpanBox>
|
||||
</>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export function ReaderHeader(props: ReaderHeaderProps): JSX.Element {
|
|||
? '1px solid $thBorderColor'
|
||||
: '1px solid transparent',
|
||||
'@xlgDown': {
|
||||
bg: '$readerMargin',
|
||||
bg: '$readerBg',
|
||||
pointerEvents: 'unset',
|
||||
borderBottom: '1px solid $thBorderColor',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const ContentSecurityPolicy = `
|
|||
font-src 'self' data: cdn.jsdelivr.net https://js.intercomcdn.com https://fonts.intercomcdn.com;
|
||||
form-action 'self' ${process.env.NEXT_PUBLIC_SERVER_BASE_URL} https://getpocket.com/auth/authorize https://intercom.help https://api-iam.intercom.io https://api-iam.eu.intercom.io https://api-iam.au.intercom.io;
|
||||
frame-ancestors 'none';
|
||||
frame-src self accounts.google.com platform.twitter.com www.youtube.com www.youtube-nocookie.com;
|
||||
frame-src 'self' accounts.google.com platform.twitter.com www.youtube.com www.youtube-nocookie.com;
|
||||
manifest-src 'self';
|
||||
script-src 'self' 'unsafe-inline' 'unsafe-eval' accounts.google.com widget.intercom.io js.intercomcdn.com platform.twitter.com cdnjs.cloudflare.com cdn.jsdelivr.net cdn.segment.com;
|
||||
style-src 'self' 'unsafe-inline' accounts.google.com cdnjs.cloudflare.com;
|
||||
|
|
|
|||
|
|
@ -462,7 +462,7 @@ export default function Home(): JSX.Element {
|
|||
css={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
background: '$readerMargin',
|
||||
background: '$readerBg',
|
||||
overflow: 'scroll',
|
||||
paddingTop: '80px',
|
||||
'@media print': {
|
||||
|
|
@ -511,7 +511,7 @@ export default function Home(): JSX.Element {
|
|||
css={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
background: '$readerMargin',
|
||||
background: '$readerBg',
|
||||
overflow: 'scroll',
|
||||
paddingTop: '80px',
|
||||
}}
|
||||
|
|
|
|||
Loading…
Reference in a new issue