@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: SeriesIndex | null;
4
- currentSlug: string;
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/jsx/jsx-dev-runtime").JSX.Element;
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 = "rb-series",
113
+ var SeriesNavigation = (async ({
114
+ index: explicitIndex,
115
+ currentSlug: explicitCurrentSlug,
116
+ className: explicitClassName,
7
117
  options
8
118
  }) => {
9
- if (!index || index.members.length === 0) return null;
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 = "rb-series",
297
+ var SeriesNavigation = (async ({
298
+ index: explicitIndex,
299
+ currentSlug: explicitCurrentSlug,
300
+ className: explicitClassName,
299
301
  options
300
302
  }) => {
301
- if (!index || index.members.length === 0) return null;
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.24",
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.24"
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.24"
30
+ "@riebeckite/test": "0.0.25"
30
31
  },
31
32
  "publishConfig": {
32
33
  "access": "public"