Skip to content

Commit

Permalink
feat(toc): affiche la table des matières au clique sur l'icone à gauc…
Browse files Browse the repository at this point in the history
…he du titre

resolves EcrituresNumeriques#1088
  • Loading branch information
ggrossetie committed Nov 7, 2024
1 parent b0465af commit 6cbff3e
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 58 deletions.
2 changes: 0 additions & 2 deletions front/src/components/Write/ArticleEditorMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useTranslation } from 'react-i18next'
import styles from './articleEditorMenu.module.scss'
import Stats from './Stats'
import Biblio from './Biblio'
import Sommaire from './Sommaire'
import Versions from './Versions'
import { Sidebar } from 'react-feather'

Expand All @@ -28,7 +27,6 @@ export default function ArticleEditorMenu ({ articleInfos, readOnly, compareTo,
compareTo={compareTo}
readOnly={readOnly}
/>
<Sommaire />
<Biblio readOnly={readOnly} article={articleInfos} />
<Stats stats={articleStats} />
</div>)}
Expand Down
47 changes: 47 additions & 0 deletions front/src/components/Write/TableOfContents.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Link as GeistLink, Popover } from '@geist-ui/core'
import clsx from 'clsx'
import React, { useCallback } from 'react'
import { AlignLeft } from 'react-feather'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { useRouteMatch } from 'react-router-dom'
import { usePandocAnchoring } from '../../hooks/pandoc.js'
import styles from './tableOfContents.module.scss'

export default function TableOfContents () {
const dispatch = useDispatch()
const { t } = useTranslation()
const articleStructure = useSelector(state => state.articleStructure)
const routeMatch = useRouteMatch()
const getAnchor = usePandocAnchoring()
const hasHtmlAnchors = routeMatch.path === '/article/:id/preview'
const handleTableEntryClick = useCallback(({ target }) => {
hasHtmlAnchors
? document.querySelector(`#${target.dataset.headingAnchor}`)?.scrollIntoView()
: dispatch({ type: 'UPDATE_EDITOR_CURSOR_POSITION', lineNumber: parseInt(target.dataset.index, 10), column: 0 })
}, [hasHtmlAnchors])

const content = () => {
if (articleStructure.length === 0) {
return <></>
}
return <>
<Popover.Item title>
<span>{t('toc.title')}</span>
</Popover.Item>
{articleStructure.map((item) => (
<Popover.Item key={`line-${item.index}-${item.line}`} tabIndex={0} data-index={item.index}
data-heading-anchor={getAnchor(item.line)}>
<GeistLink href="#" data-index={item.index} onClick={handleTableEntryClick}>{item.title}</GeistLink>
</Popover.Item>
))}
</>
}

return (
<Popover className={clsx(styles.tocTooltip, articleStructure.length === 0 && styles.empty)}
placement="bottomStart"
content={content} hideArrow={articleStructure.length === 0}>
<AlignLeft/>
</Popover>)
}
57 changes: 32 additions & 25 deletions front/src/components/Write/WorkingVersion.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { Modal as GeistModal } from '@geist-ui/core'
import React, { useEffect, useMemo, useState, useCallback } from 'react'
import { shallowEqual, useSelector } from 'react-redux'
import { Link } from "react-router-dom";
import { AlertCircle, AlignLeft, Check, Edit3, Eye, Loader, Printer } from 'react-feather'
import { Link } from 'react-router-dom'
import { AlertCircle, Check, Edit3, Eye, Loader, Printer } from 'react-feather'
import { useTranslation } from 'react-i18next'
import ArticleContributors from '../ArticleContributors.jsx'
import TimeAgo from '../TimeAgo.jsx'
import TableOfContents from './TableOfContents.jsx'

import styles from './workingVersion.module.scss'
import buttonStyles from "../button.module.scss";
import Button from "../Button";
import Modal from "../Modal";
import Export from "../Export";
import buttonStyles from '../button.module.scss'
import Button from '../Button'
import Modal from '../Modal'
import Export from '../Export'

const ONE_MINUTE = 60000

Expand Down Expand Up @@ -71,8 +70,8 @@ export function ArticleSaveState ({ state, updatedAt, stateMessage }) {
</span>)}
</span>

{state === 'saved' && (<TimeAgo date={isoString}/>)}
</>)
{state === 'saved' && (<TimeAgo date={isoString}/>)}
</>)
}

