feat: calendar context sensitive now
This commit is contained in:
@@ -68,6 +68,8 @@ export interface PostListTemplateContext {
|
|||||||
} | null;
|
} | null;
|
||||||
min_date: { day: number; month: number; year: number } | null;
|
min_date: { day: number; month: number; year: number } | null;
|
||||||
max_date: { day: number; month: number; year: number } | null;
|
max_date: { day: number; month: number; year: number } | null;
|
||||||
|
calendar_initial_year: number | null;
|
||||||
|
calendar_initial_month: number | null;
|
||||||
is_list_page: boolean;
|
is_list_page: boolean;
|
||||||
is_first_page: boolean;
|
is_first_page: boolean;
|
||||||
is_last_page: boolean;
|
is_last_page: boolean;
|
||||||
@@ -90,6 +92,8 @@ export interface SinglePostTemplateContext {
|
|||||||
post_categories: string[];
|
post_categories: string[];
|
||||||
post_tags: string[];
|
post_tags: string[];
|
||||||
tag_color_by_name: Record<string, string>;
|
tag_color_by_name: Record<string, string>;
|
||||||
|
calendar_initial_year: number | null;
|
||||||
|
calendar_initial_month: number | null;
|
||||||
canonical_post_path_by_slug: Record<string, string>;
|
canonical_post_path_by_slug: Record<string, string>;
|
||||||
canonical_media_path_by_source_path: Record<string, string>;
|
canonical_media_path_by_source_path: Record<string, string>;
|
||||||
}
|
}
|
||||||
@@ -1005,6 +1009,8 @@ export class PageRenderer {
|
|||||||
|
|
||||||
let minDateParts: { day: number; month: number; year: number } | null = null;
|
let minDateParts: { day: number; month: number; year: number } | null = null;
|
||||||
let maxDateParts: { day: number; month: number; year: number } | null = null;
|
let maxDateParts: { day: number; month: number; year: number } | null = null;
|
||||||
|
const calendarInitialDate = posts.length > 0 ? posts[0].createdAt : null;
|
||||||
|
const calendarInitialParts = calendarInitialDate ? toDateParts(calendarInitialDate) : null;
|
||||||
|
|
||||||
const hasRangeHeading = Boolean(
|
const hasRangeHeading = Boolean(
|
||||||
!isFirstPage
|
!isFirstPage
|
||||||
@@ -1060,6 +1066,8 @@ export class PageRenderer {
|
|||||||
: null,
|
: null,
|
||||||
min_date: minDateParts,
|
min_date: minDateParts,
|
||||||
max_date: maxDateParts,
|
max_date: maxDateParts,
|
||||||
|
calendar_initial_year: calendarInitialParts?.year ?? null,
|
||||||
|
calendar_initial_month: calendarInitialParts?.month ?? null,
|
||||||
is_list_page: isListPage,
|
is_list_page: isListPage,
|
||||||
is_first_page: isFirstPage,
|
is_first_page: isFirstPage,
|
||||||
is_last_page: isLastPage,
|
is_last_page: isLastPage,
|
||||||
@@ -1146,6 +1154,8 @@ export class PageRenderer {
|
|||||||
post_categories: postCategories,
|
post_categories: postCategories,
|
||||||
post_tags: postTags,
|
post_tags: postTags,
|
||||||
tag_color_by_name: pageContext.tag_color_by_name ?? {},
|
tag_color_by_name: pageContext.tag_color_by_name ?? {},
|
||||||
|
calendar_initial_year: renderablePost.createdAt.getFullYear(),
|
||||||
|
calendar_initial_month: renderablePost.createdAt.getMonth() + 1,
|
||||||
canonical_post_path_by_slug: mapToRecord(rewriteContext.canonicalPostPathBySlug),
|
canonical_post_path_by_slug: mapToRecord(rewriteContext.canonicalPostPathBySlug),
|
||||||
canonical_media_path_by_source_path: mapToRecord(rewriteContext.canonicalMediaPathBySourcePath),
|
canonical_media_path_by_source_path: mapToRecord(rewriteContext.canonicalMediaPathBySourcePath),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -73,6 +73,36 @@ export const CALENDAR_RUNTIME_JS = String.raw`(() => {
|
|||||||
window.location.assign(pathname);
|
window.location.assign(pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseInitialYearMonth() {
|
||||||
|
const initialYearRaw = button.getAttribute('data-blog-calendar-year');
|
||||||
|
const initialMonthRaw = button.getAttribute('data-blog-calendar-month');
|
||||||
|
|
||||||
|
const initialYear = Number(initialYearRaw);
|
||||||
|
const initialMonth = Number(initialMonthRaw);
|
||||||
|
|
||||||
|
let selectedYear = Number.isInteger(initialYear) && initialYear > 0 ? initialYear : null;
|
||||||
|
let selectedMonth = Number.isInteger(initialMonth) && initialMonth >= 1 && initialMonth <= 12
|
||||||
|
? (initialMonth - 1)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (!Number.isInteger(selectedYear) || !Number.isInteger(selectedMonth)) {
|
||||||
|
const pathname = window.location.pathname || '';
|
||||||
|
const parts = pathname.split('/').filter(Boolean);
|
||||||
|
const pathYear = Number(parts[0]);
|
||||||
|
const pathMonth = Number(parts[1]);
|
||||||
|
|
||||||
|
if (!Number.isInteger(selectedYear) && Number.isInteger(pathYear) && pathYear > 0 && String(parts[0]).length === 4) {
|
||||||
|
selectedYear = pathYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Number.isInteger(selectedMonth) && Number.isInteger(pathMonth) && pathMonth >= 1 && pathMonth <= 12) {
|
||||||
|
selectedMonth = pathMonth - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { selectedYear, selectedMonth };
|
||||||
|
}
|
||||||
|
|
||||||
async function loadCalendarData() {
|
async function loadCalendarData() {
|
||||||
const response = await fetch('/calendar.json', { cache: 'no-store' });
|
const response = await fetch('/calendar.json', { cache: 'no-store' });
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -116,7 +146,10 @@ export const CALENDAR_RUNTIME_JS = String.raw`(() => {
|
|||||||
throw new Error('Vanilla Calendar Pro is unavailable');
|
throw new Error('Vanilla Calendar Pro is unavailable');
|
||||||
}
|
}
|
||||||
|
|
||||||
const calendar = new Calendar('[data-blog-calendar-root]', {
|
const initialYearMonth = parseInitialYearMonth();
|
||||||
|
const calendarOptions = {
|
||||||
|
...(Number.isInteger(initialYearMonth.selectedYear) ? { selectedYear: initialYearMonth.selectedYear } : {}),
|
||||||
|
...(Number.isInteger(initialYearMonth.selectedMonth) ? { selectedMonth: initialYearMonth.selectedMonth } : {}),
|
||||||
onCreateDateEls(_self, dateEl) {
|
onCreateDateEls(_self, dateEl) {
|
||||||
const dateKey = dateEl.dataset.vcDate || '';
|
const dateKey = dateEl.dataset.vcDate || '';
|
||||||
const count = Number(days[dateKey] || 0);
|
const count = Number(days[dateKey] || 0);
|
||||||
@@ -224,7 +257,9 @@ export const CALENDAR_RUNTIME_JS = String.raw`(() => {
|
|||||||
|
|
||||||
navigateTo('/' + String(selectedYear) + '/');
|
navigateTo('/' + String(selectedYear) + '/');
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
|
const calendar = new Calendar('[data-blog-calendar-root]', calendarOptions);
|
||||||
|
|
||||||
calendar.init();
|
calendar.init();
|
||||||
status.textContent = '';
|
status.textContent = '';
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
type="button"
|
type="button"
|
||||||
class="blog-menu-calendar-button"
|
class="blog-menu-calendar-button"
|
||||||
data-blog-calendar-toggle
|
data-blog-calendar-toggle
|
||||||
|
{% if calendar_initial_year %}data-blog-calendar-year="{{ calendar_initial_year }}"{% endif %}
|
||||||
|
{% if calendar_initial_month %}data-blog-calendar-month="{{ calendar_initial_month }}"{% endif %}
|
||||||
aria-label="{{ 'render.calendar.open' | i18n: language }}"
|
aria-label="{{ 'render.calendar.open' | i18n: language }}"
|
||||||
title="{{ 'render.calendar.open' | i18n: language }}"
|
title="{{ 'render.calendar.open' | i18n: language }}"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<nav class="blog-menu">
|
<nav class="blog-menu">
|
||||||
{% if menu_items and menu_items.size > 0 %}
|
{% if menu_items and menu_items.size > 0 %}
|
||||||
{% render 'partials/menu-items', items: menu_items, include_calendar: true, language: language %}
|
{% render 'partials/menu-items', items: menu_items, include_calendar: true, language: language, calendar_initial_year: calendar_initial_year, calendar_initial_month: calendar_initial_month %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% render 'partials/menu-items', items: menu_items, include_calendar: true, language: language %}
|
{% render 'partials/menu-items', items: menu_items, include_calendar: true, language: language, calendar_initial_year: calendar_initial_year, calendar_initial_month: calendar_initial_month %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% render 'partials/menu', menu_items: menu_items, language: language %}
|
{% render 'partials/menu', menu_items: menu_items, language: language, calendar_initial_year: calendar_initial_year, calendar_initial_month: calendar_initial_month %}
|
||||||
|
|
||||||
<section class="post-list" data-template="post-list" data-list-page="{{ is_list_page }}" data-first-page="{{ is_first_page }}" data-last-page="{{ is_last_page }}">
|
<section class="post-list" data-template="post-list" data-list-page="{{ is_list_page }}" data-first-page="{{ is_first_page }}" data-last-page="{{ is_last_page }}">
|
||||||
{% for day_block in day_blocks %}
|
{% for day_block in day_blocks %}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<h1>{{ post.title }}</h1>
|
<h1>{{ post.title }}</h1>
|
||||||
{% render 'partials/menu', menu_items: menu_items, language: language %}
|
{% render 'partials/menu', menu_items: menu_items, language: language, calendar_initial_year: calendar_initial_year, calendar_initial_month: calendar_initial_month %}
|
||||||
{% if post_categories.size > 0 or post_tags.size > 0 %}
|
{% if post_categories.size > 0 or post_tags.size > 0 %}
|
||||||
<div class="single-post-taxonomy" aria-label="{{ 'render.taxonomy.ariaLabel' | i18n: language }}">
|
<div class="single-post-taxonomy" aria-label="{{ 'render.taxonomy.ariaLabel' | i18n: language }}">
|
||||||
{% for category in post_categories %}
|
{% for category in post_categories %}
|
||||||
|
|||||||
@@ -355,6 +355,7 @@ describe('BlogGenerationEngine', () => {
|
|||||||
slug: 'one',
|
slug: 'one',
|
||||||
title: 'One',
|
title: 'One',
|
||||||
categories: ['news'],
|
categories: ['news'],
|
||||||
|
tags: ['updates'],
|
||||||
createdAt: new Date('2025-03-15T10:00:00Z'),
|
createdAt: new Date('2025-03-15T10:00:00Z'),
|
||||||
}),
|
}),
|
||||||
makePost({
|
makePost({
|
||||||
@@ -362,6 +363,7 @@ describe('BlogGenerationEngine', () => {
|
|||||||
slug: 'two',
|
slug: 'two',
|
||||||
title: 'Two',
|
title: 'Two',
|
||||||
categories: ['news'],
|
categories: ['news'],
|
||||||
|
tags: ['updates'],
|
||||||
createdAt: new Date('2025-03-15T12:00:00Z'),
|
createdAt: new Date('2025-03-15T12:00:00Z'),
|
||||||
}),
|
}),
|
||||||
makePost({
|
makePost({
|
||||||
@@ -369,6 +371,7 @@ describe('BlogGenerationEngine', () => {
|
|||||||
slug: 'three',
|
slug: 'three',
|
||||||
title: 'Three',
|
title: 'Three',
|
||||||
categories: ['news'],
|
categories: ['news'],
|
||||||
|
tags: ['updates'],
|
||||||
createdAt: new Date('2025-04-01T10:00:00Z'),
|
createdAt: new Date('2025-04-01T10:00:00Z'),
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
@@ -396,16 +399,31 @@ describe('BlogGenerationEngine', () => {
|
|||||||
|
|
||||||
const indexHtml = await readFile(path.join(tempDir, 'html', 'index.html'), 'utf-8');
|
const indexHtml = await readFile(path.join(tempDir, 'html', 'index.html'), 'utf-8');
|
||||||
expect(indexHtml).toContain('class="blog-menu-calendar-button"');
|
expect(indexHtml).toContain('class="blog-menu-calendar-button"');
|
||||||
|
expect(indexHtml).toContain('data-blog-calendar-year="2025"');
|
||||||
|
expect(indexHtml).toContain('data-blog-calendar-month="4"');
|
||||||
expect(indexHtml).toContain('id="blog-calendar"');
|
expect(indexHtml).toContain('id="blog-calendar"');
|
||||||
expect(indexHtml).toContain('href="/assets/vanilla-calendar.min.css"');
|
expect(indexHtml).toContain('href="/assets/vanilla-calendar.min.css"');
|
||||||
expect(indexHtml).toContain('src="/assets/vanilla-calendar.min.js"');
|
expect(indexHtml).toContain('src="/assets/vanilla-calendar.min.js"');
|
||||||
expect(indexHtml).toContain('src="/assets/calendar-runtime.js"');
|
expect(indexHtml).toContain('src="/assets/calendar-runtime.js"');
|
||||||
|
|
||||||
|
const singleHtml = await readFile(path.join(tempDir, 'html', '2025', '03', '15', 'one', 'index.html'), 'utf-8');
|
||||||
|
expect(singleHtml).toContain('data-blog-calendar-year="2025"');
|
||||||
|
expect(singleHtml).toContain('data-blog-calendar-month="3"');
|
||||||
|
|
||||||
|
const tagArchiveHtml = await readFile(path.join(tempDir, 'html', 'tag', 'updates', 'index.html'), 'utf-8');
|
||||||
|
expect(tagArchiveHtml).toContain('data-blog-calendar-year="2025"');
|
||||||
|
expect(tagArchiveHtml).toContain('data-blog-calendar-month="4"');
|
||||||
|
|
||||||
const calendarRuntime = await readFile(path.join(tempDir, 'html', 'assets', 'calendar-runtime.js'), 'utf-8');
|
const calendarRuntime = await readFile(path.join(tempDir, 'html', 'assets', 'calendar-runtime.js'), 'utf-8');
|
||||||
expect(calendarRuntime).toContain('--blog-calendar-heat-hue');
|
expect(calendarRuntime).toContain('--blog-calendar-heat-hue');
|
||||||
expect(calendarRuntime).toContain('--blog-calendar-heat-alpha');
|
expect(calendarRuntime).toContain('--blog-calendar-heat-alpha');
|
||||||
expect(calendarRuntime).toContain('onCreateMonthEls');
|
expect(calendarRuntime).toContain('onCreateMonthEls');
|
||||||
expect(calendarRuntime).toContain('onCreateYearEls');
|
expect(calendarRuntime).toContain('onCreateYearEls');
|
||||||
|
expect(calendarRuntime).toContain('data-blog-calendar-year');
|
||||||
|
expect(calendarRuntime).toContain('data-blog-calendar-month');
|
||||||
|
expect(calendarRuntime).toContain('window.location.pathname');
|
||||||
|
expect(calendarRuntime).toContain('selectedYear');
|
||||||
|
expect(calendarRuntime).toContain('selectedMonth');
|
||||||
expect(calendarRuntime).not.toContain('blog-calendar-post-count');
|
expect(calendarRuntime).not.toContain('blog-calendar-post-count');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user