Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/issue 605 draft issues #610

Merged
merged 9 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ node_modules
.vscode/settings.json
storybook-static
# _redirects
journal-of-digital-history.code-workspace
File renamed without changes.
5 changes: 3 additions & 2 deletions src/components/Articles/ArticlesFacets.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const IssueListItem = (props) => {
const issue = props.items[group.indices[0]].issue
return (
<div className="d-flex align-items-center flex-nowrap" title={issue.name || group.key}>
<IssueLabel pid={issue.pid} publication_date={issue.publication_date} />
<IssueLabel pid={issue.pid} name={issue.name} />
<div>&nbsp;({group.count})</div>
</div>
)
Expand Down Expand Up @@ -105,14 +105,15 @@ const Dimensions = [
},
]

const ArticlesFacets = ({ items, onSelect, className }) => {
const ArticlesFacets = ({ items, onSelect, onShowMore, className }) => {
return (
<Facets
dimensions={Dimensions}
items={items}
onSelect={onSelect}
onInit={(args) => console.debug('[ArticlesFacets] @init', args)}
ShowMoreLabel={ShowMoreLabel}
onShowMore={onShowMore}
className={className}
/>
)
Expand Down
90 changes: 63 additions & 27 deletions src/components/Articles/ArticlesGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ import Article from '../../models/Article'
import ArticlesFacets from '../Articles/ArticlesFacets'
import Issue from '../Issue'
import ArticleFingerprintTooltip from '../ArticleV2/ArticleFingerprintTooltip'

import groupBy from 'lodash/groupBy'
import { Container, Row, Col } from 'react-bootstrap'
import { useSpring, config } from 'react-spring'
import { useSpring, config, a } from '@react-spring/web'
import { useHistory } from 'react-router'
import { useBoundingClientRect } from '../../hooks/graphics'
import { useWindowStore } from '../../store'

const ArticlesGrid = ({
items = [],
Expand All @@ -39,6 +39,8 @@ const ArticlesGrid = ({
// tag ategories to keep
categories = ['narrative', 'tool', 'issue'],
}) => {
const facetsRef = useRef()
const timerRef = useRef()
const { t } = useTranslation()
const [{ [OrderByQueryParam]: orderBy }, setQuery] = useQueryParams({
[OrderByQueryParam]: withDefault(
Expand All @@ -64,6 +66,10 @@ const ArticlesGrid = ({
backgroundColor: 'var(--secondary)',
config: config.stiff,
}))
// animation properties to slide up and down the articleFacets block
const [facetsAnimatedProps, setFacetsAnimatedProps] = useSpring(() => ({
height: 0,
}))
const data = (items || []).map((d, idx) => new Article({ ...d, idx }))
const articles = sort(data, AvailablesOrderByComparators[orderBy])
const { articlesByIssue, showFilters } = useMemo(() => {
Expand All @@ -78,14 +84,15 @@ const ArticlesGrid = ({
const sortedItems = data.map((item, idx) => ({
...item,
idx,
selected: selected?.includes(idx),
}))
const articlesByIssue = groupBy(sortedItems, 'issue.pid')

const showFilters = data.reduce((acc, d) => {
return acc || d.tags.some((t) => categories.includes(t.category))
}, false)
return { articlesByIssue, showFilters }
}, [url, status])
}, [url, selected, status])

const onArticleMouseMoveHandler = (e, datum, idx, article, bounds) => {
if (!isNaN(idx) && animatedRef.current.idx !== idx) {
Expand Down Expand Up @@ -154,14 +161,24 @@ const ArticlesGrid = ({
useLayoutEffect(() => {
setAnimatedProps.start({ opacity: 0 })
}, [selected])
console.debug(
'[Articles] \n- articles:',
Array.isArray(articles),
articles,
'\n- issueId:',
issueId,
selected,
)

useLayoutEffect(() => {
if (status === StatusSuccess) {
setFacetsAnimatedProps.start({
height: facetsRef.current.firstChild.scrollHeight,
delay: 1000,
})
return useWindowStore.subscribe(() => {
clearTimeout(timerRef.current)
timerRef.current = setTimeout(() => {
setFacetsAnimatedProps.start({
height: facetsRef.current.firstChild.scrollHeight,
delay: 0,
})
}, 0)
})
}
}, [status])

return (
<Container ref={ref} className="Articles Issue page ">
Expand All @@ -183,27 +200,39 @@ const ArticlesGrid = ({
</div>
</Col>
</Row>
{showFilters && (
<Row className="mb-3">
<Col md={{ offset: 1, span: 10 }}>
{status === StatusSuccess && (
<ArticlesFacets
items={data}
onSelect={onFacetsSelectHandler}
className="Articles_facets"
/>
)}
</Col>
</Row>
)}

<a.div
className="row mb-1 position-relative overflow-hidden"
ref={facetsRef}
style={facetsAnimatedProps}
>
<Col md={{ offset: 1, span: 10 }} className="position-absolute">
{status === StatusSuccess && (
<ArticlesFacets
items={data}
onShowMore={() => {
console.info('[ArticlesGrid] @showMore')
clearTimeout(timerRef.current)
setTimeout(() => {
setFacetsAnimatedProps.start({
height: facetsRef.current.firstChild.scrollHeight,
delay: 0,
})
}, 0)
}}
onSelect={onFacetsSelectHandler}
className="Articles_facets "
/>
)}
</Col>
</a.div>
{orderBy === OrderByIssue &&
issues.map((issue) => {
// const issue = articlesByIssue[id][0].issue
const numArticles = articlesByIssue[issue.pid]?.length
const numSelectedArticles = articlesByIssue[issue.pid]?.filter((d) => d.selected).length

return (
<React.Fragment key={issue.pid}>
<hr />
<a className="anchor" id={`anchor-${issue.pid}`} />

<IssueArticles
Expand All @@ -213,7 +242,14 @@ const ArticlesGrid = ({
onArticleClick={onArticleClickHandler}
onArticleMouseOut={onArticleMouseOutHandler}
>
<Issue item={issue} className="my-2" />
<Issue
numArticles={numArticles}
isInFilterMode={Array.isArray(selected)}
numSelectedArticles={numSelectedArticles}
hasSelectedArticles={!!articlesByIssue[issue.pid]}
item={issue}
className="py-2 mb-1"
/>
</IssueArticles>
</React.Fragment>
)
Expand Down
11 changes: 10 additions & 1 deletion src/components/Facets/Dimension.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const Dimension = ({
onInit,
onSelect,
onMouseEnter,
onShowMore,
children,
ListItem = DimensionGroupListItem,
}) => {
Expand Down Expand Up @@ -174,7 +175,15 @@ const Dimension = ({
))}
{restGroups.length > 0 && (
<li>
<button className="Dimension_toggleShowMoreBtn" onClick={() => setShowMore(!showMore)}>
<button
className="Dimension_toggleShowMoreBtn"
onClick={() => {
if (typeof onShowMore === 'function') {
onShowMore(!showMore)
}
setShowMore(!showMore)
}}
>
<span>
{t(showMore ? 'dimensions.actions.showLess' : 'dimensions.actions.showMore', {
n: restGroups.length,
Expand Down
2 changes: 2 additions & 0 deletions src/components/Facets/Facets.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ const Facets = ({
onSelect,
onInit,
onMouseEnter,
onShowMore,
className,
style,
}) => {
Expand Down Expand Up @@ -242,6 +243,7 @@ const Facets = ({
onSelect={onSelectHandler}
onInit={onInitHandler}
onMouseEnter={onMouseEnterHandler}
onShowMore={onShowMore}
ListItem={dimension.ListItem}
>
{reset === true && dims[dimension.name].selected.length > 0 && (
Expand Down
15 changes: 15 additions & 0 deletions src/components/Issue/Issue.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.Issue.disabled > div {
display: none !important;
}
.Issue {
border-top: 1px solid var(--gray-400);
position: sticky;
top: 99px;
background: var(--gray-100);
z-index: 1;
}
.Issue__numArticles b {
border: 1px solid;
padding: 0 6px 0 5px;
border-radius: 5px;
}
44 changes: 40 additions & 4 deletions src/components/Issue/Issue.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,27 @@ import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next'
import { Col, Row } from 'react-bootstrap'
import { a, useSpring } from '@react-spring/web'
import './Issue.css'

const Issue = ({ item, className = '' }) => {
const Issue = ({
item,
numArticles = -1,
numSelectedArticles = -1,
isInFilterMode = false,
className = '',
}) => {
const ref = useRef()
const { t } = useTranslation()
const descriptionRef = useRef()
const buttonRef = useRef()
const isOpen = useRef(false)

const [{ height }, api] = useSpring(() => ({ height: 0 }))
const { t } = useTranslation()
const label = item.pid.replace(/jdh0+(\d+)/, (m, n) => t('numbers.issue', { n }))

const activeClass = isInFilterMode && numSelectedArticles > 0 ? 'active' : ''
const disabledClass = isInFilterMode && numSelectedArticles < 1 ? 'disabled' : ''

const toggleHeight = () => {
isOpen.current = !isOpen.current
// change label on the button
Expand All @@ -38,9 +48,32 @@ const Issue = ({ item, className = '' }) => {
}, [ref])

return (
<Row className={`Issue align-items-start ${className}`}>
<Row className={`Issue align-items-start ${className} ${activeClass} ${disabledClass}`}>
<Col md={{ span: 11 }} lg={{ span: 5 }} ref={ref}>
{label} {item.status !== 'PUBLISHED' ? <b>&mdash; {t('status.' + item.status)}</b> : null}
{item.status !== 'PUBLISHED' ? <em>{t('status.' + item.status)}</em> : label}
{isInFilterMode && numSelectedArticles > 0 ? (
<>
&nbsp; &mdash; &nbsp;
<div
className="d-inline-block Issue__numArticles"
dangerouslySetInnerHTML={{
__html: t('numbers.articlesFiltered', {
n: numSelectedArticles,
total: numArticles,
}),
}}
/>
</>
) : null}
{!isInFilterMode && numArticles > 0 ? (
<>
&nbsp; &mdash; &nbsp;
<div
className="d-inline-block Issue__numArticles"
dangerouslySetInnerHTML={{ __html: t('numbers.articlesInIssue', { n: numArticles }) }}
/>{' '}
</>
) : null}
<h2>{item.name}</h2>
</Col>

Expand Down Expand Up @@ -74,6 +107,9 @@ Issue.propTypes = {
description: PropTypes.string,
status: PropTypes.string.isRequired,
}).isRequired,
numArticles: PropTypes.number,
numSelectedArticles: PropTypes.number,
isInFilterMode: PropTypes.bool,
className: PropTypes.string,
}

Expand Down
16 changes: 15 additions & 1 deletion src/components/Issue/IssueArticleGridItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import '../../styles/components/IssueArticleGridItem.scss'
import { ArrowRightCircle } from 'react-feather'
import IssueLabel from './IssueLabel'

const JustAddedTimeInterval = 3600000 * 24 * 240

const IssueArticleGridItem = ({
article = {},
isFake = false,
Expand All @@ -25,8 +27,20 @@ const IssueArticleGridItem = ({
const [{ width: size }, ref] = useBoundingClientRect()
const { title, keywords, excerpt, contributor } = extractMetadataFromArticle(article)
const { t } = useTranslation()
const isPrettyRecent = new Date() - new Date(article.publication_date) < JustAddedTimeInterval
console.debug(
'[IssueArticleGridItem]',
article.publication_date,
new Date() - new Date(article.publication_date),
JustAddedTimeInterval,
isPrettyRecent,
)
return (
<div className="IssueArticleGridItem" ref={ref} onMouseOut={onMouseOut}>
<div
className={`IssueArticleGridItem ${isPrettyRecent ? 'just-added' : ''}`}
ref={ref}
onMouseOut={onMouseOut}
>
<div
className={IsMobile ? 'half-squared' : 'squared'}
onMouseOut={onMouseOut}
Expand Down
4 changes: 3 additions & 1 deletion src/components/Issue/IssueArticles.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const IssueArticles = ({

respectOrdering = false,
children,
className = '',
}) => {
const ref = React.useRef()
const bboxRef = React.useRef()
Expand Down Expand Up @@ -77,8 +78,9 @@ const IssueArticles = ({
console.debug('[IssueArticles] rendered')

return (
<Row ref={ref}>
<Row ref={ref} className={`IssueArticles ${className}`}>
{children}
{data.length > 0 && <Col sm={{ span: 12 }} className="py-3"></Col>}
{editorials.map((article, i) => {
if (Array.isArray(selected) && selected.indexOf(article.idx) === -1) {
return null
Expand Down
17 changes: 14 additions & 3 deletions src/components/Issue/IssueLabel.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import React from 'react'
import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next'

const IssueLabel = ({ pid, publication_date }) => {
const IssueLabel = ({ pid = '', name = '' }) => {
const { t } = useTranslation()
const shortName = name.length > 16 ? name.slice(0, 16) + '...' : name
return (
<React.Fragment>
{pid.replace(/jdh0+(\d+)/, (m, n) => t('numbers.issue', { n }))}
&nbsp;&middot;&nbsp;
{new Date(publication_date).getFullYear()}
{name.length > 0 ? (
<>
&nbsp;&middot;&nbsp;
{shortName}
</>
) : null}
</React.Fragment>
)
}

IssueLabel.propTypes = {
pid: PropTypes.string,
name: PropTypes.string,
}

export default IssueLabel
Loading