@cedros/data-react 0.1.7 → 0.1.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.
Files changed (33) hide show
  1. package/dist/docsContract.d.ts +56 -0
  2. package/dist/docsContract.js +136 -0
  3. package/dist/react/docs.d.ts +14 -0
  4. package/dist/react/docs.js +207 -0
  5. package/dist/react/index.d.ts +2 -1
  6. package/dist/react/index.js +1 -0
  7. package/dist/react/server.d.ts +2 -1
  8. package/dist/react/server.js +1 -0
  9. package/dist/react/types.d.ts +17 -0
  10. package/dist/site-templates/BlogTemplates.js +16 -12
  11. package/dist/site-templates/DocsSidebar.d.ts +1 -1
  12. package/dist/site-templates/DocsSidebar.js +2 -2
  13. package/dist/site-templates/DocsTemplates.js +13 -14
  14. package/dist/site-templates/SiteFooter.js +1 -1
  15. package/dist/site-templates/SiteLayout.js +1 -1
  16. package/dist/site-templates/TopNav.js +1 -1
  17. package/dist/site-templates/blog-styles.css +259 -0
  18. package/dist/site-templates/blogControls.js +2 -2
  19. package/dist/site-templates/blogTemplateUi.d.ts +10 -0
  20. package/dist/site-templates/blogTemplateUi.js +4 -0
  21. package/dist/site-templates/content-styles.css +127 -309
  22. package/dist/site-templates/contentIndex.d.ts +4 -11
  23. package/dist/site-templates/contentIndex.js +10 -0
  24. package/dist/site-templates/contentUi.js +4 -3
  25. package/dist/site-templates/dashboard-styles.css +109 -0
  26. package/dist/site-templates/docs-layout.css +372 -0
  27. package/dist/site-templates/docs-styles.css +288 -96
  28. package/dist/site-templates/docsNavigation.d.ts +23 -3
  29. package/dist/site-templates/docsNavigation.js +178 -75
  30. package/dist/site-templates/index.d.ts +2 -1
  31. package/dist/site-templates/index.js +2 -1
  32. package/dist/site-templates/styles.css +283 -201
  33. package/package.json +1 -1
@@ -1,14 +1,16 @@
1
+ import { normalizeDocsSlug, sortDocsEntries, } from "../docsContract.js";
1
2
  import { isRouteActive, normalizeRoutePath } from "./routing.js";