export default function WorkingVersion ({ articleInfos, live, selectedVersion, mode }) {
Expand All @@ -82,7 +81,6 @@ export default function WorkingVersion ({ articleInfos, live, selectedVersion, m
const openExport = useCallback(() => setExporting(true), [])
const { t } = useTranslation()


const previewUrl = selectedVersion
? `/article/${articleInfos._id}/version/${selectedVersion}/preview`
: `/article/${articleInfos._id}/preview`
Expand All @@ -96,27 +94,35 @@ export default function WorkingVersion ({ articleInfos, live, selectedVersion, m
<section className={styles.section}>
<header className={styles.header}>
<h1 className={styles.title}>
<AlignLeft/>
<TableOfContents/>
{articleInfos.title}
</h1>
</header>
{exporting && (
<Modal title="Export" cancel={cancelExport}>
<Export articleVersionId={selectedVersion} articleId={articleInfos._id} bib={live.bibPreview} name={articleInfos.title} />
<Export articleVersionId={selectedVersion} articleId={articleInfos._id} bib={live.bibPreview}
name={articleInfos.title}/>
</Modal>
)}
<ul className={styles.actions}>
{articleInfos.preview.stylesheet && (<><li>
<Link to={`/article/${articleInfos._id}`} className={mode === 'write' ? buttonStyles.primaryDisabled : buttonStyles.secondary} title="Edit article">
<Edit3/> Edit
</Link>
</li>
<li>
<Link to={`/article/${articleInfos._id}/preview`} className={mode === 'preview' ? buttonStyles.primaryDisabled : buttonStyles.secondary} title="Preview article">
<Eye/>{articleInfos.preview.stylesheet ? 'Paged.js' : <abbr title="HyperText Markup Language">HTML</abbr>}
&nbsp;Preview
</Link>
</li></>)}
{articleInfos.preview.stylesheet && (<>
<li>
<Link to={`/article/${articleInfos._id}`}
className={mode === 'write' ? buttonStyles.primaryDisabled : buttonStyles.secondary}
title="Edit article">
<Edit3/> Edit
</Link>
</li>
<li>
<Link to={`/article/${articleInfos._id}/preview`}
className={mode === 'preview' ? buttonStyles.primaryDisabled : buttonStyles.secondary}
title="Preview article">
<Eye/>{articleInfos.preview.stylesheet ? 'Paged.js' :
<abbr title="HyperText Markup Language">HTML</abbr>}
&nbsp;Preview
</Link>
</li>
</>)}
<li>
<Button icon title={t('write.title.buttonExport')} onClick={openExport}>
<Printer/>
Expand All @@ -138,7 +144,8 @@ export default function WorkingVersion ({ articleInfos, live, selectedVersion, m
<ArticleVersion version={live.version}/>
</li>
{!live.version && <li className={styles.lastSaved}>
<ArticleSaveState state={workingArticle.state} updatedAt={workingArticle.updatedAt} stateMessage={workingArticle.stateMessage}/>
<ArticleSaveState state={workingArticle.state} updatedAt={workingArticle.updatedAt}
stateMessage={workingArticle.stateMessage}/>
</li>}
</ul>
</div>
Expand Down
11 changes: 11 additions & 0 deletions front/src/components/Write/tableOfContents.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.tocTooltip.empty {
cursor: not-allowed;
}

.tocTooltip {
cursor: pointer;
}

.tocTooltip > svg {
display: flex;
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import { Loading, Popover, Link as GeistLink, useModal, Modal as GeistModal } from '@geist-ui/core'
import clsx from 'clsx'
import { Loading, useModal, Modal as GeistModal } from '@geist-ui/core'
import { Link } from 'react-router-dom'
import PropTypes from 'prop-types'
import React, { useCallback } from 'react'
import { AlignLeft, Eye, Printer } from 'react-feather'
import { useDispatch, useSelector } from 'react-redux'
import { Eye, Printer } from 'react-feather'

import useGraphQL from '../../hooks/graphql.js'
import { getArticleInfo } from '../Article.graphql'
import Button from '../Button.jsx'
import buttonStyles from '../button.module.scss'
import Export from '../Export.jsx'
import TableOfContents from '../Write/TableOfContents.jsx'

import styles from './CollaborativeEditorArticleHeader.module.scss'


export default function CollaborativeEditorArticleHeader ({ articleId }) {
const dispatch = useDispatch()
const articleStructure = useSelector(state => state.articleStructure)
const { data, isLoading } = useGraphQL({ query: getArticleInfo, variables: { articleId } }, {
revalidateIfStale: false,
revalidateOnFocus: false,
Expand All @@ -29,10 +26,6 @@ export default function CollaborativeEditorArticleHeader ({ articleId }) {
bindings: exportModalBinding
} = useModal()

const handleTableOfContentsEntryClicked = useCallback(({ target }) => {
dispatch({ type: 'UPDATE_EDITOR_CURSOR_POSITION', lineNumber: parseInt(target.dataset.index, 10), column: 0 })
}, [])

const handleOpenExportModal = useCallback(() => {
setExportModalVisible(true)
}, [])
Expand All @@ -41,27 +34,9 @@ export default function CollaborativeEditorArticleHeader ({ articleId }) {
return <Loading/>
}

const content = () => {
if (articleStructure.length === 0) {
return <></>
}
return <>
<Popover.Item title>
<span>Table Of Contents</span>
</Popover.Item>
{articleStructure.map((item) => (
<Popover.Item key={`line-${item.index}-${item.line}`} tabIndex={0}>
<GeistLink href="#" data-index={item.index} onClick={handleTableOfContentsEntryClicked}>{item.title}</GeistLink>
</Popover.Item>
))}
</>
}

return (<header className={styles.header}>
<h1 className={styles.title}>
<Popover className={clsx(styles.tocTooltip, articleStructure.length === 0 && styles.empty)} placement="bottomStart" content={content} hideArrow={articleStructure.length === 0}>
<AlignLeft/>
</Popover>
<TableOfContents/>
{data?.article?.title}
</h1>

Expand Down
3 changes: 2 additions & 1 deletion front/src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -269,5 +269,6 @@
"corpus.metadata.form.issue": "Issue",
"corpus.metadata.form.issue.title": "Title",
"corpus.metadata.form.issue.number": "",
"corpus.metadata.form.issue.identifier": "Identifier"
"corpus.metadata.form.issue.identifier": "Identifier",
"toc.title": "Table of contents"
}
3 changes: 2 additions & 1 deletion front/src/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,5 +267,6 @@
"corpus.metadata.form.issue": "Numéro de revue",
"corpus.metadata.form.issue.title": "Titre",
"corpus.metadata.form.issue.number": "",
"corpus.metadata.form.issue.identifier": "Identifiant"
"corpus.metadata.form.issue.identifier": "Identifiant",
"toc.title": "Table des matières"
}

0 comments on commit 6cbff3e

Please sign in to comment.