- ): null}
- {cell.outputs.map((output,i) => (
-
diff --git a/src/components/Article/ArticleToCStep.js b/src/components/Article/ArticleToCStep.js
index 4f24c3c1..bda76284 100644
--- a/src/components/Article/ArticleToCStep.js
+++ b/src/components/Article/ArticleToCStep.js
@@ -1,36 +1,40 @@
import React from 'react'
import { useHistory } from 'react-router-dom'
-import { Layers, Image, Grid } from 'react-feather'
+import { Layers, Grid } from 'react-feather'
import { useArticleStore } from '../../store'
-
+import { MediaImage } from 'iconoir-react'
const ArticleToCStep = ({
- idx, active=false,
- level='',
- isFigure=false,
- isTable=false,
- isHermeneutics=false,
- isAccordionOpen=false,
- isSectionStart=false,
- isSectionEnd=false,
+ idx,
+ active = false,
+ level = '',
+ isFigure = false,
+ isTable = false,
+ isHermeneutics = false,
+ isAccordionOpen = false,
+ isSectionStart = false,
+ isSectionEnd = false,
children,
- width=0,
- marginLeft=70,
- className=''
+ width = 0,
+ marginLeft = 70,
+ className = '',
}) => {
const history = useHistory()
- const setVisibleShadowCell = useArticleStore(state=>state.setVisibleShadowCell)
- const displayLayer = useArticleStore(state=>state.displayLayer)
+ const setVisibleShadowCell = useArticleStore((state) => state.setVisibleShadowCell)
+ const displayLayer = useArticleStore((state) => state.displayLayer)
const availableWidth = width - marginLeft
const levelClassName = `ArticleToCStep_Level_${level}`
- const labelClassName = isHermeneutics
- ? 'ArticleToCStep_labelHermeneutics'
- : !isHermeneutics && !isTable && !isFigure
- ? 'ArticleToCStep_labelCircle'
- : isFigure && !isTable
- ? 'ArticleToCStep_labelFigure'
- : 'ArticleToCStep_labelTable'
+ let labelClassName = ''
+ if (isHermeneutics) {
+ labelClassName = 'ArticleToCStep_labelHermeneutics'
+ } else if (!isHermeneutics && !isTable && !isFigure) {
+ labelClassName = 'ArticleToCStep_labelCircle'
+ } else if (isFigure && !isTable) {
+ labelClassName = 'ArticleToCStep_labelFigure'
+ } else {
+ labelClassName = 'ArticleToCStep_labelTable'
+ }
const handleClick = () => {
// if the layer is hidden, opens it up and scroll to it on click.
@@ -44,17 +48,21 @@ const ArticleToCStep = ({
}
}
return (
-
-
+
+
{isHermeneutics && !isTable && !isFigure &&
}
{!isHermeneutics && !isTable && !isFigure &&
}
- {isFigure && !isTable &&
}
- {isTable &&
}
+ {isFigure && !isTable &&
}
+ {isTable &&
}
)
diff --git a/src/components/ArticleV2/ArticleCellPlaceholder.css b/src/components/ArticleV2/ArticleCellPlaceholder.css
new file mode 100644
index 00000000..9c5f22ab
--- /dev/null
+++ b/src/components/ArticleV2/ArticleCellPlaceholder.css
@@ -0,0 +1,42 @@
+.ArticleCellPlaceholder__figure {
+ white-space: nowrap;
+ position: absolute;
+ right: 0;
+ margin-top: 0.5rem;
+}
+
+.ArticleCellPlaceholder.dialog table td > div {
+ border-radius: 5px;
+ padding: 5px 10px;
+ display: inline-block;
+ background-color: rgba(255, 255, 255, 0.5);
+ box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
+}
+
+.ArticleCellPlaceholder.dialog table {
+ border-collapse: separate;
+ border-spacing: 5px;
+}
+
+.ArticleCellPlaceholder table {
+ font-size: 0.8em;
+}
+
+.ArticleCellPlaceholder table td {
+ padding: var(--spacer-2);
+}
+
+.ArticleCellPlaceholder.dialog table td {
+ padding: 5px;
+}
+.ArticleCellPlaceholder table th {
+ padding: 0 var(--spacer-2);
+}
+
+.ArticleCellPlaceholder.dialog table td:last-child {
+ text-align: right;
+}
+.ArticleCellPlaceholder.dialog table td.empty {
+ box-shadow: none;
+ background: transparent;
+}
diff --git a/src/components/ArticleV2/ArticleCellPlaceholder.js b/src/components/ArticleV2/ArticleCellPlaceholder.js
index 04a15378..1cc6a86a 100644
--- a/src/components/ArticleV2/ArticleCellPlaceholder.js
+++ b/src/components/ArticleV2/ArticleCellPlaceholder.js
@@ -3,61 +3,82 @@ import { Container, Row, Col } from 'react-bootstrap'
import { BootstrapColumLayout } from '../../constants'
import ArticleCellContent from '../Article/ArticleCellContent'
import ArticleCellSourceCode from '../Article/ArticleCellSourceCode'
-import {ArrowDown} from 'react-feather'
+import { ArrowDown } from 'react-feather'
+import { useTranslation } from 'react-i18next'
+import './ArticleCellPlaceholder.css'
+const ArticleCellPlaceholderParagraphNumbers = ({ nums = [], figure = null }) => {
+ const { t } = useTranslation()
+
+ return (
+
+ {nums.length === 1 ? (
+ nums[0]
+ ) : (
+ <>
+ {nums[0]}
+
+
+
+ {nums[nums.length - 1]}
+ >
+ )}
+ {figure ? (
+
+ {t(figure.tNLabel, { n: figure.tNum })}
+
+ ) : null}
+
+ )
+}
const ArticleCellPlaceholder = ({
- type='code',
+ type = 'code',
layer,
// whenever the placeholder stands for more than one paragraphs
- nums=[],
- content='',
+ nums = [],
+ content = '',
idx,
- headingLevel=0,
- // isFigure=false
- onNumClick
+ headingLevel = 0,
+ figure = null,
+ onNumClick,
}) => {
- const paragraphNumbers = nums.length
- ? nums.length === 1
- ? nums[0]
- : (
-
- {nums[0]}
-
-
-
- {nums[nums.length -1]}
-
- )
- : null
+ const paragraphNumbers = ArticleCellPlaceholderParagraphNumbers({ nums, figure })
+
const onNumClickHandler = (e) => {
- onNumClick(e, {layer, idx})
+ onNumClick(e, { layer, idx })
}
return (
-
- {type === 'markdown'
- ? (
-
- )
- : (
-
- )
- }
+
+ {/* {figure ? (
+
+ ) : null} */}
+ {type === 'markdown' ? (
+
+ ) : (
+
+ )}
diff --git a/src/components/ArticleV2/ArticleFlow.js b/src/components/ArticleV2/ArticleFlow.js
index ea4d4991..8a76ee73 100644
--- a/src/components/ArticleV2/ArticleFlow.js
+++ b/src/components/ArticleV2/ArticleFlow.js
@@ -46,15 +46,13 @@ const ArticleFlow = ({
if (cell.layer === LayerHidden) {
return
}
- if (
- i > 0 &&
- (cell.layer !== previousLayer || cell.isHeading || cell.isFigure || cell.isTable)
- ) {
+ if (i > 0 && (cell.layer !== previousLayer || cell.isHeading || cell.figure !== null)) {
buffers.push([...buffer])
buffer = []
}
buffer.push(i)
- // copy value
+ // update previous layer. If there is a figure and you want to isolate it, add figure suffix
+ // previousLayer = String(cell.layer) + (cell.figure !== null ? 'figure' : '')
previousLayer = String(cell.layer)
})
if (buffer.length) {
diff --git a/src/components/ArticleV2/ArticleLayer.js b/src/components/ArticleV2/ArticleLayer.js
index 58dab764..1ac05cd5 100644
--- a/src/components/ArticleV2/ArticleLayer.js
+++ b/src/components/ArticleV2/ArticleLayer.js
@@ -375,6 +375,7 @@ const ArticleLayer = ({
memoid={['pl', memoid, i, j].join('-')}
{...paragraphs[j]}
headingLevel={paragraphs[j].isHeading ? paragraphs[j].heading.level : 0}
+ figure={paragraphs[j].figure}
nums={
firstCellInGroup.idx !== paragraphs[j].idx
? []
diff --git a/src/components/ArticleV2/ArticleToC.js b/src/components/ArticleV2/ArticleToC.js
index cd11d952..b00498ff 100644
--- a/src/components/ArticleV2/ArticleToC.js
+++ b/src/components/ArticleV2/ArticleToC.js
@@ -104,6 +104,7 @@ const ArticleToC = ({
: cell.isFigure
? t(cell.figure.tNLabel, { n: cell.figure.tNum })
: '(na)',
+ figureRefPrefix: cell.isFigure ? cell.figure.refPrefix : null,
isFigure: cell.isFigure,
isTable: cell.isTable,
isHermeneutics: cell.layer === LayerHermeneutics,
diff --git a/src/components/ArticleV2/ArticleToCStep.js b/src/components/ArticleV2/ArticleToCStep.js
index 819e83e3..4b5172d9 100644
--- a/src/components/ArticleV2/ArticleToCStep.js
+++ b/src/components/ArticleV2/ArticleToCStep.js
@@ -1,29 +1,33 @@
import React from 'react'
-import { Layers, Image, Grid } from 'react-feather'
+import { Layers, Grid } from 'react-feather'
import { useArticleStore } from '../../store'
-
+import { MediaImage } from 'iconoir-react'
const ArticleToCStep = ({
- cell, active=false,
- isSectionStart=false,
- isSectionEnd=false,
+ cell,
+ active = false,
+ isSectionStart = false,
+ isSectionEnd = false,
children,
- width=0,
- marginLeft=70,
- className='',
+ width = 0,
+ marginLeft = 70,
+ className = '',
onStepClick,
}) => {
- const displayLayer = useArticleStore(state=>state.displayLayer)
+ const displayLayer = useArticleStore((state) => state.displayLayer)
const availableWidth = width - marginLeft
const levelClassName = `ArticleToCStep_Level_${cell.level}`
- const labelClassName = cell.isHermeneutics
- ? 'ArticleToCStep_labelHermeneutics'
- : !cell.isHermeneutics && !cell.isTable && !cell.isFigure
- ? 'ArticleToCStep_labelCircle'
- : cell.isFigure && !cell.isTable
- ? 'ArticleToCStep_labelFigure'
- : 'ArticleToCStep_labelTable'
+ let labelClassName = ''
+ if (cell.isHermeneutics) {
+ labelClassName = 'ArticleToCStep_labelHermeneutics'
+ } else if (!cell.isHermeneutics && !cell.isTable && !cell.isFigure) {
+ labelClassName = 'ArticleToCStep_labelCircle'
+ } else if (cell.isFigure && !cell.isTable) {
+ labelClassName = 'ArticleToCStep_labelFigure'
+ } else {
+ labelClassName = 'ArticleToCStep_labelTable'
+ }
const handleClick = () => {
if (typeof onStepClick === 'function') {
@@ -31,16 +35,24 @@ const ArticleToCStep = ({
}
}
return (
-
+
{cell.isHermeneutics && !cell.isTable && !cell.isFigure &&
}
- {!cell.isHermeneutics && !cell.isTable && !cell.isFigure &&
}
- {cell.isFigure && !cell.isTable &&
}
+ {!cell.isHermeneutics && !cell.isTable && !cell.isFigure && (
+
+ )}
+ {cell.isFigure && !cell.isTable ?
: null}
{cell.isTable &&
}
diff --git a/src/components/ToCStep.js b/src/components/ToCStep.js
index 358a86f3..06ef499f 100644
--- a/src/components/ToCStep.js
+++ b/src/components/ToCStep.js
@@ -1,7 +1,23 @@
import React from 'react'
-import { Layers, Image, Grid } from 'react-feather'
+import { Layers } from 'react-feather'
import { useArticleStore } from '../store'
import '../styles/components/ToCStep.scss'
+import {
+ DialogRefPrefix,
+ FigureRefPrefix,
+ TableRefPrefix,
+ VideoRefPrefix,
+ SoundRefPrefix,
+} from '../constants'
+import { MediaImage, MediaVideo, MessageText, SoundMin, Table } from 'iconoir-react'
+
+const FigureRefPrefixMapping = {
+ [FigureRefPrefix]: MediaImage,
+ [DialogRefPrefix]: MessageText,
+ [TableRefPrefix]: Table,
+ [SoundRefPrefix]: SoundMin,
+ [VideoRefPrefix]: MediaVideo,
+}
const ToCStep = ({
id = -1,
@@ -11,6 +27,7 @@ const ToCStep = ({
isHermeneutics = false,
isSectionStart = false,
isSectionEnd = false,
+ figureRefPrefix = null,
level = 'CODE',
label = '',
width = 100,
@@ -23,19 +40,25 @@ const ToCStep = ({
const availableWidth = width - marginEnd
const levelClassName = `ToCStep_Level_${level}`
- const labelClassName = isHermeneutics
- ? 'ToCStep_labelHermeneutics'
- : !isHermeneutics && !isTable && !isFigure
- ? 'ToCStep_labelCircle'
- : isFigure && !isTable
- ? 'ToCStep_labelFigure'
- : 'ToCStep_labelTable'
+ let labelClassName = ''
+ if (isHermeneutics) {
+ labelClassName = 'ToCStep_labelHermeneutics'
+ } else if (!isHermeneutics && !isTable && !isFigure) {
+ labelClassName = 'ToCStep_labelCircle'
+ } else if (isFigure && !isTable) {
+ labelClassName = 'ToCStep_labelFigure'
+ } else {
+ labelClassName = 'ToCStep_labelTable'
+ }
const handleClick = (e) => {
if (typeof onClick === 'function') {
onClick(e, { id, label })
}
}
+
+ const Icon = isFigure ? FigureRefPrefixMapping[figureRefPrefix] || MediaImage : null
+
return (
- {isHermeneutics && !isTable && !isFigure &&
}
- {!isHermeneutics && !isTable && !isFigure &&
}
- {isFigure && !isTable &&
}
- {isTable &&
}
+ {isHermeneutics && !isFigure &&
}
+ {!isHermeneutics && !isFigure &&
}
+ {isFigure &&
}
)
diff --git a/src/constants.js b/src/constants.js
index 2182a0f2..7cd4c896 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -1,8 +1,8 @@
import { isMobile, isTablet } from 'react-device-detect'
export const IsPortrait = window.innerWidth < window.innerHeight
-export const IsMobile = isMobile ? true : false
-export const IsTablet = isTablet ? true : false
+export const IsMobile = Boolean(isMobile)
+export const IsTablet = Boolean(isTablet)
export const HomeRoute = { to: '/', label: 'navigation.home' }
export const ReferencesRoute = {
@@ -199,7 +199,23 @@ export const FigureRefPrefix = 'figure-'
export const CoverRefPrefix = 'cover'
export const TableRefPrefix = 'table-'
export const QuoteRefPrefix = 'quote-'
+export const DialogRefPrefix = 'dialog-'
+export const SoundRefPrefix = 'sound-'
+export const VideoRefPrefix = 'video-'
export const AnchorRefPrefix = 'anchor-'
+export const GalleryRefPrefix = 'gallery-'
+export const AvailableFigureRefPrefixes = [
+ FigureRefPrefix,
+ TableRefPrefix,
+ QuoteRefPrefix,
+ DialogRefPrefix,
+ SoundRefPrefix,
+ VideoRefPrefix,
+ GalleryRefPrefix,
+ CoverRefPrefix,
+]
+export const AvailableRefPrefixes = AvailableFigureRefPrefixes.concat([AnchorRefPrefix])
+
// display Layer to enable switch between layers
export const DisplayLayerHermeneutics = 'h'
export const DisplayLayerNarrative = 'n'
diff --git a/src/logic/ipynb.js b/src/logic/ipynb.js
index 351978aa..9604e766 100644
--- a/src/logic/ipynb.js
+++ b/src/logic/ipynb.js
@@ -6,8 +6,6 @@ import ArticleHeading from '../models/ArticleHeading'
import ArticleCell from '../models/ArticleCell'
// import ArticleCellGroup from '../models/ArticleCellGroup'
import ArticleReference from '../models/ArticleReference'
-import ArticleFigure from '../models/ArticleFigure'
-import ArticleAnchor from '../models/ArticleAnchor'
import {
SectionDefault,
RoleHidden,
@@ -15,14 +13,12 @@ import {
RoleMetadata,
RoleCitation,
RoleDefault,
- RoleQuote,
CellTypeMarkdown,
CellTypeCode,
- FigureRefPrefix,
- TableRefPrefix,
- CoverRefPrefix,
- QuoteRefPrefix,
AnchorRefPrefix,
+ AvailableRefPrefixes,
+ AvailableFigureRefPrefixes,
+ DialogRefPrefix,
} from '../constants'
import ArticleTreeWarning, {
FigureAnchorWarningCode,
@@ -30,7 +26,12 @@ import ArticleTreeWarning, {
ReferenceWarningCode,
} from '../models/ArticleTreeWarning'
-import { getSectionFromCellMetadata, getLayerFromCellMetadata } from './utils'
+import {
+ getSectionFromCellMetadata,
+ getLayerFromCellMetadata,
+ getFigureFromCell,
+ getAnchorFromCell,
+} from './utils'
const encodeNotebookURL = (url) => btoa(encodeURIComponent(url))
const decodeNotebookURL = (encodedUrl) => decodeURIComponent(atob(encodedUrl))
@@ -42,12 +43,20 @@ const renderMarkdownWithReferences = ({
citationsFromMetadata = {},
figures = [],
anchors = [],
+ figure = null, // ArticleFigure instance if any, null otherwise
}) => {
const references = []
const warnings = []
+ const prefixRegex = new RegExp(
+ '
([^<]+)',
+ 'ig',
+ )
// console.info('markdownParser.render', markdownParser.render(sources))
- const content = markdownParser
+ let content = markdownParser
.render(sources)
+ // enable
+ .replace(/<br\/>/g, '
')
+ .replace(/<br>/g, '
')
// add target blank for all external links
.replace(/
{
if (href.indexOf('http') === 0) {
@@ -57,19 +66,16 @@ const renderMarkdownWithReferences = ({
})
.replace(/<a[^&]*>(.*)<\/a>/g, '')
// replace links "figure-*" ending with automatic numbering syntax
- .replace(
- /([^<]+)<\/a>/g,
- (m, anchorRef, c, content) => {
- const ref =
- anchorRef.indexOf('anchor-') !== -1
- ? anchors.find((d) => d.ref === anchorRef)
- : figures.find((d) => d.ref === anchorRef)
- if (ref) {
- return `figure ${ref.num}`
- }
- return `
${content}`
- },
- )
+ .replace(prefixRegex, (m, anchorRef, c, content) => {
+ const ref =
+ anchorRef.indexOf('anchor-') !== -1
+ ? anchors.find((d) => d.ref === anchorRef)
+ : figures.find((d) => d.ref === anchorRef)
+ if (ref) {
+ return `
figure ${ref.num}`
+ }
+ return `
${content}`
+ })
// replace links "figure-" add data-idx attribute containing a figure id
.replace(/
/g, (m, anchorRef) => {
const ref =
@@ -138,6 +144,15 @@ const renderMarkdownWithReferences = ({
`
})
+ // add specific replacement for specific figure RefPrefix
+ if (figure?.refPrefix === DialogRefPrefix) {
+ // eslint-disable-next-line
+ content = content
+ .replace(/\s*<\/td>/g, ' | | ')
+ .replace(/
\s* \s*<\/td>/g, ' | | ')
+ .replace(/
(.*)<\/td>/g, ' | $1 | ')
+ }
+
return { content, references, warnings }
}
@@ -150,9 +165,17 @@ const getArticleTreeFromIpynb = ({ id, cells = [], metadata = {} }) => {
const anchors = []
const sectionsIndex = {}
const warnings = []
+ // this contain footnotes => zotero id to remap reference at paragraph level
+ const referenceIndex = {}
+ let bibliography = null
let citationsFromMetadata = metadata?.cite2c?.citations
- let tableAutonumbering = 0
-
+ // initialize figure numbering using constants/AvailableFigureRefPrefixes
+ const figureNumberingByRefPrefix = AvailableFigureRefPrefixes.reduce((acc, prefix) => {
+ acc[prefix] = 0
+ return acc
+ }, {})
+ let paragraphNumber = 0
+ // parse citations
if (citationsFromMetadata) {
// if one of the key is named "udefined" (sic)
if (citationsFromMetadata.undefined) {
@@ -178,36 +201,40 @@ const getArticleTreeFromIpynb = ({ id, cells = [], metadata = {} }) => {
return acc
}, {})
}
- // this contain footnotes => zotero id to remap reference at paragraph level
- const referenceIndex = {}
- //
- let bibliography = null
+
// parse biobliographic elements
if (citationsFromMetadata instanceof Object) {
bibliography = new Cite(Object.values(citationsFromMetadata).filter((d) => d))
}
- let paragraphNumber = 0
+
// cycle through notebook cells to fill ArticleCells, figures, headings
cells
.map((cell, idx) => {
- const sources = Array.isArray(cell.source) ? cell.source.join('\n') : cell.source
- // find footnote citations (with the number)
- const footnote = sources.match(/
/)
- const coverRef = cell.metadata.tags?.find((d) => d.indexOf(CoverRefPrefix) === 0)
- const figureRef = cell.metadata.tags?.find((d) => d.indexOf(FigureRefPrefix) === 0)
- const tableRef = cell.metadata.tags?.find((d) => d.indexOf(TableRefPrefix) === 0)
- const quoteRef = cell.metadata.tags?.find((d) => d.indexOf(QuoteRefPrefix) === 0)
- const anchorRef = cell.metadata.tags?.find((d) => d.indexOf(AnchorRefPrefix) === 0)
- // get section and layer from metadata
+ cell.idx = idx
+ // get section and layer from cell metadata
cell.section = getSectionFromCellMetadata(cell.metadata)
cell.layer = getLayerFromCellMetadata(cell.metadata)
cell.role = RoleDefault
- // get role in a secont step
- if (
- sources.length === 0 ||
- cell.metadata.tags?.includes('hidden') ||
- cell.metadata.jdh?.hidden
- ) {
+
+ const sources = Array.isArray(cell.source) ? cell.source.join('\n') : cell.source
+ // find footnote citations (with the number)
+ const footnote = sources.match(//)
+ const figure = getFigureFromCell(cell, AvailableFigureRefPrefixes)
+ const anchor = getAnchorFromCell(cell, [AnchorRefPrefix])
+ const isHidden =
+ sources.length === 0 || cell.metadata.tags?.includes('hidden') || cell.metadata.jdh?.hidden
+
+ if (figure) {
+ // update global index of figure numbering for this specific prefix and forward it to the figure
+ figureNumberingByRefPrefix[figure.refPrefix] += 1
+ figure.setNum(+figureNumberingByRefPrefix[figure.refPrefix])
+ figures.push(figure)
+ cell.figure = figure
+ cell.role = RoleFigure
+ } else if (anchor) {
+ cell.anchor = anchor
+ anchors.push(anchor)
+ } else if (isHidden) {
// is hidden (e.g. uninteresting code, like pip install)
cell.hidden = true
cell.role = RoleHidden
@@ -216,70 +243,10 @@ const getArticleTreeFromIpynb = ({ id, cells = [], metadata = {} }) => {
referenceIndex[footnote[1]] = footnote[2]
cell.hidden = true
cell.role = RoleCitation
- } else if (figureRef) {
- // this is a proper figure, nothing to say about it.
- figures.push(
- new ArticleFigure({
- ref: figureRef,
- idx,
- num: figures.length + 1,
- }),
- )
- cell.role = RoleFigure
- } else if (coverRef) {
- figures.push(new ArticleFigure({ ref: coverRef, idx, isCover: true }))
- cell.role = RoleFigure
- } else if (tableRef) {
- tableAutonumbering += 1
- // this is a proper figure, nothing to say about it.
- figures.push(
- new ArticleFigure({
- ref: tableRef,
- idx,
- isTable: true,
- num: +tableAutonumbering,
- }),
- )
- cell.role = RoleFigure
- } else if (quoteRef) {
- cell.role = RoleQuote
- } else if (
- idx < cells.length &&
- cell.cell_type === 'code' &&
- Array.isArray(cell.outputs) &&
- cell.outputs.length
- ) {
- // this is a "Figure" candindate.
- // Let's check whether the cell outputs JDH metadata and if jdh namespace contains **module**;
- const cellOutputJdhMetadata = cell.outputs.find((d) => d.metadata?.jdh?.module)
- if (cellOutputJdhMetadata) {
- // if yes, these metadata AND its output will be added to this cell.
- cell.metadata = {
- ...cell.metadata,
- jdh: {
- ...cell.metadata.jdh,
- ...cellOutputJdhMetadata.metadata.jdh,
- ref: idx,
- outputs: cell.outputs,
- },
- }
- figures.push(
- new ArticleFigure({
- module: cell.metadata.jdh.module,
- type: cell.metadata.jdh.object?.type,
- idx,
- num: figures.length + 1,
- }),
- )
- cell.role = RoleFigure
- }
- // this is not a real "Figure", just a "Data..."
} else if (cell.section !== SectionDefault) {
cell.role = RoleMetadata
}
- if (anchorRef) {
- anchors.push(new ArticleAnchor({ ref: anchorRef, idx }))
- }
+
cell.source = Array.isArray(cell.source) ? cell.source : [cell.source]
if (cell.role !== RoleMetadata) {
paragraphNumber += 1
@@ -316,6 +283,7 @@ const getArticleTreeFromIpynb = ({ id, cells = [], metadata = {} }) => {
citationsFromMetadata,
figures,
anchors,
+ figure: cell.figure,
})
if (postRenderWarnings.length) {
warnings.push(...postRenderWarnings)
@@ -372,7 +340,8 @@ const getArticleTreeFromIpynb = ({ id, cells = [], metadata = {} }) => {
hidden: !!cell.hidden,
heading: headerIdx > -1 ? headings[headings.length - 1] : null,
level: headerIdx > -1 ? tokens[headerIdx].tag : 'p',
- figure: cell.role === RoleFigure ? figures.find((d) => d.idx === idx) : null,
+ figure: cell.figure,
+ anchor: cell.anchor,
}),
)
} else if (cell.cell_type === CellTypeCode) {
diff --git a/src/logic/utils.js b/src/logic/utils.js
index fc886007..a4d56754 100644
--- a/src/logic/utils.js
+++ b/src/logic/utils.js
@@ -1,4 +1,6 @@
import { LayerChoices, LayerNarrative, SectionChoices, SectionDefault } from '../constants'
+import ArticleAnchor from '../models/ArticleAnchor'
+import ArticleFigure from '../models/ArticleFigure'
import ArticleReference from '../models/ArticleReference'
import ArticleTreeWarning, {
FigureAnchorWarningCode,
@@ -61,6 +63,65 @@ export const getLayerFromCellMetadata = (metadata) =>
LayerNarrative,
)
+/**
+ * getFigureFromCell
+ *
+ * Returns an ArticleFigure object based on the given cell's metadata tags.
+ * @param {Object} cell - The cell object to extract the figure from.
+ * @returns {ArticleFigure|null} - The ArticleFigure object if found, otherwise null.
+ */
+/**
+ * Returns an ArticleFigure object from a cell's metadata tags that match the given refPrefixes.
+ * @param {Object} cell - The cell object to extract the figure from.
+ * @param {Array} [refPrefixes=[]] - An array of reference prefixes to match against the cell's metadata tags.
+ * @returns {ArticleFigure|null} - An ArticleFigure object if a matching tag is found, otherwise null.
+ */
+export const getFigureFromCell = (cell, refPrefixes = []) => {
+ if (!cell.metadata?.tags) {
+ return null
+ }
+ let figure = null
+ for (const refPrefix of refPrefixes) {
+ for (const tag of cell.metadata.tags) {
+ if (tag.indexOf(refPrefix) === 0) {
+ figure = new ArticleFigure({
+ ref: tag,
+ idx: cell.idx,
+ refPrefix,
+ })
+ break
+ }
+ }
+ }
+ return figure
+}
+
+/**
+ * Returns an ArticleAnchor object from a cell's metadata tags that match the given refPrefixes.
+ * @param {Object} cell - The cell object to extract the anchor from.
+ * @param {Array} refPrefixes - An array of reference prefixes to match against the cell's metadata tags.
+ * @returns {ArticleAnchor|null} - An ArticleAnchor object if a matching tag is found, otherwise null.
+ */
+export const getAnchorFromCell = (cell, refPrefixes = []) => {
+ if (!cell.metadata?.tags) {
+ return null
+ }
+ let anchor = null
+ for (const refPrefix of refPrefixes) {
+ for (const tag of cell.metadata.tags) {
+ if (tag.indexOf(refPrefix) === 0) {
+ anchor = new ArticleAnchor({
+ ref: tag,
+ idx: cell.idx,
+ refPrefix,
+ })
+ break
+ }
+ }
+ }
+ return anchor
+}
+
export const renderMarkdownWithReferences = ({
idx = -1,
sources = '',
diff --git a/src/models/ArticleFigure.js b/src/models/ArticleFigure.js
index fe12b378..d35b1084 100644
--- a/src/models/ArticleFigure.js
+++ b/src/models/ArticleFigure.js
@@ -1,3 +1,5 @@
+import { CoverRefPrefix, FigureRefPrefix } from '../constants'
+
export default class ArticleFigure {
constructor({
ref = null, // 'figure-', // 'figure-12a' figure identifier. Must start with constants/FigureRefPrefix
@@ -6,7 +8,7 @@ export default class ArticleFigure {
idx = -1,
num = -1,
isTable = false,
- isCover = false
+ refPrefix = FigureRefPrefix,
}) {
this.type = type
this.module = module
@@ -14,12 +16,29 @@ export default class ArticleFigure {
this.num = num
this.ref = ref
this.isTable = isTable
- this.isCover = isCover
- this.tNLabel = this.isTable ? 'numbers.table': 'numbers.figure'
- this.tNum = typeof this.ref === 'string'
- ? this.ref.lastIndexOf('-*') !== -1
- ? this.num
- : this.ref.split('-').pop()
- : this.num
+ this.isCover = refPrefix === CoverRefPrefix
+ this.tNLabel = this.isCover
+ ? 'cover'
+ : `numbers.${refPrefix.substring(0, refPrefix.length - 1)}`
+ // number in the table of contents. It is more important than `num` because it is used to sort the figures
+ this.refPrefix = refPrefix
+ }
+
+ getPrefix() {
+ return this.refPrefix.substring(0, this.refPrefix.length - 1)
+ }
+
+ setNum(num) {
+ this.num = num
+ if (typeof this.ref !== 'string' || this.ref.lastIndexOf('-*') !== -1) {
+ this.tNum = this.num
+ return
+ }
+ const refNum = this.ref.split('-').pop()
+ if (isNaN(refNum)) {
+ this.tNum = this.num
+ } else {
+ this.tNum = parseInt(refNum)
+ }
}
}
diff --git a/src/stories/ArticleCellWithDialogue.stories.js b/src/stories/ArticleCellWithDialogue.stories.js
new file mode 100644
index 00000000..156dc599
--- /dev/null
+++ b/src/stories/ArticleCellWithDialogue.stories.js
@@ -0,0 +1,68 @@
+import React from 'react'
+import { useIpynbNotebookParagraphs } from '../hooks/ipynb'
+import ArticleCell from '../components/Article/ArticleCell'
+
+export default {
+ title: 'ArticleCell with dialogue',
+ component: ArticleCell,
+ argTypes: {
+ metadata: { control: { type: 'object' }, defaultValue: {} },
+ },
+}
+
+const CellWithDialogue = {
+ cell_type: 'markdown',
+ metadata: {
+ tags: ['table-border-xp-*', 'dialog-border-xp-*'],
+ },
+ source: [
+ 'Termine | Aura\n',
+ '--- | ---\n',
+ 'so eahm to me the border it has a very familiar like \n',
+ 'it’s very familiar to me because I grew up on the border with Slovenia | \n',
+ ' | \n',
+ 'and in fact \n',
+ 'I am part of the Slovenian minority in Italy | \n',
+ ' | OK\n',
+ 'so since I was born \n',
+ 'I was little \n',
+ 'I always \n',
+ 'with my family we always travelled from Italy to Slovenia \n',
+ 'like on the regular basis daily \n',
+ 'so to me the border wasn’t at the beginning \n',
+ 'when when there was still like \n',
+ 'the euhm physical border with the (unclear) that goes up and down \n',
+ 'there was like \n',
+ 'I wouldn’t call it a shock \n',
+ 'because I grew up with it so I got to know it \n',
+ 'but like when euhm like the the police stop you at the border and said to you \n',
+ 'give me your ID give me your prekusnica \n',
+ 'which was a type of passport the people who lived on the border had \n',
+ 'that was quite like euhm it wasn’t normal for me \n',
+ 'and then when the border was taken down \n',
+ 'and now we can you can pass it whatever you like it \n',
+ 'or how many times you like it \n',
+ 'now it’s different but eahm \n',
+ 'so as I said | ',
+ ],
+}
+
+const Template = ({ cells, metadata, isJavascriptTrusted }) => {
+ const articleTree = useIpynbNotebookParagraphs({
+ id: 'memoid',
+ cells,
+ metadata,
+ })
+ return [
+ articleTree.paragraphs.map((p, i) => (
+
+ )),
+ ]
+}
+export const Default = Template.bind({})
+
+Default.args = {
+ isJavascriptTrusted: true,
+ metadata: {},
+ cells: [CellWithDialogue],
+}
diff --git a/src/styles/article.scss b/src/styles/article.scss
index 2d0e8a51..4c30230c 100644
--- a/src/styles/article.scss
+++ b/src/styles/article.scss
@@ -279,10 +279,13 @@ $breakpoint-lg: 992px;
tbody tr:nth-of-type(odd) {
background-color: var(--gray-200);
}
- td,
- th {
+
+ td {
padding: var(--spacer-2);
}
+ th {
+ padding: 0 var(--spacer-2);
+ }
}
.ArticleCellFigure img {
@@ -398,21 +401,6 @@ ArticleShadowLayer_animatedLabel {
height: 20px;
}
-.ArticleFigure {
- min-height: 50px;
- margin-top: var(--spacer-3);
-}
-
-.ArticleFigure_figcaption_num {
- position: absolute;
- left: -140px;
- width: 140px;
- text-align: right;
- padding-right: var(--spacer-3);
- font-family: var(--font-family-monospace);
- font-weight: bold;
-}
-
svg.ArticleFingerprint {
g.type-code {
// is type===cell
diff --git a/src/styles/components/Article/ArticleCellFigure.scss b/src/styles/components/Article/ArticleCellFigure.scss
index 91b37bbc..7894196a 100644
--- a/src/styles/components/Article/ArticleCellFigure.scss
+++ b/src/styles/components/Article/ArticleCellFigure.scss
@@ -23,3 +23,41 @@
top: 0;
}
}
+.ArticleCellFigure.figure {
+ display: block;
+}
+.ArticleCellFigure.dialog {
+ table {
+ background: transparent;
+ border: none;
+ border-collapse: separate;
+ border-spacing: 5px;
+ td > div {
+ border-radius: 5px;
+ padding: 5px 10px;
+ display: inline-block;
+ background-color: rgba(255, 255, 255, 0.5);
+ // simple box shadow
+ box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
+ }
+ td.empty {
+ box-shadow: none;
+ background: transparent;
+ }
+ // last td in each row
+ td:last-child {
+ text-align: right;
+
+ div {
+ text-align: left;
+ }
+ }
+ }
+ table tbody tr:nth-of-type(2n + 1) {
+ background-color: transparent;
+ }
+
+ table td {
+ padding: 5px;
+ }
+}
diff --git a/src/translations.json b/src/translations.json
index de9710da..e3b9be00 100644
--- a/src/translations.json
+++ b/src/translations.json
@@ -245,7 +245,9 @@
},
"numbers": {
"issue": "Issue n.{{n}}",
+ "dialog": "dialog {{ n }}",
"figure": "figure {{ n }}",
+ "sound": "sound {{ n }}",
"table": "table {{ n }}",
"yExponent": "Power scale exponent (y axis): {{n}}",
"errors": "{{count}} errors",
diff --git a/yarn.lock b/yarn.lock
index ee5c36dc..3856b821 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9493,6 +9493,11 @@ i18next@^19.8.4:
dependencies:
"@babel/runtime" "^7.12.0"
+iconoir-react@^6.11.0:
+ version "6.11.0"
+ resolved "https://registry.yarnpkg.com/iconoir-react/-/iconoir-react-6.11.0.tgz#a88d896148c8389138ec931ce7367f3abc6a7144"
+ integrity sha512-+1RgmEWh/9H0aYR2e8sDL5elDUYnkyOXO0E+RnwhkjhJY36Lge6r/9nv1n1FvDXoeFY48omkPUl8s/ciA0XOAg==
+
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"