@riebeckite/plugin-series 0.0.24 → 0.0.25
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.
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { ResolvedSeriesOptions, SeriesIndex } from "../src/types.js";
|
|
2
2
|
export type SeriesNavigationProps = {
|
|
3
|
-
index
|
|
4
|
-
currentSlug
|
|
3
|
+
index?: SeriesIndex | null;
|
|
4
|
+
currentSlug?: string;
|
|
5
5
|
className?: string;
|
|
6
6
|
options?: Pick<ResolvedSeriesOptions, "heading" | "positionLabel">;
|
|
7
7
|
};
|
|
8
|
-
declare const SeriesNavigation: ({ index, currentSlug, className, options, }: Readonly<SeriesNavigationProps>) => import("hono/
|
|
8
|
+
declare const SeriesNavigation: ({ index: explicitIndex, currentSlug: explicitCurrentSlug, className: explicitClassName, options, }: Readonly<SeriesNavigationProps>) => Promise<import("hono/utils/html").HtmlEscapedString>;
|
|
9
9
|
export default SeriesNavigation;
|
|
@@ -1,19 +1,145 @@
|
|
|
1
|
+
// components/series-navigation.tsx
|
|
2
|
+
import { useArticleContext, useSiteContext } from "@riebeckite/ui";
|
|
3
|
+
import { html } from "hono/html";
|
|
4
|
+
|
|
5
|
+
// src/series.ts
|
|
6
|
+
import {
|
|
7
|
+
escapeHtml,
|
|
8
|
+
escapeHtmlAttribute
|
|
9
|
+
} from "@riebeckite/core";
|
|
10
|
+
var DEFAULT_SERIES_CLASS_NAME = "rb-series";
|
|
11
|
+
var DEFAULT_SERIES_BASE_PATH = "/series";
|
|
12
|
+
function resolveSeriesOptions(options = {}) {
|
|
13
|
+
return {
|
|
14
|
+
key: options.key ?? "series",
|
|
15
|
+
orderKey: options.orderKey ?? "series_order",
|
|
16
|
+
titleKey: options.titleKey ?? "series_title",
|
|
17
|
+
heading: options.heading ?? true,
|
|
18
|
+
className: options.className ?? DEFAULT_SERIES_CLASS_NAME,
|
|
19
|
+
positionLabel: options.positionLabel ?? false,
|
|
20
|
+
basePath: normalizeBasePath(options.basePath ?? DEFAULT_SERIES_BASE_PATH)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function collectSeriesIndexes(manifest, options = {}) {
|
|
24
|
+
const resolved = resolveSeriesOptions(options);
|
|
25
|
+
const groups = /* @__PURE__ */ new Map();
|
|
26
|
+
const titles = /* @__PURE__ */ new Map();
|
|
27
|
+
for (const entry of manifest.entries) {
|
|
28
|
+
const name = readSeriesName(entry, resolved.key);
|
|
29
|
+
if (!name) continue;
|
|
30
|
+
const group = groups.get(name);
|
|
31
|
+
if (group) group.push(entry);
|
|
32
|
+
else groups.set(name, [entry]);
|
|
33
|
+
if (!titles.has(name)) {
|
|
34
|
+
const title = readString(entry, resolved.titleKey);
|
|
35
|
+
if (title) titles.set(name, title);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const indexes = [];
|
|
39
|
+
for (const [name, entries] of groups) {
|
|
40
|
+
const members = [...entries].sort((a, b) => compareEntries(a, b, resolved)).map((entry) => toMember(manifest, entry, resolved, name));
|
|
41
|
+
indexes.push({ name, title: titles.get(name) ?? name, members });
|
|
42
|
+
}
|
|
43
|
+
return indexes;
|
|
44
|
+
}
|
|
45
|
+
var getSeriesNavigation = ({ view, entry, index }) => {
|
|
46
|
+
if (!index || !view.findDiscoverableBySlug(entry.slug)) return null;
|
|
47
|
+
const members = index.members.filter(
|
|
48
|
+
(member) => view.findDiscoverableBySlug(member.slug)
|
|
49
|
+
);
|
|
50
|
+
return members.some((member) => member.slug === entry.slug) ? { ...index, members } : null;
|
|
51
|
+
};
|
|
52
|
+
function toMember(manifest, entry, resolved, name) {
|
|
53
|
+
return {
|
|
54
|
+
slug: entry.slug,
|
|
55
|
+
title: entry.title || entry.slug,
|
|
56
|
+
permalink: manifest.bySlug.get(entry.slug)?.permalink ?? entry.permalink,
|
|
57
|
+
order: readSeriesOrder(entry, resolved.orderKey) ?? null,
|
|
58
|
+
series: name
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function compareEntries(a, b, resolved) {
|
|
62
|
+
const orderA = readSeriesOrder(a, resolved.orderKey) ?? Number.POSITIVE_INFINITY;
|
|
63
|
+
const orderB = readSeriesOrder(b, resolved.orderKey) ?? Number.POSITIVE_INFINITY;
|
|
64
|
+
if (orderA !== orderB) return orderA < orderB ? -1 : 1;
|
|
65
|
+
const dateA = readSeriesDate(a) ?? Number.POSITIVE_INFINITY;
|
|
66
|
+
const dateB = readSeriesDate(b) ?? Number.POSITIVE_INFINITY;
|
|
67
|
+
if (dateA !== dateB) return dateA < dateB ? -1 : 1;
|
|
68
|
+
if (a.title !== b.title) return a.title < b.title ? -1 : 1;
|
|
69
|
+
if (a.slug !== b.slug) return a.slug < b.slug ? -1 : 1;
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
function readSeriesName(entry, key) {
|
|
73
|
+
return readString(entry, key);
|
|
74
|
+
}
|
|
75
|
+
function readSeriesOrder(entry, key) {
|
|
76
|
+
const value = entry.frontmatter[key];
|
|
77
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
80
|
+
function readString(entry, key) {
|
|
81
|
+
const value = entry.frontmatter[key];
|
|
82
|
+
if (typeof value !== "string") return void 0;
|
|
83
|
+
const trimmed = value.trim();
|
|
84
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
85
|
+
}
|
|
86
|
+
function readSeriesDate(entry) {
|
|
87
|
+
for (const key of ["date", "created", "published"]) {
|
|
88
|
+
const time = parseDate(entry.frontmatter[key]);
|
|
89
|
+
if (time !== void 0) return time;
|
|
90
|
+
}
|
|
91
|
+
return void 0;
|
|
92
|
+
}
|
|
93
|
+
function parseDate(value) {
|
|
94
|
+
if (value instanceof Date) {
|
|
95
|
+
const time = value.getTime();
|
|
96
|
+
return Number.isFinite(time) ? time : void 0;
|
|
97
|
+
}
|
|
98
|
+
if (typeof value === "string") {
|
|
99
|
+
const time = Date.parse(value);
|
|
100
|
+
return Number.isNaN(time) ? void 0 : time;
|
|
101
|
+
}
|
|
102
|
+
return void 0;
|
|
103
|
+
}
|
|
104
|
+
function normalizeBasePath(value) {
|
|
105
|
+
const trimmed = value.trim();
|
|
106
|
+
if (trimmed === "") return "";
|
|
107
|
+
const prefixed = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
108
|
+
return prefixed.length > 1 ? prefixed.replace(/\/+$/, "") : prefixed;
|
|
109
|
+
}
|
|
110
|
+
|
|
1
111
|
// components/series-navigation.tsx
|
|
2
112
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
3
|
-
var SeriesNavigation = (({
|
|
4
|
-
index,
|
|
5
|
-
currentSlug,
|
|
6
|
-
className
|
|
113
|
+
var SeriesNavigation = (async ({
|
|
114
|
+
index: explicitIndex,
|
|
115
|
+
currentSlug: explicitCurrentSlug,
|
|
116
|
+
className: explicitClassName,
|
|
7
117
|
options
|
|
8
118
|
}) => {
|
|
9
|
-
|
|
119
|
+
const site = useSiteContext();
|
|
120
|
+
const article = useArticleContext();
|
|
121
|
+
const configured = site ? findSeriesOptions(site.config.plugins) : void 0;
|
|
122
|
+
const currentSlug = explicitCurrentSlug ?? article?.entry?.slug;
|
|
123
|
+
const sourceIndex = explicitIndex ?? (site && article?.entry ? collectSeriesIndexes(site.manifest, configured).find(
|
|
124
|
+
(candidate) => candidate.members.some(
|
|
125
|
+
(member) => member.slug === article.entry?.slug
|
|
126
|
+
)
|
|
127
|
+
) ?? null : null);
|
|
128
|
+
const index = site && article?.entry && sourceIndex ? await getSeriesNavigation({
|
|
129
|
+
view: site.view,
|
|
130
|
+
entry: article.entry,
|
|
131
|
+
index: sourceIndex
|
|
132
|
+
}) : sourceIndex;
|
|
133
|
+
if (!currentSlug) return html``;
|
|
134
|
+
if (!index || index.members.length === 0) return html``;
|
|
10
135
|
const position = index.members.findIndex(
|
|
11
136
|
(member) => member.slug === currentSlug
|
|
12
137
|
);
|
|
13
138
|
const previous = position > 0 ? index.members[position - 1] : void 0;
|
|
14
139
|
const next = position >= 0 && position < index.members.length - 1 ? index.members[position + 1] : void 0;
|
|
15
|
-
const heading = options?.heading ?? true;
|
|
16
|
-
const positionLabel = options?.positionLabel ?? false;
|
|
140
|
+
const heading = options?.heading ?? configured?.heading ?? true;
|
|
141
|
+
const positionLabel = options?.positionLabel ?? configured?.positionLabel ?? false;
|
|
142
|
+
const className = explicitClassName ?? configured?.className ?? "rb-series";
|
|
17
143
|
return /* @__PURE__ */ jsxs(
|
|
18
144
|
"nav",
|
|
19
145
|
{
|
|
@@ -49,6 +175,9 @@ var SeriesNavigation = (({
|
|
|
49
175
|
);
|
|
50
176
|
});
|
|
51
177
|
var series_navigation_default = SeriesNavigation;
|
|
178
|
+
function findSeriesOptions(plugins) {
|
|
179
|
+
return plugins.find((plugin) => plugin.name === "series")?.options;
|
|
180
|
+
}
|
|
52
181
|
export {
|
|
53
182
|
series_navigation_default as default
|
|
54
183
|
};
|
package/dist/index.js
CHANGED
|
@@ -291,21 +291,39 @@ function normalizeBasePath(value) {
|
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
// components/series-navigation.tsx
|
|
294
|
+
import { useArticleContext, useSiteContext } from "@riebeckite/ui";
|
|
295
|
+
import { html } from "hono/html";
|
|
294
296
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
295
|
-
var SeriesNavigation = (({
|
|
296
|
-
index,
|
|
297
|
-
currentSlug,
|
|
298
|
-
className
|
|
297
|
+
var SeriesNavigation = (async ({
|
|
298
|
+
index: explicitIndex,
|
|
299
|
+
currentSlug: explicitCurrentSlug,
|
|
300
|
+
className: explicitClassName,
|
|
299
301
|
options
|
|
300
302
|
}) => {
|
|
301
|
-
|
|
303
|
+
const site = useSiteContext();
|
|
304
|
+
const article = useArticleContext();
|
|
305
|
+
const configured = site ? findSeriesOptions(site.config.plugins) : void 0;
|
|
306
|
+
const currentSlug = explicitCurrentSlug ?? article?.entry?.slug;
|
|
307
|
+
const sourceIndex = explicitIndex ?? (site && article?.entry ? collectSeriesIndexes(site.manifest, configured).find(
|
|
308
|
+
(candidate) => candidate.members.some(
|
|
309
|
+
(member) => member.slug === article.entry?.slug
|
|
310
|
+
)
|
|
311
|
+
) ?? null : null);
|
|
312
|
+
const index = site && article?.entry && sourceIndex ? await getSeriesNavigation({
|
|
313
|
+
view: site.view,
|
|
314
|
+
entry: article.entry,
|
|
315
|
+
index: sourceIndex
|
|
316
|
+
}) : sourceIndex;
|
|
317
|
+
if (!currentSlug) return html``;
|
|
318
|
+
if (!index || index.members.length === 0) return html``;
|
|
302
319
|
const position = index.members.findIndex(
|
|
303
320
|
(member) => member.slug === currentSlug
|
|
304
321
|
);
|
|
305
322
|
const previous = position > 0 ? index.members[position - 1] : void 0;
|
|
306
323
|
const next = position >= 0 && position < index.members.length - 1 ? index.members[position + 1] : void 0;
|
|
307
|
-
const heading = options?.heading ?? true;
|
|
308
|
-
const positionLabel = options?.positionLabel ?? false;
|
|
324
|
+
const heading = options?.heading ?? configured?.heading ?? true;
|
|
325
|
+
const positionLabel = options?.positionLabel ?? configured?.positionLabel ?? false;
|
|
326
|
+
const className = explicitClassName ?? configured?.className ?? "rb-series";
|
|
309
327
|
return /* @__PURE__ */ jsxs(
|
|
310
328
|
"nav",
|
|
311
329
|
{
|
|
@@ -341,6 +359,9 @@ var SeriesNavigation = (({
|
|
|
341
359
|
);
|
|
342
360
|
});
|
|
343
361
|
var series_navigation_default = SeriesNavigation;
|
|
362
|
+
function findSeriesOptions(plugins) {
|
|
363
|
+
return plugins.find((plugin) => plugin.name === "series")?.options;
|
|
364
|
+
}
|
|
344
365
|
|
|
345
366
|
// index.ts
|
|
346
367
|
function series(options = {}) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-series",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,11 +22,12 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"hono": "^4.12.25",
|
|
25
|
-
"@riebeckite/core": "0.0.
|
|
25
|
+
"@riebeckite/core": "0.0.25",
|
|
26
|
+
"@riebeckite/ui": "0.0.25"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"tsx": "^4.22.4",
|
|
29
|
-
"@riebeckite/test": "0.0.
|
|
30
|
+
"@riebeckite/test": "0.0.25"
|
|
30
31
|
},
|
|
31
32
|
"publishConfig": {
|
|
32
33
|
"access": "public"
|