forked from cms/tibi-svelte-starter
✨ feat: enhance accessibility with skip to main content button and improve navigation handling
🔧 fix: update navigation href resolution to include localized paths 🆕 feat: add new FeatureIcon component for feature boxes 🎨 style: improve styling for prose elements in richtext blocks 🛠️ refactor: streamline medialib image loading and caching logic 📦 chore: update mock data handling to support new medialib entries 🔄 chore: synchronize i18n initialization and locale management 📝 docs: update video tour descriptions to reflect recent changes
This commit is contained in:
+123
-5
@@ -14,11 +14,50 @@
|
||||
// Add new collections here as needed.
|
||||
// ---------------------------------------------------------------------------
|
||||
import contentData from "../../mocking/content.json"
|
||||
import medialibData from "../../mocking/medialib.json"
|
||||
import navigationData from "../../mocking/navigation.json"
|
||||
|
||||
type EJsonObjectId = {
|
||||
$oid: string
|
||||
}
|
||||
|
||||
const mockRegistry: Record<string, Record<string, unknown>[]> = {
|
||||
content: contentData as Record<string, unknown>[],
|
||||
navigation: navigationData as Record<string, unknown>[],
|
||||
content: normalizeMockCollection(contentData as Record<string, unknown>[]),
|
||||
medialib: normalizeMockCollection(medialibData as Record<string, unknown>[]),
|
||||
navigation: normalizeMockCollection(navigationData as Record<string, unknown>[]),
|
||||
}
|
||||
|
||||
function isEJsonObjectId(value: unknown): value is EJsonObjectId {
|
||||
return !!value && typeof value === "object" && "$oid" in value && typeof (value as EJsonObjectId).$oid === "string"
|
||||
}
|
||||
|
||||
function normalizeMockCollection(entries: Record<string, unknown>[]): Record<string, unknown>[] {
|
||||
return entries.map((entry) => normalizeMockValue(entry))
|
||||
}
|
||||
|
||||
function normalizeMockValue<T>(value: T): T {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => normalizeMockValue(item)) as T
|
||||
}
|
||||
|
||||
if (!value || typeof value !== "object") {
|
||||
return value
|
||||
}
|
||||
|
||||
const normalized: Record<string, unknown> = {}
|
||||
for (const [key, nestedValue] of Object.entries(value as Record<string, unknown>)) {
|
||||
normalized[key] = normalizeMockValue(nestedValue)
|
||||
}
|
||||
|
||||
if (isEJsonObjectId(normalized._id)) {
|
||||
normalized._id = normalized._id.$oid
|
||||
}
|
||||
|
||||
if (typeof normalized._id === "string" && normalized.id === undefined) {
|
||||
normalized.id = normalized._id
|
||||
}
|
||||
|
||||
return normalized as T
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -49,9 +88,10 @@ export function mockApiRequest(endpoint: string, options?: ApiOptions, _body?: u
|
||||
// --- Single-item retrieval ---
|
||||
if (itemId) {
|
||||
const item = sourceData.find((e) => e.id === itemId || e._id === itemId)
|
||||
const resultItem = item ? applyLookups(cloneEntry(item), options) : null
|
||||
return {
|
||||
data: item ?? null,
|
||||
count: item ? 1 : 0,
|
||||
data: resultItem,
|
||||
count: resultItem ? 1 : 0,
|
||||
buildTime: null,
|
||||
}
|
||||
}
|
||||
@@ -79,6 +119,8 @@ export function mockApiRequest(endpoint: string, options?: ApiOptions, _body?: u
|
||||
results = results.slice(0, options.limit)
|
||||
}
|
||||
|
||||
results = results.map((entry) => applyLookups(cloneEntry(entry), options))
|
||||
|
||||
// Projection
|
||||
if (options?.projection) {
|
||||
results = applyProjection(results, options.projection)
|
||||
@@ -184,6 +226,81 @@ function getNestedValue(obj: Record<string, unknown>, path: string): unknown {
|
||||
}, obj)
|
||||
}
|
||||
|
||||
function cloneEntry<T>(entry: T): T {
|
||||
return JSON.parse(JSON.stringify(entry)) as T
|
||||
}
|
||||
|
||||
function applyLookups(entry: Record<string, unknown>, options?: ApiOptions): Record<string, unknown> {
|
||||
const lookupSpecs = parseLookupSpecs(options)
|
||||
if (!lookupSpecs.length) return entry
|
||||
|
||||
for (const spec of lookupSpecs) {
|
||||
const [fieldPath, collection] = spec.split(":")
|
||||
if (!fieldPath || !collection) continue
|
||||
|
||||
const lookupSource = mockRegistry[collection]
|
||||
if (!lookupSource) continue
|
||||
|
||||
applyLookupAtPath(entry, fieldPath.split("."), lookupSource)
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
function parseLookupSpecs(options?: ApiOptions): string[] {
|
||||
const rawLookup = [options?.lookup, options?.params?.lookup]
|
||||
.filter((value): value is string => typeof value === "string" && value.length > 0)
|
||||
.flatMap((value) => value.split(","))
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
return Array.from(new Set(rawLookup))
|
||||
}
|
||||
|
||||
function applyLookupAtPath(
|
||||
current: Record<string, unknown>,
|
||||
pathSegments: string[],
|
||||
lookupSource: Record<string, unknown>[]
|
||||
): void {
|
||||
const [segment, ...rest] = pathSegments
|
||||
if (!segment) return
|
||||
|
||||
const value = current[segment]
|
||||
|
||||
if (rest.length === 0) {
|
||||
current._lookup = (current._lookup as Record<string, unknown> | undefined) || {}
|
||||
;(current._lookup as Record<string, unknown>)[segment] = resolveLookupValue(value, lookupSource)
|
||||
return
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((item) => {
|
||||
if (item && typeof item === "object") {
|
||||
applyLookupAtPath(item as Record<string, unknown>, rest, lookupSource)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (value && typeof value === "object") {
|
||||
applyLookupAtPath(value as Record<string, unknown>, rest, lookupSource)
|
||||
}
|
||||
}
|
||||
|
||||
function resolveLookupValue(value: unknown, lookupSource: Record<string, unknown>[]): unknown {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((entryId) => resolveLookupById(entryId, lookupSource))
|
||||
}
|
||||
|
||||
return resolveLookupById(value, lookupSource)
|
||||
}
|
||||
|
||||
function resolveLookupById(value: unknown, lookupSource: Record<string, unknown>[]): Record<string, unknown> | null {
|
||||
if (typeof value !== "string") return null
|
||||
|
||||
return lookupSource.find((entry) => entry.id === value || entry._id === value) || null
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Sort
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -244,7 +361,8 @@ function applyProjection(data: Record<string, unknown>[], projectionStr: string)
|
||||
if (field in entry) result[field] = entry[field]
|
||||
}
|
||||
// Always include id/_id
|
||||
if (entry.id !== undefined) result.id = entry.id
|
||||
if (typeof entry.id === "string") result.id = entry.id
|
||||
else if (typeof entry._id === "string") result.id = entry._id
|
||||
if (entry._id !== undefined) result._id = entry._id
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user