Compare commits
16 Commits
b62122eb59
...
98043b37dc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98043b37dc | ||
|
|
ad86d2b086 | ||
|
|
568f4de9a5 | ||
|
|
27357e5c3c | ||
|
|
3b0b0d226d | ||
|
|
2a64076f85 | ||
|
|
388a9e6b35 | ||
|
|
469d568f38 | ||
|
|
b7549f05d1 | ||
|
|
7d51bb057a | ||
|
|
54ea02fc15 | ||
|
|
59a65c3eb5 | ||
|
|
c2ad212895 | ||
|
|
3a8887d47b | ||
|
|
49ab88505b | ||
|
|
d1b3ffd81c |
|
|
@ -28,7 +28,6 @@ function getRevueAuthors($revueID)
|
|||
}
|
||||
}
|
||||
return array_unique($authors);
|
||||
s;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -47,3 +46,82 @@ function count_user_articles($userID, $postType)
|
|||
$query = new WP_Query($args);
|
||||
return $query->found_posts;
|
||||
}
|
||||
|
||||
|
||||
function build_sommaire_from_content($postID)
|
||||
{
|
||||
$blocks = parse_blocks(get_the_content($postID));
|
||||
$titleBlocks = array_filter(
|
||||
$blocks,
|
||||
function ($block) {
|
||||
return $block['blockName'] === 'core/heading' && isset($block['attrs']['level']) && in_array($block['attrs']['level'], array(2, 3), true);
|
||||
}
|
||||
);
|
||||
|
||||
$outputIndex = [];
|
||||
|
||||
foreach ($titleBlocks as $block) {
|
||||
$title = strip_tags($block['innerHTML']);
|
||||
$anchor = $block['attrs']['idName'] ?? sanitize_title($title);
|
||||
$level = $block['attrs']['level'];
|
||||
|
||||
$outputIndex[] = [
|
||||
'title' => $title,
|
||||
'anchor' => $anchor,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
return $outputIndex;
|
||||
}
|
||||
|
||||
|
||||
function build_footnotes_index_from_content($content)
|
||||
{
|
||||
if (empty($content)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$footnotes = [];
|
||||
$dom = new DOMDocument();
|
||||
// On supprime les erreurs de parsing pour le HTML5
|
||||
@$dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||
|
||||
$links = $dom->getElementsByTagName('a');
|
||||
foreach ($links as $key => $link) {
|
||||
if ($link->hasAttribute('class') && strpos($link->getAttribute('class'), 'footnote-reference') !== false) {
|
||||
$footnote_content = $link->getAttribute('footnote-content');
|
||||
if (!empty($footnote_content)) {
|
||||
$footnotes[] = array(
|
||||
'key' => $key + 1,
|
||||
'anchorID' => $key + 1,
|
||||
'content' => $footnote_content
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $footnotes;
|
||||
}
|
||||
|
||||
add_filter('the_content', 'apply_footnotes_urls_to_content', 10);
|
||||
|
||||
function apply_footnotes_urls_to_content($content)
|
||||
{
|
||||
$post_type = get_post_type();
|
||||
if ($post_type !== 'articles' && !is_admin()) return $content;
|
||||
|
||||
$footnotes = build_footnotes_index_from_content($content);
|
||||
$dom = new DOMDocument();
|
||||
@$dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||
|
||||
$links = $dom->getElementsByTagName('a');
|
||||
foreach ($links as $key => $link) {
|
||||
if ($link->hasAttribute('class') && strpos($link->getAttribute('class'), 'footnote-reference') !== false) {
|
||||
$link->setAttribute('id', 'footnote-' . $key + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return $dom->saveHTML();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@
|
|||
@import './components/article-content.css';
|
||||
@import './components/content-meta.css';
|
||||
@import './components/post-header.css';
|
||||
@import './components/sommaire-index.css';
|
||||
@import './components/index-panel.css';
|
||||
@import './components/footnotes-index.css';
|
||||
|
||||
/* ########### PAGES ############ */
|
||||
@import './pages/singles.css';
|
||||
|
|
|
|||
|
|
@ -1,2 +1,5 @@
|
|||
.article-content {
|
||||
.article-tags-list {
|
||||
@apply mb-8;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
resources/css/components/footnotes-index.css
Normal file
19
resources/css/components/footnotes-index.css
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
.footnotes-index {
|
||||
counter-reset: footnote-index;
|
||||
|
||||
@apply mb-4;
|
||||
li {
|
||||
@apply mb-4;
|
||||
&:before {
|
||||
content: counter(footnote-index);
|
||||
counter-increment: footnote-index;
|
||||
@apply mr-2 text-white text-base inline-flex bg-primary rounded-full w-8 h-8 items-center justify-center;
|
||||
}
|
||||
}
|
||||
a {
|
||||
@apply text-carhop-gray opacity-80;
|
||||
&[active='true'] {
|
||||
@apply text-primary font-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
resources/css/components/index-panel.css
Normal file
34
resources/css/components/index-panel.css
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
.index-panel {
|
||||
@apply p-6 m-0 border-primary border my-2;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
|
||||
&__header {
|
||||
@apply flex gap-4 border-b border-gray-300 mb-8;
|
||||
|
||||
button {
|
||||
@apply pb-3 text-carhop-gray opacity-60 relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
button[aria-selected='true'] {
|
||||
@apply text-primary opacity-100;
|
||||
&:after {
|
||||
@apply bg-primary;
|
||||
content: '';
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
overflow-wrap: anywhere;
|
||||
ul[aria-hidden='true'] {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
}
|
||||
}
|
||||
&__title {
|
||||
@apply text-3xl font-normal;
|
||||
@apply text-3xl font-normal uppercase;
|
||||
}
|
||||
&__date {
|
||||
@apply capitalize pt-3 block;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
.post-header {
|
||||
@apply bg-purple-50 text-primary py-32;
|
||||
@apply bg-primary text-white py-32;
|
||||
|
||||
h1.post-header__title,
|
||||
h2.post-header__title {
|
||||
@apply uppercase font-medium text-7xl;
|
||||
@apply uppercase font-medium text-7xl text-white;
|
||||
line-height: 1.2;
|
||||
}
|
||||
&__inner {
|
||||
|
|
@ -15,7 +15,9 @@
|
|||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.content-meta__revue-issue {
|
||||
@apply bg-white text-primary;
|
||||
}
|
||||
.thumbnail-wrapper {
|
||||
@apply bg-red-200;
|
||||
img {
|
||||
|
|
@ -29,7 +31,7 @@
|
|||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
&__label {
|
||||
@apply uppercase font-bold text-lg;
|
||||
@apply uppercase font-bold text-lg text-white;
|
||||
letter-spacing: 0.2em;
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +40,7 @@
|
|||
@apply flex gap-4;
|
||||
|
||||
&__button {
|
||||
@apply bg-white text-carhop-green-700 px-4 py-2 font-normal rounded-full border-primary w-fit border-2 flex items-center gap-2;
|
||||
@apply bg-white text-carhop-green-700 px-4 py-2 font-normal rounded-full w-fit flex items-center gap-2;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
&:hover {
|
||||
transform: scale(1.05);
|
||||
|
|
@ -55,5 +57,12 @@
|
|||
}
|
||||
|
||||
.article-meta__related-revue a {
|
||||
@apply hover:underline underline-offset-8;
|
||||
@apply hover:underline underline-offset-8 text-white;
|
||||
text-decoration-color: #fff;
|
||||
text-decoration-thickness: 1px;
|
||||
}
|
||||
|
||||
.article-meta__value {
|
||||
@apply text-white font-light tracking-wide;
|
||||
letter-spacing: 0.0015em;
|
||||
}
|
||||
|
|
|
|||
9
resources/css/components/sommaire-index.css
Normal file
9
resources/css/components/sommaire-index.css
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
.sommaire-index {
|
||||
@apply list-none;
|
||||
li {
|
||||
@apply mb-4;
|
||||
}
|
||||
a {
|
||||
@apply text-xl text-carhop-gray opacity-80;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,30 @@
|
|||
.page--single-revue,
|
||||
.page--single-articles {
|
||||
.content-wrapper {
|
||||
@apply container mx-auto grid grid-cols-12 gap-12 py-12;
|
||||
grid-template-columns: 1fr 4fr;
|
||||
@apply container mx-auto grid grid-cols-12 gap-12 py-12 items-start;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
}
|
||||
|
||||
.top-toolbar {
|
||||
@apply col-span-2;
|
||||
|
||||
.tablist {
|
||||
@apply flex gap-12 border-b border-primary;
|
||||
button {
|
||||
@apply text-xl mt-8 nunito pb-4;
|
||||
|
||||
&[aria-selected='true'] {
|
||||
@apply text-primary border-b-4 border-primary;
|
||||
}
|
||||
&[aria-selected='false'] {
|
||||
@apply text-carhop-gray opacity-80;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
@apply sticky top-0 left-0 h-fit;
|
||||
.search-field {
|
||||
input {
|
||||
@apply border border-primary w-full;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import menuInit from './header';
|
||||
import singleRevue from './single-revue';
|
||||
import singlesInit from './singles';
|
||||
import singlesInit from './singles/singles';
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
menuInit();
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
export default function singles(): void {
|
||||
const isSingleRevue: HTMLElement | null =
|
||||
document.querySelector('.page--single-revue');
|
||||
const isSingleArticle: HTMLElement | null =
|
||||
document.querySelector('.page--single-articles');
|
||||
if (!isSingleRevue && !isSingleArticle) return;
|
||||
|
||||
handleCiteButton();
|
||||
}
|
||||
|
||||
function handleCiteButton(): void {
|
||||
const citeButton: HTMLElement | null =
|
||||
document.querySelector(
|
||||
'.socials-buttons__button--cite'
|
||||
);
|
||||
const citeReference: HTMLElement | null =
|
||||
document.querySelector('#cite-reference');
|
||||
if (!citeButton || !citeReference) return;
|
||||
|
||||
if (!window.isSecureContext) {
|
||||
citeButton.setAttribute('disabled', 'true');
|
||||
citeButton.setAttribute(
|
||||
'title',
|
||||
'Vous devez utiliser un navigation sécurisé (https) pour copier la citation'
|
||||
);
|
||||
}
|
||||
|
||||
citeButton.addEventListener('click', () => {
|
||||
const textToCopy = citeReference.textContent;
|
||||
if (!textToCopy) return;
|
||||
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard
|
||||
.writeText(textToCopy)
|
||||
.then(() => {
|
||||
const notyf = new Notyf({
|
||||
duration: 4000,
|
||||
ripple: false,
|
||||
dismissible: true,
|
||||
types: [
|
||||
{
|
||||
type: 'success',
|
||||
icon: {
|
||||
className: 'notyf__icon--success',
|
||||
tagName: 'i',
|
||||
},
|
||||
},
|
||||
],
|
||||
position: {
|
||||
x: 'right',
|
||||
y: 'top',
|
||||
},
|
||||
});
|
||||
notyf.success(
|
||||
'Citation copiée dans le presse-papiers ! <br> Vous pouvez maintenant la coller dans votre document.'
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to copy text: ', err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
80
resources/js/singles/index-panel.ts
Normal file
80
resources/js/singles/index-panel.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
export default function handleIndexPanels(): void {
|
||||
const indexPanel = document.querySelector('.index-panel');
|
||||
if (!indexPanel) return;
|
||||
|
||||
observeTabsButtons();
|
||||
observeFootnotesLinks();
|
||||
}
|
||||
|
||||
// HANDLE TABS
|
||||
function observeTabsButtons(): void {
|
||||
const buttons = document.querySelectorAll('.index-panel__header button');
|
||||
|
||||
buttons.forEach((button) => {
|
||||
button.addEventListener('click', () => {
|
||||
toggleActiveTabsButton(button as HTMLElement);
|
||||
toggleActiveTabsPanel(button as HTMLElement);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function toggleActiveTabsButton(button: HTMLElement): void {
|
||||
const buttons = document.querySelectorAll('.index-panel__header button');
|
||||
buttons.forEach((button) => {
|
||||
button.setAttribute('aria-selected', 'false');
|
||||
});
|
||||
button.setAttribute('aria-selected', 'true');
|
||||
}
|
||||
|
||||
function toggleActiveTabsPanel(activeButton: HTMLElement): void {
|
||||
const dataIndex = activeButton.getAttribute('data-index');
|
||||
const activePanel = document.querySelector<HTMLElement>(
|
||||
`.index-panel__content > [data-index="${dataIndex}"]`
|
||||
);
|
||||
if (!dataIndex || !activePanel) return;
|
||||
|
||||
// Hide all buttons and panels
|
||||
const allButtons = document.querySelectorAll<HTMLElement>('.index-panel__header button');
|
||||
const allPanels = document.querySelectorAll<HTMLElement>('.index-panel__content > [data-index]');
|
||||
|
||||
allButtons.forEach((button) => {
|
||||
button.setAttribute('aria-selected', 'false');
|
||||
});
|
||||
|
||||
allPanels.forEach((panel) => {
|
||||
panel.setAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
// Active the button and the panel
|
||||
activeButton.setAttribute('aria-selected', 'true');
|
||||
activePanel.setAttribute('aria-hidden', 'false');
|
||||
}
|
||||
|
||||
function observeFootnotesLinks(): void {
|
||||
const footnotesButtons = document.querySelectorAll('.footnotes-index a');
|
||||
|
||||
footnotesButtons.forEach((footnoteButton) => {
|
||||
footnoteButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const target = e.target as HTMLElement;
|
||||
const targetId = target.getAttribute('href');
|
||||
|
||||
if (!targetId) return;
|
||||
|
||||
document.querySelector(targetId)?.scrollIntoView({ behavior: 'smooth' });
|
||||
toggleActiveFootnote(footnoteButton as HTMLElement);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function toggleActiveFootnote(button: HTMLElement): void {
|
||||
const buttons = document.querySelectorAll('footnote-reference-item');
|
||||
|
||||
const footnotesItems = document.querySelectorAll('.footnote-reference-item');
|
||||
|
||||
footnotesItems.forEach((footnoteItem) => {
|
||||
footnoteItem.setAttribute('active', 'false');
|
||||
});
|
||||
|
||||
button.setAttribute('active', 'true');
|
||||
}
|
||||
94
resources/js/singles/singles.ts
Normal file
94
resources/js/singles/singles.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import handleIndexPanels from './index-panel';
|
||||
|
||||
export default function singles(): void {
|
||||
const isSingleRevue: HTMLElement | null = document.querySelector('.page--single-revue');
|
||||
const isSingleArticle: HTMLElement | null = document.querySelector('.page--single-articles');
|
||||
if (!isSingleRevue && !isSingleArticle) return;
|
||||
|
||||
injectIdToNativeTitles();
|
||||
|
||||
handleIndexPanels();
|
||||
handleCiteButton();
|
||||
handleSmoothScrollToTitle();
|
||||
}
|
||||
|
||||
function handleCiteButton(): void {
|
||||
const citeButton: HTMLElement | null = document.querySelector('.socials-buttons__button--cite');
|
||||
const citeReference: HTMLElement | null = document.querySelector('#cite-reference');
|
||||
if (!citeButton || !citeReference) return;
|
||||
|
||||
if (!window.isSecureContext) {
|
||||
citeButton.setAttribute('disabled', 'true');
|
||||
citeButton.setAttribute(
|
||||
'title',
|
||||
'Vous devez utiliser un navigation sécurisé (https) pour copier la citation'
|
||||
);
|
||||
}
|
||||
|
||||
citeButton.addEventListener('click', () => {
|
||||
const textToCopy = citeReference.textContent;
|
||||
if (!textToCopy) return;
|
||||
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard
|
||||
.writeText(textToCopy)
|
||||
.then(() => {
|
||||
const notyf = new Notyf({
|
||||
duration: 4000,
|
||||
ripple: false,
|
||||
dismissible: true,
|
||||
types: [
|
||||
{
|
||||
type: 'success',
|
||||
icon: {
|
||||
className: 'notyf__icon--success',
|
||||
tagName: 'i',
|
||||
},
|
||||
},
|
||||
],
|
||||
position: {
|
||||
x: 'right',
|
||||
y: 'top',
|
||||
},
|
||||
});
|
||||
notyf.success(
|
||||
'Citation copiée dans le presse-papiers ! <br> Vous pouvez maintenant la coller dans votre document.'
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to copy text: ', err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function injectIdToNativeTitles(): void {
|
||||
const titles = document.querySelectorAll('.content-area h2, .content-area h3');
|
||||
titles.forEach((title) => {
|
||||
const titleText = title.textContent || '';
|
||||
const slug = titleText
|
||||
.toLowerCase()
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/[^\w-]+/g, '');
|
||||
title.setAttribute('id', slug);
|
||||
});
|
||||
}
|
||||
|
||||
function handleSmoothScrollToTitle(): void {
|
||||
const sommaireTitles: NodeListOf<Element> = document.querySelectorAll('.sommaire-index li a');
|
||||
for (const title of sommaireTitles) {
|
||||
title.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const target = title.getAttribute('href');
|
||||
if (!target) return;
|
||||
|
||||
const targetElement = document.querySelector(target);
|
||||
if (!targetElement) return;
|
||||
|
||||
targetElement.scrollIntoView({ behavior: 'smooth' });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,18 @@ $articleID = $args['ID'];
|
|||
$articleContent = get_the_content($articleID);
|
||||
$articleTitle = get_the_title($articleID);
|
||||
$citeReference = get_field('cite_reference', $articleID);
|
||||
$tags = get_the_terms($articleID, 'etiquettes');
|
||||
?>
|
||||
|
||||
<article class="article-content">
|
||||
<h1><?php echo $articleTitle; ?></h1>
|
||||
<?php echo $articleContent; ?>
|
||||
<ul class="article-tags-list">
|
||||
<?php foreach ($tags as $tag) : ?>
|
||||
<li class="article-tag">
|
||||
<?php echo $tag->name; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php the_content(); ?>
|
||||
<?php if ($citeReference) : ?>
|
||||
<p id="cite-reference">
|
||||
<?php echo $citeReference; ?>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ $issueNumber = get_field('issue_number', $currentRevueID);
|
|||
|
||||
$post_type = get_post_type();
|
||||
$hasThumbnail = has_post_thumbnail();
|
||||
$citeReference = get_field('cite_reference', $currentRevueID);
|
||||
|
||||
?>
|
||||
|
||||
<section class="post-header post-header--<?php echo $post_type; ?> ">
|
||||
|
|
@ -42,7 +44,7 @@ $hasThumbnail = has_post_thumbnail();
|
|||
<?php if ($post_type === 'revues') : ?>
|
||||
<h1 class="post-header__title"> <?php echo $revueTitle; ?></h1>
|
||||
<?php elseif ($post_type === 'articles') : ?>
|
||||
<h2 class="post-header__title"> <?php echo $revueTitle; ?></h2>
|
||||
<h2 class="post-header__title"> <?php echo get_the_title(); ?></h2>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="post-details">
|
||||
|
|
@ -62,7 +64,7 @@ $hasThumbnail = has_post_thumbnail();
|
|||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="socials-buttons">
|
||||
<button class="socials-buttons__button socials-buttons__button--cite">
|
||||
<button class="socials-buttons__button socials-buttons__button--cite" disabled="<?php echo empty($citeReference) ? 'true' : 'false'; ?>" title="<?php echo empty($citeReference) ? 'Citation non disponible' : 'Copier la citation'; ?>">
|
||||
<img src="<?php echo get_template_directory_uri(); ?>/resources/img/icons/carhop-citer-article.svg" alt="">
|
||||
Citer
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user