2
3
  export function buildDocsSidebarSections(docs, basePath = "/docs") {
3
4
  const grouped = new Map();
4
5
  docs.forEach((doc) => {
5
6
  const sectionLabel = doc.category ?? "Overview";
6
- const sectionKey = sectionLabel.toLowerCase().replace(/\s+/gu, "-");
7
+ const sectionKey = normalizeFragment(doc.sectionKey ?? sectionLabel) || "overview";
7
8
  const current = grouped.get(sectionKey) ?? { label: sectionLabel, items: [] };
8
9
  current.items.push({
9
10
  key: doc.slug,
10
- label: doc.title,
11
- href: buildDocHref(basePath, doc.slug)
11
+ label: doc.navLabel ?? doc.title,
12
+ href: buildDocHref(basePath, doc.slug),
13
+ badge: doc.badge,
12
14
  });
13
15
  grouped.set(sectionKey, current);
14
16
  });
@@ -16,64 +18,77 @@ export function buildDocsSidebarSections(docs, basePath = "/docs") {
16
18
  .map(([key, value]) => ({
17
19
  key,
18
20
  label: value.label,
19
- items: [...value.items].sort((left, right) => left.label.localeCompare(right.label))
21
+ items: [...value.items].sort((left, right) => left.label.localeCompare(right.label)),
20
22
  }))
21
23
  .sort((left, right) => left.label.localeCompare(right.label));
22
24
  }
23
25
  export function fetchDocEntry(docs, slugOrPath, options = {}) {
24
- const normalizedLookup = normalizeDocLookup(slugOrPath, options.basePath);
25
- return docs.find((doc) => {
26
- const normalizedDocSlug = normalizeDocSlug(doc.slug);
27
- return normalizedDocSlug === normalizedLookup || (!normalizedLookup && normalizedDocSlug === "index");
28
- });
29
- }
30
- export function buildDocsTree(docs, basePath = "/docs") {
31
- const sections = [];
32
- const grouped = new Map();
33
- docs.forEach((doc) => {
34
- const sectionLabel = doc.category ?? "Overview";
35
- const sectionKey = sectionLabel.toLowerCase().replace(/\s+/gu, "-");
36
- const section = grouped.get(sectionKey) ?? { key: sectionKey, label: sectionLabel, nodes: [] };
37
- if (!grouped.has(sectionKey)) {
38
- grouped.set(sectionKey, section);
39
- sections.push(section);
40
- }
41
- insertDocTreeNode(section.nodes, doc, basePath);
42
- });
43
- return sections;
26
+ const normalizedLookup = normalizeDocsSlug(slugOrPath, options);
27
+ return docs.find((doc) => docLookupCandidates(doc, options).some((candidate) => matchesDocLookup(candidate, normalizedLookup)));
44
28
  }
45
- export function buildHierarchicalDocsSidebarSections(docs, basePath = "/docs") {
46
- return buildDocsTree(docs, basePath)
29
+ /**
30
+ * Builds sidebar-ready docs navigation from a normalized docs collection.
31
+ *
32
+ * Groups by product automatically when multiple products exist and preserves explicit ordering fields when present.
33
+ */
34
+ export function buildDocsNavigation(docs, options = {}) {
35
+ const sections = buildDocsTreeSections(docs, options)
47
36
  .map((section) => ({
48
37
  key: section.key,
49
38
  label: section.label,
50
- items: flattenDocsTreeNodes(section.nodes)
39
+ items: flattenDocsTreeNodes(section.nodes),
51
40
  }))
52
41
  .filter((section) => section.items.length > 0);
42
+ const activeSections = withActiveDocsSidebar(sections, options.currentPath);
43
+ if (!options.collapsible || !options.currentPath) {
44
+ return activeSections;
45
+ }
46
+ return activeSections.map((section) => ({
47
+ ...section,
48
+ collapsed: !section.items.some((item) => item.isActive),
49
+ }));
53
50
  }
54
- export function buildDocsPrevNext(docs, currentSlugOrPath, options = {}) {
51
+ export function buildDocsTree(docs, basePath = "/docs") {
52
+ return buildDocsTreeSections(docs, { basePath });
53
+ }
54
+ export function buildHierarchicalDocsSidebarSections(docs, basePath = "/docs") {
55
+ return buildDocsNavigation(docs, { basePath });
56
+ }
57
+ /**
58
+ * Builds previous/next links from the same ordered docs collection used for navigation.
59
+ *
60
+ * When the current doc belongs to a product, pager links stay inside that product by default.
61
+ */
62
+ export function buildDocsArticlePager(docs, currentSlugOrPath, options = {}) {
55
63
  const current = fetchDocEntry(docs, currentSlugOrPath, options);
56
64
  if (!current) {
57
65
  return {};
58
66
  }
59
- const currentSlug = normalizeDocSlug(current.slug);
60
- const currentIndex = docs.findIndex((doc) => normalizeDocSlug(doc.slug) === currentSlug);
67
+ const scopedProduct = options.productSlug ?? current.productSlug;
68
+ const orderedDocs = resolveOrderedDocs(docs, {
69
+ ...options,
70
+ productSlug: scopedProduct,
71
+ });
72
+ const currentIndex = orderedDocs.findIndex((doc) => normalizeDocsSlug(doc.slug, options) === normalizeDocsSlug(current.slug, options));
61
73
  if (currentIndex === -1) {
62
74
  return {};
63
75
  }
64
76
  return {
65
- previousDoc: buildDocsPagerLink(docs[currentIndex - 1], options.basePath),
66
- nextDoc: buildDocsPagerLink(docs[currentIndex + 1], options.basePath)
77
+ previousDoc: buildDocsPagerLink(orderedDocs[currentIndex - 1], options.basePath),
78
+ nextDoc: buildDocsPagerLink(orderedDocs[currentIndex + 1], options.basePath),
67
79
  };
68
80
  }
81
+ export function buildDocsPrevNext(docs, currentSlugOrPath, options = {}) {
82
+ return buildDocsArticlePager(docs, currentSlugOrPath, options);
83
+ }
69
84
  export function withActiveDocsSidebar(sections, currentPath) {
70
85
  if (!currentPath) {
71
86
  return sections.map((section) => ({
72
87
  ...section,
73
88
  items: section.items.map((item) => ({
74
89
  ...item,
75
- isActive: item.isActive ?? false
76
- }))
90
+ isActive: item.isActive ?? false,
91
+ })),
77
92
  }));
78
93
  }
79
94
  const candidateLengths = sections
@@ -89,64 +104,122 @@ export function withActiveDocsSidebar(sections, currentPath) {
89
104
  isRouteActive(item.href, currentPath);
90
105
  return {
91
106
  ...item,
92
- isActive: item.isActive ?? inferredActive
107
+ isActive: item.isActive ?? inferredActive,
93
108
  };
94
- })
109
+ }),
95
110
  }));
