Files
NODEDC_DESIGN_GUIDELINE/apps/catalog/src/designProfile.ts
T

126 lines
3.9 KiB
TypeScript

import type { GlassMaterialSettings, NodedcTheme } from "@nodedc/ui-core";
import type { ToolbarPlacement } from "@nodedc/ui-react";
import type { FaviconAssetUrls } from "./favicon.js";
import type { MapPageLayout, MapPageSettings } from "./MapFixturePreview.js";
import type { MapPresentationProfile } from "./mapPresentationProfile.js";
export type MaterialDraft = {
panelHex: string;
panelOpacity: number;
fieldHex: string;
fieldOpacity: number;
nestedHex: string;
};
export type MapDesignProfileFragment = {
schemaVersion: 1;
templateId: "map";
templateVersion: string;
settings: MapPageSettings;
presentationProfiles: MapPresentationProfile[];
};
export type DesignProfilePageFragment = MapDesignProfileFragment;
export interface StoredLayout {
theme?: NodedcTheme;
accentHex?: string;
materialByTheme?: Record<NodedcTheme, MaterialDraft>;
environment?: {
lightColor?: string;
brightness?: number;
glowDistance?: number;
connectionType?: string;
connectionColor?: string;
usePortColors?: boolean;
fillColor?: string;
fillOpacity?: number;
strokeColor?: string;
strokeOpacity?: number;
};
media?: {
source?: "file" | "url";
url?: string;
fileName?: string;
fileSrc?: string;
visible?: boolean;
logoSource?: "file" | "url";
logoUrl?: string;
logoFileName?: string;
logoFileSrc?: string;
faviconFileName?: string;
faviconAssets?: FaviconAssetUrls;
};
glass?: GlassMaterialSettings;
toolbar?: {
placement?: ToolbarPlacement;
background?: string;
border?: string;
outline?: string;
minSize?: number;
maxSize?: number;
lensCount?: number;
autoHide?: boolean;
};
/** Design fragments are keyed by a registered Page Library type/version. */
pageTypes?: Record<string, DesignProfilePageFragment>;
}
export type MapDesignOverrides = {
settings?: Partial<MapPageSettings>;
presentationProfiles?: MapPresentationProfile[];
};
export const mapDesignProfileKey = (templateVersion: string) => `map@${templateVersion}`;
export function mapDesignFragmentFromLayout(layout: MapPageLayout, templateVersion: string): MapDesignProfileFragment {
return {
schemaVersion: 1,
templateId: "map",
templateVersion,
settings: structuredClone(layout.settings),
presentationProfiles: structuredClone(layout.presentationProfiles),
};
}
export function mapDesignFragmentForLayout(layout: StoredLayout | null | undefined, templateVersion: string) {
const fragment = layout?.pageTypes?.[mapDesignProfileKey(templateVersion)];
return fragment?.templateId === "map" ? fragment : null;
}
export function resolveMapDesignLayout(
base: MapPageLayout | null | undefined,
fragment: MapDesignProfileFragment | null | undefined,
overrides?: MapDesignOverrides,
) {
if (!base) return null;
if (!fragment) return base;
return {
...base,
settings: {
...base.settings,
...fragment.settings,
...overrides?.settings,
},
presentationProfiles: structuredClone(overrides?.presentationProfiles ?? fragment.presentationProfiles),
} satisfies MapPageLayout;
}
export function mapDesignOverridesFromResolved(
resolved: MapPageLayout,
fragment: MapDesignProfileFragment | null | undefined,
): MapDesignOverrides | undefined {
if (!fragment) return undefined;
const settings = Object.fromEntries(Object.entries(resolved.settings).filter(([key, value]) => (
fragment.settings[key as keyof MapPageSettings] !== value
))) as Partial<MapPageSettings>;
const presentationProfiles = JSON.stringify(resolved.presentationProfiles) === JSON.stringify(fragment.presentationProfiles)
? undefined
: structuredClone(resolved.presentationProfiles);
if (Object.keys(settings).length === 0 && !presentationProfiles) return undefined;
return {
...(Object.keys(settings).length > 0 ? { settings } : {}),
...(presentationProfiles ? { presentationProfiles } : {}),
};
}