@crtobiasdelsud/portal-ui 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/ArticleDetailView/ArticleDetailView.jsx +39 -0
- package/src/components/ArticleDetailView/ArticleDetailView.module.scss +18 -0
- package/src/components/ArticleDetailView/Full.jsx +46 -0
- package/src/components/ArticleDetailView/Full.module.scss +48 -0
- package/src/components/ArticleDetailView/Standard.jsx +116 -0
- package/src/components/ArticleDetailView/Standard.module.scss +179 -0
- package/src/components/ArticleHero/ArticleHero.jsx +13 -2
- package/src/components/ArticleHero/ArticleHero.module.scss +16 -0
- package/src/components/ArticleHeroFull/ArticleHeroFull.jsx +4 -1
- package/src/components/ArticleHeroFull/ArticleHeroFull.module.scss +26 -3
- package/src/components/AuthorBlock/AuthorBlock.jsx +1 -1
- package/src/components/EditorOutput/EditorOutput.jsx +29 -20
- package/src/components/EditorOutput/EditorOutput.module.scss +86 -6
- package/src/components/EditorOutputFull/EditorOutputFull.jsx +29 -20
- package/src/components/EditorOutputFull/EditorOutputFull.module.scss +86 -6
- package/src/index.js +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crtobiasdelsud/portal-ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Componentes compartidos entre el portal (Next) y el CMS (Vite) — widgets, views, providers para adapters y article pool.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import styles from './ArticleDetailView.module.scss'
|
|
4
|
+
import Standard from './Standard.jsx'
|
|
5
|
+
import Full from './Full.jsx'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ArticleDetailView — réplica portable de las screens `ArticleDetail` /
|
|
9
|
+
* `ArticleDetailFull` del portal (editor-template-front).
|
|
10
|
+
*
|
|
11
|
+
* Pensada para previews fuera del portal (ej. el CMS): es una *vista pura*,
|
|
12
|
+
* recibe el artículo ya resuelto y no hace data-fetching ni depende del
|
|
13
|
+
* registry de widgets. Reproduce el layout y los estilos de las screens, y
|
|
14
|
+
* elige la variante igual que el portal:
|
|
15
|
+
*
|
|
16
|
+
* article.tipoContenido === 'notaEspecial' → Full (ArticleDetailFull)
|
|
17
|
+
* resto → Standard (ArticleDetail)
|
|
18
|
+
*
|
|
19
|
+
* El root recrea el `<main>` del portal con `container-type: inline-size`
|
|
20
|
+
* para que las container-queries de los componentes portal-ui respondan al
|
|
21
|
+
* ancho real del contenedor — imprescindible al renderizar en un iframe.
|
|
22
|
+
*
|
|
23
|
+
* @param {object} props
|
|
24
|
+
* @param {object} props.article - Artículo resuelto. Shape esperado:
|
|
25
|
+
* { id, titulo, volanta, copete, imagen: { url, epigrafe }, imagenEpigrafe,
|
|
26
|
+
* focalPoint, categoria: { nombre, slug }, autor, publicarComoOrg,
|
|
27
|
+
* fechaPublicacion, tipoContenido, cuerpo (payload EditorJS) }
|
|
28
|
+
*/
|
|
29
|
+
export default function ArticleDetailView({ article }) {
|
|
30
|
+
if (!article) return null
|
|
31
|
+
|
|
32
|
+
const isFull = article.tipoContenido === 'notaEspecial'
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<main className={styles.root}>
|
|
36
|
+
{isFull ? <Full article={article} /> : <Standard article={article} />}
|
|
37
|
+
</main>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Root del preview — recrea el `<main>` del portal: es el container de las
|
|
2
|
+
// container-queries (`container-type: inline-size`) de los componentes
|
|
3
|
+
// portal-ui, para que el responsive responda al ancho real del contenedor
|
|
4
|
+
// (clave cuando se renderiza dentro de un iframe).
|
|
5
|
+
//
|
|
6
|
+
// NOTA: a diferencia del `<main>` real, NO se usa `min-height: 100dvh` —
|
|
7
|
+
// dentro de un iframe auto-dimensionado generaría un loop de altura.
|
|
8
|
+
.root {
|
|
9
|
+
overflow-x: clip;
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
container-type: inline-size;
|
|
13
|
+
background: #fff;
|
|
14
|
+
|
|
15
|
+
// Fija la altura del hero "full" a un valor estable: dentro de un iframe
|
|
16
|
+
// auto-dimensionado, el `90vh/75vh` por defecto generaría un loop de altura.
|
|
17
|
+
--ahf-min-h: 620px;
|
|
18
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import ArticleHeroFull from '../ArticleHeroFull/ArticleHeroFull.jsx'
|
|
4
|
+
import AuthorBlock from '../AuthorBlock/AuthorBlock.jsx'
|
|
5
|
+
import ShareBlock from '../ShareBlock/ShareBlock.jsx'
|
|
6
|
+
import EditorOutputFull from '../EditorOutputFull/EditorOutputFull.jsx'
|
|
7
|
+
import styles from './Full.module.scss'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Réplica de la screen `ArticleDetailFull` (camino non-AMP) del portal —
|
|
11
|
+
* la "nota especial": hero a sangre + cuerpo a una columna.
|
|
12
|
+
*
|
|
13
|
+
* Se omiten las piezas de infraestructura de la app (ArticleTracker y la
|
|
14
|
+
* zona post-body de widgets desde el registry).
|
|
15
|
+
*/
|
|
16
|
+
export default function Full({ article }) {
|
|
17
|
+
const imagenEpigrafe = article.imagen?.epigrafe ?? article.imagenEpigrafe ?? null
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<ArticleHeroFull
|
|
22
|
+
titulo={article.titulo}
|
|
23
|
+
copete={article.copete}
|
|
24
|
+
imagen={article.imagen?.url ?? null}
|
|
25
|
+
imagenEpigrafe={imagenEpigrafe}
|
|
26
|
+
focalPoint={article.focalPoint}
|
|
27
|
+
categoria={article.categoria ?? null}
|
|
28
|
+
/>
|
|
29
|
+
|
|
30
|
+
<div className={styles.wrap}>
|
|
31
|
+
<div className={styles.author}>
|
|
32
|
+
<AuthorBlock
|
|
33
|
+
autor={article.autor}
|
|
34
|
+
publicarComoOrg={article.publicarComoOrg}
|
|
35
|
+
fechaPublicacion={article.fechaPublicacion}
|
|
36
|
+
/>
|
|
37
|
+
<ShareBlock />
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div className={styles.body}>
|
|
41
|
+
<EditorOutputFull data={article.cuerpo} />
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Réplica de ArticleDetailFull.module.scss del portal (editor-template-front).
|
|
2
|
+
@use "../../styles/index" as *;
|
|
3
|
+
|
|
4
|
+
.wrap {
|
|
5
|
+
width: 100%;
|
|
6
|
+
margin: 0 auto;
|
|
7
|
+
padding: 1.5rem 1rem;
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
|
|
10
|
+
@include respond(tablet) {
|
|
11
|
+
max-width: 66.66667%;
|
|
12
|
+
min-width: 66.66667%;
|
|
13
|
+
margin-right: auto;
|
|
14
|
+
margin-left: auto;
|
|
15
|
+
padding: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@include respond(laptop) {
|
|
19
|
+
max-width: 1250px;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.author {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: stretch;
|
|
26
|
+
justify-content: space-between;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
margin: 1rem 0 1.5rem;
|
|
29
|
+
width: 100%;
|
|
30
|
+
|
|
31
|
+
@include respond(tablet) {
|
|
32
|
+
flex-direction: row;
|
|
33
|
+
align-items: center;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.heroImage {
|
|
38
|
+
width: 100%;
|
|
39
|
+
margin-bottom: 2rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.body {
|
|
43
|
+
width: 100%;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.postBody {
|
|
47
|
+
margin-top: 2rem;
|
|
48
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import PageWrapper from '../UI/PageWrapper/PageWrapper.jsx'
|
|
4
|
+
import AspectImage from '../UI/AspectImage/AspectImage.jsx'
|
|
5
|
+
import EditorOutput from '../EditorOutput/EditorOutput.jsx'
|
|
6
|
+
import Breadcrumb from '../Breadcrumb/Breadcrumb.jsx'
|
|
7
|
+
import ArticleHero from '../ArticleHero/ArticleHero.jsx'
|
|
8
|
+
import AuthorBlock from '../AuthorBlock/AuthorBlock.jsx'
|
|
9
|
+
import ShareBlock from '../ShareBlock/ShareBlock.jsx'
|
|
10
|
+
import SpeechButton from '../SpeechButton/SpeechButton.jsx'
|
|
11
|
+
import ArticleSidebar from '../ArticleSidebar/ArticleSidebar.jsx'
|
|
12
|
+
import styles from './Standard.module.scss'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Réplica de la screen `ArticleDetail` (camino non-AMP) del portal.
|
|
16
|
+
*
|
|
17
|
+
* Reproduce el mismo árbol JSX y los mismos estilos. Se omiten las piezas
|
|
18
|
+
* que dependen de infraestructura de la app (ArticleTracker, zonas de
|
|
19
|
+
* widgets pre/post/in-body desde el registry, LoQueSeLee con data-fetching):
|
|
20
|
+
* el preview muestra el artículo en sí, no la config de layout del sitio.
|
|
21
|
+
*/
|
|
22
|
+
export default function Standard({ article }) {
|
|
23
|
+
// Epígrafe de la imagen principal — normalizado en `imagen.epigrafe` y/o
|
|
24
|
+
// `imagenEpigrafe` a nivel raíz; puede venir null.
|
|
25
|
+
const imagenEpigrafe = article.imagen?.epigrafe ?? article.imagenEpigrafe ?? null
|
|
26
|
+
|
|
27
|
+
const breadcrumbItems = [
|
|
28
|
+
{ label: 'Inicio', href: '/' },
|
|
29
|
+
{ label: article.categoria?.nombre ?? '', href: `/${article.categoria?.slug ?? ''}` },
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<PageWrapper>
|
|
34
|
+
<div className={styles.pageWrap}>
|
|
35
|
+
<div className={styles.articleWrap}>
|
|
36
|
+
|
|
37
|
+
{/* Row 2 — breadcrumb, hero, autor, speechbutton */}
|
|
38
|
+
<div className={styles.articleHeader}>
|
|
39
|
+
<Breadcrumb items={breadcrumbItems} />
|
|
40
|
+
<ArticleHero
|
|
41
|
+
titulo={article.titulo}
|
|
42
|
+
volanta={article.volanta}
|
|
43
|
+
copete={article.copete}
|
|
44
|
+
imagen={article.imagen?.url ?? null}
|
|
45
|
+
imagenEpigrafe={imagenEpigrafe}
|
|
46
|
+
focalPoint={article.focalPoint}
|
|
47
|
+
hideImageOnDesktop
|
|
48
|
+
extras={
|
|
49
|
+
<div className={styles.heroExtras}>
|
|
50
|
+
<AuthorBlock
|
|
51
|
+
autor={article.autor}
|
|
52
|
+
publicarComoOrg={article.publicarComoOrg}
|
|
53
|
+
fechaPublicacion={article.fechaPublicacion}
|
|
54
|
+
/>
|
|
55
|
+
<ShareBlock />
|
|
56
|
+
</div>
|
|
57
|
+
}
|
|
58
|
+
/>
|
|
59
|
+
{/* mobile-only: autor + share debajo del hero */}
|
|
60
|
+
<div className={styles.authorContainer}>
|
|
61
|
+
<AuthorBlock
|
|
62
|
+
autor={article.autor}
|
|
63
|
+
publicarComoOrg={article.publicarComoOrg}
|
|
64
|
+
fechaPublicacion={article.fechaPublicacion}
|
|
65
|
+
/>
|
|
66
|
+
<ShareBlock />
|
|
67
|
+
</div>
|
|
68
|
+
{/* mobile-only: speechbutton debajo del hero */}
|
|
69
|
+
<div className={styles.speechMobile}>
|
|
70
|
+
<SpeechButton
|
|
71
|
+
titulo={article.titulo}
|
|
72
|
+
copete={article.copete}
|
|
73
|
+
cuerpo={article.cuerpo}
|
|
74
|
+
imagen={article.imagen?.url ?? null}
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
{/* Row 3 — body (2fr) + sidebar sticky (1fr) */}
|
|
80
|
+
<div className={styles.contentRow}>
|
|
81
|
+
<div className={styles.body}>
|
|
82
|
+
{article.imagen?.url && (
|
|
83
|
+
<figure className={styles.bodyImage}>
|
|
84
|
+
<AspectImage
|
|
85
|
+
src={article.imagen.url}
|
|
86
|
+
alt={article.titulo ?? ''}
|
|
87
|
+
aspect="16:9"
|
|
88
|
+
focalPoint={article.focalPoint}
|
|
89
|
+
/>
|
|
90
|
+
{imagenEpigrafe && (
|
|
91
|
+
<figcaption className={styles.bodyImageCaption}>{imagenEpigrafe}</figcaption>
|
|
92
|
+
)}
|
|
93
|
+
</figure>
|
|
94
|
+
)}
|
|
95
|
+
<div className={styles.speechDesktop}>
|
|
96
|
+
<SpeechButton
|
|
97
|
+
titulo={article.titulo}
|
|
98
|
+
copete={article.copete}
|
|
99
|
+
cuerpo={article.cuerpo}
|
|
100
|
+
imagen={article.imagen?.url ?? null}
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
{article.cuerpo?.blocks?.length > 0 && (
|
|
104
|
+
<EditorOutput data={article.cuerpo} />
|
|
105
|
+
)}
|
|
106
|
+
</div>
|
|
107
|
+
<div className={styles.sidebarPlacement}>
|
|
108
|
+
<ArticleSidebar hasWidgets={false} />
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</PageWrapper>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Réplica de ArticleDetail.module.scss del portal (editor-template-front).
|
|
2
|
+
@use "../../styles/index" as *;
|
|
3
|
+
|
|
4
|
+
// ── Tokens ────────────────────────────────────────────────────────────────────
|
|
5
|
+
$page-max: 1376px;
|
|
6
|
+
$sidebar-w: 322px;
|
|
7
|
+
$col-gap: 30px;
|
|
8
|
+
$page-pad: 16px;
|
|
9
|
+
|
|
10
|
+
// ── Contenedor de ancho máximo ────────────────────────────────────────────────
|
|
11
|
+
.pageWrap {
|
|
12
|
+
width: 100%;
|
|
13
|
+
max-width: $page-max;
|
|
14
|
+
margin: 0 auto;
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
|
|
17
|
+
@include respond(tablet) {
|
|
18
|
+
max-width: 720px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@include respond(laptop) {
|
|
22
|
+
max-width: 960px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@include respond(desktop) {
|
|
26
|
+
max-width: $page-max;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ── Grid de 4 columnas iguales (igual que Home) ───────────────────────────────
|
|
31
|
+
.articleWrap {
|
|
32
|
+
@include respond(laptop) {
|
|
33
|
+
display: grid;
|
|
34
|
+
grid-template-columns: 1fr 1fr 1fr;
|
|
35
|
+
column-gap: $col-gap;
|
|
36
|
+
row-gap: 0;
|
|
37
|
+
align-items: start;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@include respond(desktop) {
|
|
41
|
+
display: grid;
|
|
42
|
+
grid-template-columns: 1fr 1fr 1fr 1fr;
|
|
43
|
+
column-gap: $col-gap;
|
|
44
|
+
row-gap: 0;
|
|
45
|
+
align-items: start;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Breadcrumb + Hero + Author + SpeechButton — cols 1-3, row 2
|
|
50
|
+
.articleHeader {
|
|
51
|
+
min-width: 0;
|
|
52
|
+
padding-top: 1.5rem;
|
|
53
|
+
|
|
54
|
+
@include respond(laptop) {
|
|
55
|
+
grid-column: 1 / 3;
|
|
56
|
+
grid-row: 2;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@include respond(desktop) {
|
|
60
|
+
grid-column: 1 / 4;
|
|
61
|
+
grid-row: 2;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Row 3 — grid item que agrupa body + sidebar.
|
|
66
|
+
.contentRow {
|
|
67
|
+
min-width: 0;
|
|
68
|
+
|
|
69
|
+
@include respond(laptop) {
|
|
70
|
+
grid-column: 1 / 4;
|
|
71
|
+
grid-row: 3;
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: stretch;
|
|
74
|
+
gap: $col-gap;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@include respond(desktop) {
|
|
78
|
+
grid-column: 1 / 4;
|
|
79
|
+
grid-row: 3;
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: stretch;
|
|
82
|
+
gap: $col-gap;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Body — hijo flex del contentRow (2/3 del ancho)
|
|
87
|
+
.body {
|
|
88
|
+
min-width: 0;
|
|
89
|
+
flex: 2 1 0;
|
|
90
|
+
align-self: flex-start;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Sidebar — ocupa el alto del body para que los sticky internos funcionen
|
|
94
|
+
.sidebarPlacement {
|
|
95
|
+
display: none;
|
|
96
|
+
container-type: inline-size;
|
|
97
|
+
|
|
98
|
+
@include respond(laptop) {
|
|
99
|
+
display: block;
|
|
100
|
+
flex: 1 0 0;
|
|
101
|
+
min-width: 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@include respond(desktop) {
|
|
105
|
+
display: block;
|
|
106
|
+
flex: 1 0 0;
|
|
107
|
+
min-width: 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Widgets dentro del body — separación vertical entre widgets consecutivos
|
|
112
|
+
.inBodyWidget {
|
|
113
|
+
margin-top: 24px;
|
|
114
|
+
margin-bottom: 24px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── Imagen del artículo en el body (tablet+) ─────────────────────────────────
|
|
118
|
+
.bodyImage {
|
|
119
|
+
display: none;
|
|
120
|
+
position: relative;
|
|
121
|
+
margin: 0;
|
|
122
|
+
|
|
123
|
+
@include respond(tablet) {
|
|
124
|
+
display: block;
|
|
125
|
+
margin: 0 0 1.5rem;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ── Epígrafe — franja negra sobre el borde inferior de la imagen ─────────────
|
|
130
|
+
.bodyImageCaption {
|
|
131
|
+
position: absolute;
|
|
132
|
+
left: 0;
|
|
133
|
+
right: 0;
|
|
134
|
+
bottom: 0;
|
|
135
|
+
z-index: 1;
|
|
136
|
+
margin: 0;
|
|
137
|
+
padding: 10px 14px;
|
|
138
|
+
font-family: "Inter", "Helvetica", Arial, sans-serif;
|
|
139
|
+
font-size: 0.85rem;
|
|
140
|
+
line-height: 1.4;
|
|
141
|
+
color: #fff;
|
|
142
|
+
background: #000;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ── SpeechButton mobile (en articleHeader) / desktop (en body tras imagen) ───
|
|
146
|
+
.speechMobile {
|
|
147
|
+
@include respond(laptop) {
|
|
148
|
+
display: none;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.speechDesktop {
|
|
153
|
+
display: none;
|
|
154
|
+
|
|
155
|
+
@include respond(laptop) {
|
|
156
|
+
display: block;
|
|
157
|
+
margin-bottom: 1rem;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ── Author + share (mobile) ───────────────────────────────────────────────────
|
|
162
|
+
.authorContainer {
|
|
163
|
+
display: flex;
|
|
164
|
+
flex-direction: column;
|
|
165
|
+
margin-bottom: 0.5rem;
|
|
166
|
+
|
|
167
|
+
@include respond(laptop) {
|
|
168
|
+
display: none;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Extras dentro del hero — visible solo en desktop (el ArticleHero lo oculta en mobile)
|
|
173
|
+
.heroExtras {
|
|
174
|
+
display: flex;
|
|
175
|
+
flex-direction: row;
|
|
176
|
+
justify-content: space-between;
|
|
177
|
+
align-items: center;
|
|
178
|
+
padding-top: 0.75rem;
|
|
179
|
+
}
|
|
@@ -14,7 +14,7 @@ import V0Desktop from './variants/V0Desktop/V0Desktop'
|
|
|
14
14
|
|
|
15
15
|
const VARIANTS = { '0': V0, '1': V1, '2': V2, '3': V3, '4': V4, '5': V5 }
|
|
16
16
|
|
|
17
|
-
export default function ArticleHero({ titulo, volanta, copete, imagen, focalPoint, isAmp = false, extras = null, hideImageOnDesktop = false }) {
|
|
17
|
+
export default function ArticleHero({ titulo, volanta, copete, imagen, imagenEpigrafe, focalPoint, isAmp = false, extras = null, hideImageOnDesktop = false }) {
|
|
18
18
|
const theme = useTheme()
|
|
19
19
|
const variant = String(theme.articleHero ?? 1)
|
|
20
20
|
|
|
@@ -27,10 +27,21 @@ export default function ArticleHero({ titulo, volanta, copete, imagen, focalPoin
|
|
|
27
27
|
|
|
28
28
|
const ExtrasEl = (!isAmp && extras) ? <div className={styles.extras}>{extras}</div> : null
|
|
29
29
|
|
|
30
|
+
// Epígrafe: overlay sutil sobre el borde inferior de la imagen del hero.
|
|
31
|
+
// Solo non-amp (en amp no se aplican los CSS modules).
|
|
32
|
+
const EpigrafeEl = (!isAmp && imagen && imagenEpigrafe)
|
|
33
|
+
? <p className={styles.epigrafe}>{imagenEpigrafe}</p>
|
|
34
|
+
: null
|
|
35
|
+
|
|
30
36
|
const ImgEl = imagen
|
|
31
37
|
? isAmp
|
|
32
38
|
? <img src={imagen} alt={titulo ?? ''} className="article-hero__img" />
|
|
33
|
-
:
|
|
39
|
+
: (
|
|
40
|
+
<>
|
|
41
|
+
<AspectImage src={imagen} alt={titulo ?? ''} aspect="16:9" fill={true} focalPoint={focalPoint} />
|
|
42
|
+
{EpigrafeEl}
|
|
43
|
+
</>
|
|
44
|
+
)
|
|
34
45
|
: null
|
|
35
46
|
|
|
36
47
|
const imgWrapClass = isAmp
|
|
@@ -87,3 +87,19 @@
|
|
|
87
87
|
max-height: none;
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
|
+
|
|
91
|
+
// ── Epígrafe de la imagen — franja negra sobre el borde inferior de la foto ──
|
|
92
|
+
.epigrafe {
|
|
93
|
+
position: absolute;
|
|
94
|
+
left: 0;
|
|
95
|
+
right: 0;
|
|
96
|
+
bottom: 0;
|
|
97
|
+
z-index: 1;
|
|
98
|
+
margin: 0;
|
|
99
|
+
padding: 10px 14px;
|
|
100
|
+
font-family: "Inter", "Helvetica", Arial, sans-serif;
|
|
101
|
+
font-size: 0.8rem;
|
|
102
|
+
line-height: 1.35;
|
|
103
|
+
color: #fff;
|
|
104
|
+
background: #000;
|
|
105
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import styles from './ArticleHeroFull.module.scss'
|
|
4
4
|
import { useSiteConfig } from '../../context/SiteConfigContext.jsx'
|
|
5
5
|
|
|
6
|
-
export default function ArticleHeroFull({ titulo, copete, imagen, focalPoint, categoria }) {
|
|
6
|
+
export default function ArticleHeroFull({ titulo, copete, imagen, imagenEpigrafe, focalPoint, categoria }) {
|
|
7
7
|
const { config } = useSiteConfig()
|
|
8
8
|
const siteName = config?.slots?.header?.settings?.siteName ?? ''
|
|
9
9
|
|
|
@@ -22,6 +22,9 @@ export default function ArticleHeroFull({ titulo, copete, imagen, focalPoint, ca
|
|
|
22
22
|
/>
|
|
23
23
|
)}
|
|
24
24
|
<div className={styles.gradient} />
|
|
25
|
+
{imagen && imagenEpigrafe && (
|
|
26
|
+
<p className={styles.epigrafe}>{imagenEpigrafe}</p>
|
|
27
|
+
)}
|
|
25
28
|
<div className={styles.content}>
|
|
26
29
|
{categoria && (
|
|
27
30
|
<div className={styles.breadcrumb}>
|
|
@@ -3,17 +3,20 @@
|
|
|
3
3
|
.hero {
|
|
4
4
|
position: relative;
|
|
5
5
|
width: 100%;
|
|
6
|
-
|
|
6
|
+
// Altura via custom property: el portal usa el default en `vh`; un preview
|
|
7
|
+
// dentro de un iframe puede fijar `--ahf-min-h` a un valor estable y evitar
|
|
8
|
+
// el loop de altura (vh → alto del iframe → scrollHeight → vh…).
|
|
9
|
+
min-height: var(--ahf-min-h, 90vh);
|
|
7
10
|
overflow: hidden;
|
|
8
11
|
display: flex;
|
|
9
12
|
align-items: flex-end;
|
|
10
13
|
|
|
11
14
|
@include respond(tablet) {
|
|
12
|
-
min-height: 75vh;
|
|
15
|
+
min-height: var(--ahf-min-h, 75vh);
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
@include respond(desktop) {
|
|
16
|
-
min-height: 75vh;
|
|
19
|
+
min-height: var(--ahf-min-h, 75vh);
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
22
|
|
|
@@ -113,4 +116,24 @@
|
|
|
113
116
|
line-height: 1.3em;
|
|
114
117
|
padding-top: 1.875rem;
|
|
115
118
|
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Epígrafe de la imagen — crédito sutil en el borde inferior del hero ──────
|
|
122
|
+
.epigrafe {
|
|
123
|
+
position: absolute;
|
|
124
|
+
right: 0;
|
|
125
|
+
bottom: 0;
|
|
126
|
+
left: 0;
|
|
127
|
+
z-index: 1;
|
|
128
|
+
margin: 0;
|
|
129
|
+
padding: 0 5% 0.5rem;
|
|
130
|
+
font-family: var(--font-inter), Inter, sans-serif;
|
|
131
|
+
font-size: 0.78rem;
|
|
132
|
+
line-height: 1.35;
|
|
133
|
+
text-align: right;
|
|
134
|
+
color: rgb(255 255 255 / 0.75);
|
|
135
|
+
|
|
136
|
+
@include respond(tablet) {
|
|
137
|
+
padding: 0 10% 0.6rem;
|
|
138
|
+
}
|
|
116
139
|
}
|
|
@@ -15,7 +15,7 @@ function formatDate(fechaPublicacion, useLongDate) {
|
|
|
15
15
|
const fecha = date.toLocaleDateString('es-AR', useLongDate
|
|
16
16
|
? { day: '2-digit', month: 'long', year: 'numeric' }
|
|
17
17
|
: { day: '2-digit', month: '2-digit', year: 'numeric' })
|
|
18
|
-
const hora = date.toLocaleTimeString('es-AR', { hour: '2-digit', minute: '2-digit' })
|
|
18
|
+
const hora = date.toLocaleTimeString('es-AR', { hour: '2-digit', minute: '2-digit', hourCycle: 'h23' })
|
|
19
19
|
return `${fecha} - ${hora}`
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -14,6 +14,8 @@ const ampCls = {
|
|
|
14
14
|
listOl: "eo-list-ol",
|
|
15
15
|
quote: "eo-quote",
|
|
16
16
|
image: "eo-image",
|
|
17
|
+
imageWrap: "eo-image-wrap",
|
|
18
|
+
epigrafe: "eo-image-epigrafe",
|
|
17
19
|
delimiter: "eo-delimiter",
|
|
18
20
|
code: "eo-code",
|
|
19
21
|
pullquote: "eo-pullquote",
|
|
@@ -114,23 +116,25 @@ function Block({ block, cls, isAmp }) {
|
|
|
114
116
|
)
|
|
115
117
|
|
|
116
118
|
case "image": {
|
|
117
|
-
const src
|
|
118
|
-
const alt
|
|
119
|
-
const
|
|
119
|
+
const src = block.data.url || block.data.file?.url
|
|
120
|
+
const alt = block.data.altText || block.data.caption || ""
|
|
121
|
+
const epigrafe = block.data.epigrafe
|
|
120
122
|
return (
|
|
121
123
|
<figure className={cls.image}>
|
|
122
|
-
{
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
124
|
+
<div className={cls.imageWrap}>
|
|
125
|
+
{isAmp
|
|
126
|
+
? <img src={src} alt={alt} />
|
|
127
|
+
: <Image
|
|
128
|
+
src={src}
|
|
129
|
+
alt={alt}
|
|
130
|
+
width={0}
|
|
131
|
+
height={0}
|
|
132
|
+
sizes="(max-width: 768px) 100vw, 800px"
|
|
133
|
+
style={{ width: "100%", height: "auto" }}
|
|
134
|
+
/>
|
|
135
|
+
}
|
|
136
|
+
{epigrafe && <p className={cls.epigrafe}>{epigrafe}</p>}
|
|
137
|
+
</div>
|
|
134
138
|
</figure>
|
|
135
139
|
)
|
|
136
140
|
}
|
|
@@ -225,22 +229,27 @@ function Block({ block, cls, isAmp }) {
|
|
|
225
229
|
return <div className={cls.raw} dangerouslySetInnerHTML={{ __html: block.data.html }} suppressHydrationWarning />
|
|
226
230
|
|
|
227
231
|
case "pullquote": {
|
|
228
|
-
const { variant, text } = block.data
|
|
232
|
+
const { variant, text, color, align } = block.data
|
|
229
233
|
const hasClose = variant !== "2"
|
|
234
|
+
// color === '' → el CSS cae a var(--primary-color, #af0437).
|
|
235
|
+
// align ausente (bloques viejos) → default 'center'. `data-align` mueve
|
|
236
|
+
// todo el bloque (comillas + texto); `--pq-text-align` alinea el texto.
|
|
237
|
+
const pqStyle = { "--pq-text-align": align || "center" }
|
|
238
|
+
if (color) pqStyle["--eo-pullquote-color"] = color
|
|
230
239
|
if (isAmp) {
|
|
231
240
|
return (
|
|
232
|
-
<div className={cls.pullquote}>
|
|
241
|
+
<div className={cls.pullquote} style={pqStyle} data-align={align || "center"}>
|
|
233
242
|
<span className={cls.pullquoteOpen}>“</span>
|
|
234
|
-
<p
|
|
243
|
+
<p dangerouslySetInnerHTML={{ __html: text }} suppressHydrationWarning />
|
|
235
244
|
{hasClose && <span className={cls.pullquoteClose}>”</span>}
|
|
236
245
|
</div>
|
|
237
246
|
)
|
|
238
247
|
}
|
|
239
248
|
const pullCls = [cls.pullquote, cls[`pullquoteV${variant}`]].filter(Boolean).join(" ")
|
|
240
249
|
return (
|
|
241
|
-
<div className={pullCls}>
|
|
250
|
+
<div className={pullCls} style={pqStyle} data-align={align || "center"}>
|
|
242
251
|
<span className={cls.pullquoteOpen}>“</span>
|
|
243
|
-
<p
|
|
252
|
+
<p dangerouslySetInnerHTML={{ __html: text }} suppressHydrationWarning />
|
|
244
253
|
{hasClose && <span className={cls.pullquoteClose}>”</span>}
|
|
245
254
|
</div>
|
|
246
255
|
)
|
|
@@ -108,13 +108,28 @@
|
|
|
108
108
|
height: auto;
|
|
109
109
|
display: block;
|
|
110
110
|
}
|
|
111
|
+
}
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
// Wrapper relativo para anclar el epígrafe sobre la imagen (igual que el hero).
|
|
114
|
+
.imageWrap {
|
|
115
|
+
position: relative;
|
|
116
|
+
display: block;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Epígrafe — franja negra sobre el borde inferior de la foto (estilo ArticleHero).
|
|
120
|
+
.epigrafe {
|
|
121
|
+
position: absolute;
|
|
122
|
+
left: 0;
|
|
123
|
+
right: 0;
|
|
124
|
+
bottom: 0;
|
|
125
|
+
z-index: 1;
|
|
126
|
+
margin: 0;
|
|
127
|
+
padding: 10px 14px;
|
|
128
|
+
font-family: "Inter", "Helvetica", Arial, sans-serif;
|
|
129
|
+
font-size: 0.8rem;
|
|
130
|
+
line-height: 1.35;
|
|
131
|
+
color: #fff;
|
|
132
|
+
background: #000;
|
|
118
133
|
}
|
|
119
134
|
|
|
120
135
|
.embed {
|
|
@@ -209,6 +224,71 @@
|
|
|
209
224
|
line-height: 1.6;
|
|
210
225
|
}
|
|
211
226
|
|
|
227
|
+
/* ── Pullquote / Cita destacada ──
|
|
228
|
+
El color usa `--eo-pullquote-color` (parámetro elegido en el CMS);
|
|
229
|
+
si no se setea, cae al color del portal: var(--primary-color, #af0437). */
|
|
230
|
+
.pullquote {
|
|
231
|
+
--pq-color: var(--eo-pullquote-color, var(--primary-color, #af0437));
|
|
232
|
+
/* Comillas a los lados del texto (no arriba/abajo): fila flex con la
|
|
233
|
+
comilla de apertura a la izquierda y la de cierre a la derecha. */
|
|
234
|
+
display: flex;
|
|
235
|
+
align-items: stretch;
|
|
236
|
+
gap: 0.75rem;
|
|
237
|
+
max-width: 500px;
|
|
238
|
+
margin: 2.25rem auto;
|
|
239
|
+
padding: 0.25rem 0;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* La alineación mueve TODO el bloque (comillas + texto) a izq/centro/der. */
|
|
243
|
+
.pullquote[data-align="left"] { margin-inline: 0 auto; }
|
|
244
|
+
.pullquote[data-align="right"] { margin-inline: auto 0; }
|
|
245
|
+
.pullquote[data-align="center"] { margin-inline: auto; }
|
|
246
|
+
|
|
247
|
+
.pullquoteOpen,
|
|
248
|
+
.pullquoteClose {
|
|
249
|
+
flex: 0 0 auto;
|
|
250
|
+
font-family: Georgia, "Times New Roman", serif;
|
|
251
|
+
font-weight: 700;
|
|
252
|
+
font-size: 6.5rem;
|
|
253
|
+
line-height: 1;
|
|
254
|
+
color: var(--pq-color);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* Apertura pegada arriba; cierre pegado abajo — flanqueando el texto.
|
|
258
|
+
line-height 0.1 en el cierre colapsa la caja de línea: así el glifo (que
|
|
259
|
+
se dibuja arriba de su caja) cae hasta el fondo del bloque. */
|
|
260
|
+
.pullquoteOpen { align-self: flex-start; }
|
|
261
|
+
.pullquoteClose { align-self: flex-end; line-height: 0.1; }
|
|
262
|
+
|
|
263
|
+
.pullquote p {
|
|
264
|
+
flex: 1 1 auto;
|
|
265
|
+
/* Permite que el flex item se encoja: sin esto una palabra larga sin
|
|
266
|
+
espacios revienta el max-width del bloque. */
|
|
267
|
+
min-width: 0;
|
|
268
|
+
overflow-wrap: break-word;
|
|
269
|
+
word-break: break-word;
|
|
270
|
+
margin: 0;
|
|
271
|
+
font-family: Georgia, "Times New Roman", serif;
|
|
272
|
+
font-size: 1.6rem;
|
|
273
|
+
font-weight: 700;
|
|
274
|
+
line-height: 1.4;
|
|
275
|
+
color: var(--pq-color);
|
|
276
|
+
text-align: var(--pq-text-align, center);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/* Variante 3 — comillas en color, texto en negro. */
|
|
280
|
+
.pullquoteV3 p { color: #111; }
|
|
281
|
+
|
|
282
|
+
/* Variante 2 — fondo oscuro, texto blanco, sólo comilla de apertura. */
|
|
283
|
+
.pullquoteV2 {
|
|
284
|
+
background: #333;
|
|
285
|
+
border-radius: 6px;
|
|
286
|
+
padding: 1.5rem 1.75rem 2rem;
|
|
287
|
+
|
|
288
|
+
p { color: #fff; }
|
|
289
|
+
.pullquoteClose { display: none; }
|
|
290
|
+
}
|
|
291
|
+
|
|
212
292
|
.table {
|
|
213
293
|
width: 100%;
|
|
214
294
|
border-collapse: collapse;
|
|
@@ -14,6 +14,8 @@ const ampCls = {
|
|
|
14
14
|
listOl: "eo-list-ol",
|
|
15
15
|
quote: "eo-quote",
|
|
16
16
|
image: "eo-image",
|
|
17
|
+
imageWrap: "eo-image-wrap",
|
|
18
|
+
epigrafe: "eo-image-epigrafe",
|
|
17
19
|
delimiter: "eo-delimiter",
|
|
18
20
|
code: "eo-code",
|
|
19
21
|
pullquote: "eo-pullquote",
|
|
@@ -114,23 +116,25 @@ function Block({ block, cls, isAmp }) {
|
|
|
114
116
|
)
|
|
115
117
|
|
|
116
118
|
case "image": {
|
|
117
|
-
const src
|
|
118
|
-
const alt
|
|
119
|
-
const
|
|
119
|
+
const src = block.data.url || block.data.file?.url
|
|
120
|
+
const alt = block.data.altText || block.data.caption || ""
|
|
121
|
+
const epigrafe = block.data.epigrafe
|
|
120
122
|
return (
|
|
121
123
|
<figure className={cls.image}>
|
|
122
|
-
{
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
124
|
+
<div className={cls.imageWrap}>
|
|
125
|
+
{isAmp
|
|
126
|
+
? <img src={src} alt={alt} />
|
|
127
|
+
: <Image
|
|
128
|
+
src={src}
|
|
129
|
+
alt={alt}
|
|
130
|
+
width={0}
|
|
131
|
+
height={0}
|
|
132
|
+
sizes="(max-width: 768px) 100vw, 800px"
|
|
133
|
+
style={{ width: "100%", height: "auto" }}
|
|
134
|
+
/>
|
|
135
|
+
}
|
|
136
|
+
{epigrafe && <p className={cls.epigrafe}>{epigrafe}</p>}
|
|
137
|
+
</div>
|
|
134
138
|
</figure>
|
|
135
139
|
)
|
|
136
140
|
}
|
|
@@ -225,22 +229,27 @@ function Block({ block, cls, isAmp }) {
|
|
|
225
229
|
return <div className={cls.raw} dangerouslySetInnerHTML={{ __html: block.data.html }} />
|
|
226
230
|
|
|
227
231
|
case "pullquote": {
|
|
228
|
-
const { variant, text } = block.data
|
|
232
|
+
const { variant, text, color, align } = block.data
|
|
229
233
|
const hasClose = variant !== "2"
|
|
234
|
+
// color === '' → el CSS cae a var(--primary-color, #af0437).
|
|
235
|
+
// align ausente (bloques viejos) → default 'center'. `data-align` mueve
|
|
236
|
+
// todo el bloque (comillas + texto); `--pq-text-align` alinea el texto.
|
|
237
|
+
const pqStyle = { "--pq-text-align": align || "center" }
|
|
238
|
+
if (color) pqStyle["--eo-pullquote-color"] = color
|
|
230
239
|
if (isAmp) {
|
|
231
240
|
return (
|
|
232
|
-
<div className={cls.pullquote}>
|
|
241
|
+
<div className={cls.pullquote} style={pqStyle} data-align={align || "center"}>
|
|
233
242
|
<span className={cls.pullquoteOpen}>“</span>
|
|
234
|
-
<p
|
|
243
|
+
<p dangerouslySetInnerHTML={{ __html: text }} />
|
|
235
244
|
{hasClose && <span className={cls.pullquoteClose}>”</span>}
|
|
236
245
|
</div>
|
|
237
246
|
)
|
|
238
247
|
}
|
|
239
248
|
const pullCls = [cls.pullquote, cls[`pullquoteV${variant}`]].filter(Boolean).join(" ")
|
|
240
249
|
return (
|
|
241
|
-
<div className={pullCls}>
|
|
250
|
+
<div className={pullCls} style={pqStyle} data-align={align || "center"}>
|
|
242
251
|
<span className={cls.pullquoteOpen}>“</span>
|
|
243
|
-
<p
|
|
252
|
+
<p dangerouslySetInnerHTML={{ __html: text }} />
|
|
244
253
|
{hasClose && <span className={cls.pullquoteClose}>”</span>}
|
|
245
254
|
</div>
|
|
246
255
|
)
|
|
@@ -115,13 +115,28 @@
|
|
|
115
115
|
height: auto;
|
|
116
116
|
display: block;
|
|
117
117
|
}
|
|
118
|
+
}
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
// Wrapper relativo para anclar el epígrafe sobre la imagen (igual que el hero).
|
|
121
|
+
.imageWrap {
|
|
122
|
+
position: relative;
|
|
123
|
+
display: block;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Epígrafe — franja negra sobre el borde inferior de la foto (estilo ArticleHero).
|
|
127
|
+
.epigrafe {
|
|
128
|
+
position: absolute;
|
|
129
|
+
left: 0;
|
|
130
|
+
right: 0;
|
|
131
|
+
bottom: 0;
|
|
132
|
+
z-index: 1;
|
|
133
|
+
margin: 0;
|
|
134
|
+
padding: 10px 14px;
|
|
135
|
+
font-family: "Inter", "Helvetica", Arial, sans-serif;
|
|
136
|
+
font-size: 0.8rem;
|
|
137
|
+
line-height: 1.35;
|
|
138
|
+
color: #fff;
|
|
139
|
+
background: #000;
|
|
125
140
|
}
|
|
126
141
|
|
|
127
142
|
.embed {
|
|
@@ -216,6 +231,71 @@
|
|
|
216
231
|
line-height: 1.6;
|
|
217
232
|
}
|
|
218
233
|
|
|
234
|
+
/* ── Pullquote / Cita destacada ──
|
|
235
|
+
El color usa `--eo-pullquote-color` (parámetro elegido en el CMS);
|
|
236
|
+
si no se setea, cae al color del portal: var(--primary-color, #af0437). */
|
|
237
|
+
.pullquote {
|
|
238
|
+
--pq-color: var(--eo-pullquote-color, var(--primary-color, #af0437));
|
|
239
|
+
/* Comillas a los lados del texto (no arriba/abajo): fila flex con la
|
|
240
|
+
comilla de apertura a la izquierda y la de cierre a la derecha. */
|
|
241
|
+
display: flex;
|
|
242
|
+
align-items: stretch;
|
|
243
|
+
gap: 0.75rem;
|
|
244
|
+
max-width: 500px;
|
|
245
|
+
margin: 2.25rem auto;
|
|
246
|
+
padding: 0.25rem 0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* La alineación mueve TODO el bloque (comillas + texto) a izq/centro/der. */
|
|
250
|
+
.pullquote[data-align="left"] { margin-inline: 0 auto; }
|
|
251
|
+
.pullquote[data-align="right"] { margin-inline: auto 0; }
|
|
252
|
+
.pullquote[data-align="center"] { margin-inline: auto; }
|
|
253
|
+
|
|
254
|
+
.pullquoteOpen,
|
|
255
|
+
.pullquoteClose {
|
|
256
|
+
flex: 0 0 auto;
|
|
257
|
+
font-family: Georgia, "Times New Roman", serif;
|
|
258
|
+
font-weight: 700;
|
|
259
|
+
font-size: 6.5rem;
|
|
260
|
+
line-height: 1;
|
|
261
|
+
color: var(--pq-color);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* Apertura pegada arriba; cierre pegado abajo — flanqueando el texto.
|
|
265
|
+
line-height 0.1 en el cierre colapsa la caja de línea: así el glifo (que
|
|
266
|
+
se dibuja arriba de su caja) cae hasta el fondo del bloque. */
|
|
267
|
+
.pullquoteOpen { align-self: flex-start; }
|
|
268
|
+
.pullquoteClose { align-self: flex-end; line-height: 0.1; }
|
|
269
|
+
|
|
270
|
+
.pullquote p {
|
|
271
|
+
flex: 1 1 auto;
|
|
272
|
+
/* Permite que el flex item se encoja: sin esto una palabra larga sin
|
|
273
|
+
espacios revienta el max-width del bloque. */
|
|
274
|
+
min-width: 0;
|
|
275
|
+
overflow-wrap: break-word;
|
|
276
|
+
word-break: break-word;
|
|
277
|
+
margin: 0;
|
|
278
|
+
font-family: Georgia, "Times New Roman", serif;
|
|
279
|
+
font-size: 1.6rem;
|
|
280
|
+
font-weight: 700;
|
|
281
|
+
line-height: 1.4;
|
|
282
|
+
color: var(--pq-color);
|
|
283
|
+
text-align: var(--pq-text-align, center);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* Variante 3 — comillas en color, texto en negro. */
|
|
287
|
+
.pullquoteV3 p { color: #111; }
|
|
288
|
+
|
|
289
|
+
/* Variante 2 — fondo oscuro, texto blanco, sólo comilla de apertura. */
|
|
290
|
+
.pullquoteV2 {
|
|
291
|
+
background: #333;
|
|
292
|
+
border-radius: 6px;
|
|
293
|
+
padding: 1.5rem 1.75rem 2rem;
|
|
294
|
+
|
|
295
|
+
p { color: #fff; }
|
|
296
|
+
.pullquoteClose { display: none; }
|
|
297
|
+
}
|
|
298
|
+
|
|
219
299
|
.table {
|
|
220
300
|
width: 100%;
|
|
221
301
|
border-collapse: collapse;
|
package/src/index.js
CHANGED
|
@@ -57,6 +57,9 @@ export { default as ArticleHero } from './components/ArticleHero/ArticleHero
|
|
|
57
57
|
export { default as ArticleHeroFull } from './components/ArticleHeroFull/ArticleHeroFull.jsx'
|
|
58
58
|
export { default as ArticleSidebar } from './components/ArticleSidebar/ArticleSidebar.jsx'
|
|
59
59
|
|
|
60
|
+
// === Screens / vistas de detalle de artículo (réplica portable para previews) ===
|
|
61
|
+
export { default as ArticleDetailView } from './components/ArticleDetailView/ArticleDetailView.jsx'
|
|
62
|
+
|
|
60
63
|
// === Headers ===
|
|
61
64
|
export { default as HeaderSimpleSwitch } from './components/Headers/HeaderSimple/HeaderSimpleSwitch/HeaderSimpleSwitch.jsx'
|
|
62
65
|
export { default as HeaderSimpleDesktop } from './components/Headers/HeaderSimple/HeaderSimpleDesktop/HeaderSimpleDesktop.jsx'
|