96
111
  }
97
- function buildDocHref(basePath, slug) {
98
- const normalizedBasePath = normalizeRoutePath(basePath);
99
- const normalizedSlug = normalizeDocSlug(slug);
100
- if (!normalizedSlug) {
101
- return normalizedBasePath;
102
- }
103
- if (normalizedBasePath === "/") {
104
- return `/${normalizedSlug}`;
105
- }
106
- return `${normalizedBasePath}/${normalizedSlug}`;
112
+ function buildDocsTreeSections(docs, options) {
113
+ const orderedDocs = resolveOrderedDocs(docs, options);
114
+ const groupByProduct = shouldGroupByProduct(orderedDocs, options.groupByProduct);
115
+ const sections = [];
116
+ const sectionMap = new Map();
117
+ orderedDocs.forEach((doc) => {
118
+ const identity = sectionIdentity(doc, groupByProduct);
119
+ let section = sectionMap.get(identity.key);
120
+ if (!section) {
121
+ section = { key: identity.key, label: identity.label, nodes: [] };
122
+ sectionMap.set(identity.key, section);
123
+ sections.push(section);
124
+ }
125
+ insertDocTreeNode(section.nodes, doc, options.basePath ?? "/docs", groupByProduct, options);
126
+ });
127
+ return sections;
107
128
  }
108
- function normalizeDocSlug(slug) {
109
- return slug.trim().replace(/^\/+|\/+$/gu, "");
129
+ function resolveOrderedDocs(docs, options) {
130
+ return sortDocsEntries(docs.filter((doc) => matchesNavigationScope(doc, options)), { preserveSourceOrder: options.preserveOrder !== false });
110
131
  }
111
- function normalizeDocLookup(value, basePath = "/docs") {
112
- const normalizedValue = normalizeRoutePath(value).replace(/^\/+/u, "");
113
- const normalizedBasePath = normalizeRoutePath(basePath).replace(/^\/+/u, "");
114
- if (!normalizedBasePath) {
115
- return normalizedValue;
132
+ function matchesNavigationScope(doc, options) {
133
+ if (options.productSlug) {
134
+ const productSlug = normalizeDocsSlug(doc.productSlug ?? "", options);
135
+ if (!productSlug || productSlug !== normalizeDocsSlug(options.productSlug, options)) {
136
+ return false;
137
+ }
138
+ }
139
+ if (options.sectionKey) {
140
+ const sectionKey = normalizeFragment(doc.sectionKey);
141
+ if (!sectionKey || sectionKey !== normalizeFragment(options.sectionKey)) {
142
+ return false;
143
+ }
116
144
  }
117
- if (normalizedValue === normalizedBasePath) {
118
- return "";
145
+ return true;
146
+ }
147
+ function shouldGroupByProduct(docs, explicit) {
148
+ if (explicit !== undefined) {
149
+ return explicit;
119
150
  }
120
- if (normalizedValue.startsWith(`${normalizedBasePath}/`)) {
121
- return normalizedValue.slice(normalizedBasePath.length + 1);
151
+ return new Set(docs.map((doc) => normalizeFragment(doc.productSlug)).filter(Boolean)).size > 1;
152
+ }
153
+ function sectionIdentity(doc, groupByProduct) {
154
+ if (groupByProduct) {
155
+ const productSlug = normalizeDocsSlug(doc.productSlug ?? doc.slug.split("/")[0] ?? "");
156
+ return {
157
+ key: productSlug || "docs",
158
+ label: doc.productLabel ?? humanizeDocSegment(productSlug || "docs"),
159
+ };
122
160
  }
123
- return normalizedValue;
161
+ const label = doc.sectionLabel ?? doc.category ?? "Overview";
162
+ return {
163
+ key: normalizeFragment(doc.sectionKey ?? label) || "overview",
164
+ label,
165
+ };
124
166
  }
125
- function insertDocTreeNode(nodes, doc, basePath) {
126
- const segments = normalizedDocSegments(doc.slug);
167
+ function insertDocTreeNode(nodes, doc, basePath, groupByProduct, options) {
168
+ const fullSegments = normalizedDocSegments(doc.slug, options);
169
+ const visibleSegments = visibleDocSegments(doc, groupByProduct, options);
170
+ if (visibleSegments.length === 0) {
171
+ nodes.push({
172
+ key: doc.slug,
173
+ slug: normalizeDocsSlug(doc.slug, options),
174
+ label: doc.navLabel ?? doc.title,
175
+ href: buildDocHref(basePath, doc.slug),
176
+ entry: doc,
177
+ children: [],
178
+ });
179
+ return;
180
+ }
127
181
  let cursor = nodes;
128
- segments.forEach((segment, index) => {
129
- const slug = segments.slice(0, index + 1).join("/");
130
- let node = cursor.find((candidate) => candidate.slug === slug);
182
+ const hiddenCount = fullSegments.length - visibleSegments.length;
183
+ visibleSegments.forEach((segment, index) => {
184
+ const fullSlug = fullSegments.slice(0, hiddenCount + index + 1).join("/");
185
+ let node = cursor.find((candidate) => candidate.slug === fullSlug);
131
186
  if (!node) {
132
187
  node = {
133
- key: slug || "index",
134
- slug,
188
+ key: fullSlug || "index",
189
+ slug: fullSlug,
135
190
  label: humanizeDocSegment(segment),
136
- children: []
191
+ children: [],
137
192
  };
138
193
  cursor.push(node);
139
194
  }
140
- if (index === segments.length - 1) {
195
+ if (index === visibleSegments.length - 1) {
141
196
  node.entry = doc;
142
197
  node.href = buildDocHref(basePath, doc.slug);
143
- node.label = doc.title;
198
+ node.label = doc.navLabel ?? doc.title;
144
199
  }
145
200
  cursor = node.children;
146
201
  });
147
202
  }
148
- function normalizedDocSegments(slug) {
149
- const normalized = normalizeDocSlug(slug);
203
+ function visibleDocSegments(doc, groupByProduct, options) {
204
+ const segments = normalizedDocSegments(doc.slug, options);
205
+ if (groupByProduct) {
206
+ const productSlug = normalizeDocsSlug(doc.productSlug ?? "", options);
207
+ if (productSlug && segments[0] === productSlug) {
208
+ return segments.slice(1);
209
+ }
210
+ return segments;
211
+ }
212
+ const withoutProduct = doc.productSlug && segments[0] === normalizeDocsSlug(doc.productSlug, options)
213
+ ? segments.slice(1)
214
+ : segments;
215
+ const sectionKey = normalizeFragment(doc.sectionKey);
216
+ if (sectionKey && withoutProduct[0] === sectionKey) {
217
+ return withoutProduct.slice(1);
218
+ }
219
+ return withoutProduct;
220
+ }
221
+ function normalizedDocSegments(slug, options) {
222
+ const normalized = normalizeDocsSlug(slug, options);
150
223
  return normalized ? normalized.split("/").filter(Boolean) : ["index"];
151
224
  }
152
225
  function flattenDocsTreeNodes(nodes, depth = 0) {
@@ -157,7 +230,8 @@ function flattenDocsTreeNodes(nodes, depth = 0) {
157
230
  key: node.key,
158
231
  label: node.label,
159
232
  href: node.href,
160
- depth: Math.min(depth, 2)
233
+ depth: Math.min(depth, 2),
234
+ badge: node.entry.badge,
161
235
  });
162
236
  }
163
237
  items.push(...flattenDocsTreeNodes(node.children, depth + 1));
@@ -171,11 +245,40 @@ function buildDocsPagerLink(doc, basePath = "/docs") {
171
245
  return {
172
246
  title: doc.title,
173
247
  href: buildDocHref(basePath, doc.slug),
174
- entry: doc
248
+ entry: doc,
175
249
  };
176
250
  }
251
+ function buildDocHref(basePath, slug) {
252
+ const normalizedBasePath = normalizeRoutePath(basePath);
253
+ const normalizedSlug = normalizeDocsSlug(slug, { basePath });
254
+ if (!normalizedSlug || normalizedSlug === "index") {
255
+ return normalizedBasePath;
256
+ }
257
+ if (normalizedBasePath === "/") {
258
+ return `/${normalizedSlug}`;
259
+ }
260
+ return `${normalizedBasePath}/${normalizedSlug}`;
261
+ }
262
+ function docLookupCandidates(doc, options) {
263
+ const candidates = [normalizeDocsSlug(doc.slug, options)];
264
+ doc.aliases?.forEach((alias) => {
265
+ const normalized = normalizeDocsSlug(alias, options);
266
+ if (normalized) {
267
+ candidates.push(normalized);
268
+ }
269
+ });
270
+ return [...new Set(candidates.filter(Boolean))];
271
+ }
272
+ function matchesDocLookup(candidate, lookup) {
273
+ return candidate === lookup || (!lookup && (candidate === "" || candidate === "index"));
274
+ }
275
+ function normalizeFragment(value) {
276
+ return (value ?? "")
277
+ .trim()
278
+ .toLowerCase()
279
+ .replace(/[^a-z0-9]+/gu, "-")
280
+ .replace(/^-+|-+$/gu, "");
281
+ }
177
282
  function humanizeDocSegment(segment) {
178
- return segment
179
- .replace(/[-_]+/gu, " ")
180
- .replace(/\b\w/gu, (char) => char.toUpperCase());
283
+ return segment.replace(/[-_]+/gu, " ").replace(/\b\w/gu, (char) => char.toUpperCase());
181
284
  }
@@ -5,7 +5,8 @@ export { DashboardShell, type DashboardShellProps, type DashboardNavItem } from
5
5
  export { DashboardOverviewTemplate, type DashboardOverviewTemplateProps, type DashboardPanel, type DashboardStat } from "./DashboardOverviewTemplate.js";
6
6
  export { isRouteActive, normalizeRoutePath, withActiveRouteState, type RouteAwareItem, type RouteMatcherOptions } from "./routing.js";
7
7
  export { buildContentListHref, collectFilterValues, collectDimensionValues, matchesFilterDimensions, prepareBlogIndex, prepareDocsIndex, type BlogIndexEntry, type BlogIndexQuery, type DocsIndexEntry, type DocsIndexQuery, type FilterDimension, type FilterDimensionValues, type PaginationResult } from "./contentIndex.js";
8
- export { buildDocsSidebarSections, buildDocsTree, buildHierarchicalDocsSidebarSections, buildDocsPrevNext, fetchDocEntry, withActiveDocsSidebar, type DocsTreeNode, type DocsTreeSection, type DocsPagerLink, type DocsPrevNext, type FetchDocEntryOptions, type DocsSidebarItem, type DocsSidebarSection } from "./docsNavigation.js";
8
+ export { buildDocsNavigation, buildDocsArticlePager, buildDocsSidebarSections, buildDocsTree, buildHierarchicalDocsSidebarSections, buildDocsPrevNext, fetchDocEntry, withActiveDocsSidebar, type DocsTreeNode, type DocsTreeSection, type DocsPagerLink, type DocsPrevNext, type DocsNavigationOptions, type FetchDocEntryOptions, type DocsSidebarItem, type DocsSidebarSection } from "./docsNavigation.js";
9
+ export { normalizeDocsHref, normalizeDocsSlug, type DocsBreadcrumb, type DocsEntryRecord, type DocsNormalizationOptions, type DocsTocHeading, } from "../docsContract.js";
9
10
  export { MarkdownContent, type MarkdownContentProps } from "./MarkdownContent.js";
10
11
  export { extractTocFromMarkdown, type TocEntry } from "./tocExtractor.js";
11
12
  export { DocsSidebar, type DocsSidebarProps } from "./DocsSidebar.js";
@@ -5,7 +5,8 @@ export { DashboardShell } from "./DashboardShell.js";
5
5
  export { DashboardOverviewTemplate } from "./DashboardOverviewTemplate.js";
6
6
  export { isRouteActive, normalizeRoutePath, withActiveRouteState } from "./routing.js";
7
7
  export { buildContentListHref, collectFilterValues, collectDimensionValues, matchesFilterDimensions, prepareBlogIndex, prepareDocsIndex } from "./contentIndex.js";
8
- export { buildDocsSidebarSections, buildDocsTree, buildHierarchicalDocsSidebarSections, buildDocsPrevNext, fetchDocEntry, withActiveDocsSidebar } from "./docsNavigation.js";
8
+ export { buildDocsNavigation, buildDocsArticlePager, buildDocsSidebarSections, buildDocsTree, buildHierarchicalDocsSidebarSections, buildDocsPrevNext, fetchDocEntry, withActiveDocsSidebar } from "./docsNavigation.js";
9
+ export { normalizeDocsHref, normalizeDocsSlug, } from "../docsContract.js";
9
10
  export { MarkdownContent } from "./MarkdownContent.js";
10
11
  export { extractTocFromMarkdown } from "./tocExtractor.js";
11
12
  export { DocsSidebar } from "./DocsSidebar.js";