feat(glossary): add step 21 smart navigation
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { GLOSSARY_NAV_DEFAULTS } from "./glossary-navigation-defaults";
|
||||
|
||||
export type GlossaryEntry = CollectionEntry<"glossaire">;
|
||||
|
||||
@@ -16,6 +17,32 @@ export type GlossaryRelationBlock = GlossaryRelationSection & {
|
||||
className: string;
|
||||
};
|
||||
|
||||
export type GlossarySmartNavigationPathKey =
|
||||
| "understand"
|
||||
| "deepen"
|
||||
| "compare"
|
||||
| "apply";
|
||||
|
||||
export type GlossarySmartNavigationPath = {
|
||||
key: GlossarySmartNavigationPathKey;
|
||||
label: string;
|
||||
entries: GlossaryEntry[];
|
||||
};
|
||||
|
||||
export type GlossarySmartNavigationFlow = {
|
||||
key: string;
|
||||
label: string;
|
||||
primaryNext?: GlossaryEntry;
|
||||
primaryReason?: string;
|
||||
};
|
||||
|
||||
export type GlossarySmartNavigation = {
|
||||
primaryNext?: GlossaryEntry;
|
||||
primaryReason?: string;
|
||||
paths: GlossarySmartNavigationPath[];
|
||||
flows: GlossarySmartNavigationFlow[];
|
||||
};
|
||||
|
||||
export type GlossaryHomeStats = {
|
||||
totalEntries: number;
|
||||
paradigmesCount: number;
|
||||
@@ -122,6 +149,16 @@ export const FAMILY_SECTION_TITLES: Record<string, string> = {
|
||||
epistemologie: "Outillage épistémologique",
|
||||
};
|
||||
|
||||
export const SMART_NAV_PATH_LABELS: Record<
|
||||
GlossarySmartNavigationPathKey,
|
||||
string
|
||||
> = {
|
||||
understand: "Comprendre",
|
||||
deepen: "Approfondir",
|
||||
compare: "Comparer",
|
||||
apply: "Appliquer",
|
||||
};
|
||||
|
||||
const PREFERRED_PARADIGME_SLUGS = [
|
||||
"gouvernementalite",
|
||||
"gouvernementalite-algorithmique",
|
||||
@@ -202,35 +239,35 @@ export function uniqueGlossaryEntries(
|
||||
}
|
||||
|
||||
export function slugsOfGlossaryEntries(
|
||||
entries: GlossaryEntry[] = [],
|
||||
): Set<string> {
|
||||
const slugs = new Set<string>();
|
||||
|
||||
for (const entry of entries) {
|
||||
const slug = slugOfGlossaryEntry(entry);
|
||||
if (!slug) continue;
|
||||
slugs.add(slug);
|
||||
}
|
||||
|
||||
return slugs;
|
||||
}
|
||||
|
||||
export function excludeGlossaryEntries(
|
||||
entries: GlossaryEntry[] = [],
|
||||
excluded: Iterable<string> = [],
|
||||
): GlossaryEntry[] {
|
||||
const excludedSlugs = new Set(
|
||||
Array.from(excluded)
|
||||
.map((value) => normalizeGlossarySlug(value))
|
||||
.filter(Boolean),
|
||||
);
|
||||
|
||||
return entries.filter((entry) => {
|
||||
const slug = slugOfGlossaryEntry(entry);
|
||||
return Boolean(slug) && !excludedSlugs.has(slug);
|
||||
});
|
||||
entries: GlossaryEntry[] = [],
|
||||
): Set<string> {
|
||||
const slugs = new Set<string>();
|
||||
|
||||
for (const entry of entries) {
|
||||
const slug = slugOfGlossaryEntry(entry);
|
||||
if (!slug) continue;
|
||||
slugs.add(slug);
|
||||
}
|
||||
|
||||
return slugs;
|
||||
}
|
||||
|
||||
export function excludeGlossaryEntries(
|
||||
entries: GlossaryEntry[] = [],
|
||||
excluded: Iterable<string> = [],
|
||||
): GlossaryEntry[] {
|
||||
const excludedSlugs = new Set(
|
||||
Array.from(excluded)
|
||||
.map((value) => normalizeGlossarySlug(value))
|
||||
.filter(Boolean),
|
||||
);
|
||||
|
||||
return entries.filter((entry) => {
|
||||
const slug = slugOfGlossaryEntry(entry);
|
||||
return Boolean(slug) && !excludedSlugs.has(slug);
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveGlossaryEntriesInSourceOrder(
|
||||
slugs: string[] = [],
|
||||
allEntries: GlossaryEntry[] = [],
|
||||
@@ -467,6 +504,96 @@ export function getRelationBlocks(
|
||||
].filter((block) => block.items.length > 0);
|
||||
}
|
||||
|
||||
export function getGlossarySmartNavigation(
|
||||
currentEntry: GlossaryEntry,
|
||||
allEntries: GlossaryEntry[] = [],
|
||||
): GlossarySmartNavigation {
|
||||
const currentSlug = slugOfGlossaryEntry(currentEntry);
|
||||
const rawNavigation = currentEntry.data.navigation;
|
||||
const defaultNavigation = GLOSSARY_NAV_DEFAULTS[familyOf(currentEntry)];
|
||||
|
||||
const navigationSource = rawNavigation ?? {
|
||||
primaryNext: undefined,
|
||||
primaryReason: undefined,
|
||||
paths: defaultNavigation ?? {},
|
||||
flows: {},
|
||||
};
|
||||
|
||||
const primaryNext = resolveGlossaryEntriesInSourceOrder(
|
||||
navigationSource.primaryNext ? [navigationSource.primaryNext] : [],
|
||||
allEntries,
|
||||
).find((entry) => slugOfGlossaryEntry(entry) !== currentSlug);
|
||||
|
||||
const pathKeys: GlossarySmartNavigationPathKey[] = [
|
||||
"understand",
|
||||
"deepen",
|
||||
"compare",
|
||||
"apply",
|
||||
];
|
||||
|
||||
const paths = pathKeys
|
||||
.map((key) => {
|
||||
const entries = resolveGlossaryEntriesInSourceOrder(
|
||||
navigationSource.paths?.[key] ?? [],
|
||||
allEntries,
|
||||
).filter((entry) => slugOfGlossaryEntry(entry) !== currentSlug);
|
||||
|
||||
return {
|
||||
key,
|
||||
label: SMART_NAV_PATH_LABELS[key],
|
||||
entries,
|
||||
};
|
||||
})
|
||||
.filter((path) => path.entries.length > 0);
|
||||
|
||||
const flows = Object.entries(navigationSource.flows ?? {})
|
||||
.map(([key, flow]) => {
|
||||
const primaryNext = resolveGlossaryEntriesInSourceOrder(
|
||||
flow.primaryNext ? [flow.primaryNext] : [],
|
||||
allEntries,
|
||||
).find((entry) => slugOfGlossaryEntry(entry) !== currentSlug);
|
||||
|
||||
return {
|
||||
key,
|
||||
label: flow.label,
|
||||
primaryNext,
|
||||
primaryReason: flow.primaryReason,
|
||||
};
|
||||
})
|
||||
.filter((flow) => flow.primaryNext);
|
||||
|
||||
if (primaryNext || paths.length > 0 || flows.length > 0) {
|
||||
return {
|
||||
primaryNext,
|
||||
primaryReason: navigationSource.primaryReason,
|
||||
paths,
|
||||
flows,
|
||||
};
|
||||
}
|
||||
|
||||
const fallbackEntries = uniqueGlossaryEntries([
|
||||
...resolveGlossaryEntriesInSourceOrder(
|
||||
currentEntry.data.related ?? [],
|
||||
allEntries,
|
||||
),
|
||||
...resolveGlossaryEntriesInSourceOrder(
|
||||
currentEntry.data.seeAlso ?? [],
|
||||
allEntries,
|
||||
),
|
||||
]).filter((entry) => slugOfGlossaryEntry(entry) !== currentSlug);
|
||||
|
||||
const fallbackPrimary = fallbackEntries[0];
|
||||
|
||||
return {
|
||||
primaryNext: fallbackPrimary,
|
||||
primaryReason: fallbackPrimary
|
||||
? "Ce lien prolonge directement les relations conceptuelles de cette fiche."
|
||||
: undefined,
|
||||
paths: [],
|
||||
flows: [],
|
||||
};
|
||||
}
|
||||
|
||||
export function getRelationSections(
|
||||
entry: GlossaryEntry,
|
||||
allEntries: GlossaryEntry[] = [],
|
||||
|
||||
Reference in New Issue
Block a user