feat(foundry): close the operational map data loop
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
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 } : {}),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user