|
|
|
@ -6,6 +6,14 @@
|
|
|
|
|
import { dataStore } from '$lib/core/dataLoader';
|
|
|
|
|
import { createCompositeStore, getCompositeStore } from '$lib/core/compositeStores';
|
|
|
|
|
|
|
|
|
|
interface ICompositeLayout {
|
|
|
|
|
areas: string;
|
|
|
|
|
columns?: string;
|
|
|
|
|
rows?: string;
|
|
|
|
|
gap?: string;
|
|
|
|
|
tailwindClasses?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface IComposite {
|
|
|
|
|
layout?: ICompositeLayout;
|
|
|
|
|
id: string;
|
|
|
|
@ -15,14 +23,7 @@
|
|
|
|
|
map?: Record<string, string>;
|
|
|
|
|
store?: Record<string, any>;
|
|
|
|
|
children?: IComposite[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ICompositeLayout {
|
|
|
|
|
areas: string;
|
|
|
|
|
columns?: string;
|
|
|
|
|
rows?: string;
|
|
|
|
|
gap?: string;
|
|
|
|
|
tailwindClasses?: string;
|
|
|
|
|
servicesLoaded?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export let composite: IComposite;
|
|
|
|
@ -30,39 +31,49 @@
|
|
|
|
|
let layoutStyle = '';
|
|
|
|
|
|
|
|
|
|
$: {
|
|
|
|
|
// Create layout style reactively
|
|
|
|
|
layoutStyle = computeLayoutStyle(composite?.layout);
|
|
|
|
|
|
|
|
|
|
// Load services reactively
|
|
|
|
|
if (composite?.services) {
|
|
|
|
|
for (const serviceName of composite.services) {
|
|
|
|
|
if (!loadedServices[serviceName]) {
|
|
|
|
|
// Note: We're ignoring async operation here, you might want to handle it if needed
|
|
|
|
|
loadService(serviceName).then((service) => {
|
|
|
|
|
loadedServices[serviceName] = service;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
initializeAndLoadServices(composite);
|
|
|
|
|
initializeCompositeState(composite);
|
|
|
|
|
mapAndSubscribe(composite);
|
|
|
|
|
|
|
|
|
|
// Initialize composite state reactively
|
|
|
|
|
if (composite?.children) {
|
|
|
|
|
composite.children.forEach((child) => {
|
|
|
|
|
initializeCompositeState(child);
|
|
|
|
|
initializeAndLoadServices(child);
|
|
|
|
|
mapAndSubscribe(child);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function computeLayoutStyle(layout?: ICompositeLayout) {
|
|
|
|
|
function computeLayoutStyle(layout?: ICompositeLayout): string {
|
|
|
|
|
if (!layout) return '';
|
|
|
|
|
|
|
|
|
|
return `
|
|
|
|
|
grid-template-areas: ${layout.areas};
|
|
|
|
|
${layout.gap ? `gap: ${layout.gap};` : ''}
|
|
|
|
|
${layout.columns ? `grid-template-columns: ${layout.columns};` : ''}
|
|
|
|
|
${layout.rows ? `grid-template-rows: ${layout.rows};` : ''}
|
|
|
|
|
`;
|
|
|
|
|
grid-template-areas: ${layout.areas};
|
|
|
|
|
${layout.gap ? `gap: ${layout.gap};` : ''}
|
|
|
|
|
${layout.columns ? `grid-template-columns: ${layout.columns};` : ''}
|
|
|
|
|
${layout.rows ? `grid-template-rows: ${layout.rows};` : ''}
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initializeAndLoadServices(component: IComposite) {
|
|
|
|
|
if (!component) return;
|
|
|
|
|
|
|
|
|
|
if (component.services) {
|
|
|
|
|
let servicePromises = component.services.map((serviceName) =>
|
|
|
|
|
loadedServices[serviceName]
|
|
|
|
|
? Promise.resolve(loadedServices[serviceName])
|
|
|
|
|
: loadService(serviceName)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Promise.all(servicePromises).then((loaded) => {
|
|
|
|
|
loaded.forEach((service, index) => {
|
|
|
|
|
loadedServices[component.services[index]] = service;
|
|
|
|
|
});
|
|
|
|
|
component.servicesLoaded = true;
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
component.servicesLoaded = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initializeCompositeState(child: IComposite) {
|
|
|
|
@ -78,8 +89,6 @@
|
|
|
|
|
let unsubscribers = [];
|
|
|
|
|
|
|
|
|
|
function mapAndSubscribe(component: IComposite) {
|
|
|
|
|
console.log('Mapping and subscribing for:', component.id);
|
|
|
|
|
|
|
|
|
|
if (component.map) {
|
|
|
|
|
const localStore = getCompositeStore(component.id);
|
|
|
|
|
|
|
|
|
@ -91,15 +100,14 @@
|
|
|
|
|
if (externalKey in store) {
|
|
|
|
|
if (store[externalKey] && typeof store[externalKey].subscribe === 'function') {
|
|
|
|
|
let innerUnsub = store[externalKey].subscribe((value) => {
|
|
|
|
|
localStore.update((storeValue) => {
|
|
|
|
|
return { ...storeValue, [localKey]: value };
|
|
|
|
|
});
|
|
|
|
|
localStore.update((storeValue) => ({ ...storeValue, [localKey]: value }));
|
|
|
|
|
});
|
|
|
|
|
unsubscribers.push(innerUnsub);
|
|
|
|
|
} else {
|
|
|
|
|
localStore.update((storeValue) => {
|
|
|
|
|
return { ...storeValue, [localKey]: store[externalKey] };
|
|
|
|
|
});
|
|
|
|
|
localStore.update((storeValue) => ({
|
|
|
|
|
...storeValue,
|
|
|
|
|
[localKey]: store[externalKey]
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
@ -110,9 +118,10 @@
|
|
|
|
|
if (externalStore) {
|
|
|
|
|
const unsubscribe = externalStore.subscribe((externalState) => {
|
|
|
|
|
if (externalState && externalKey in externalState) {
|
|
|
|
|
localStore.update((storeValue) => {
|
|
|
|
|
return { ...storeValue, [localKey]: externalState[externalKey] };
|
|
|
|
|
});
|
|
|
|
|
localStore.update((storeValue) => ({
|
|
|
|
|
...storeValue,
|
|
|
|
|
[localKey]: externalState[externalKey]
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
@ -127,7 +136,6 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call all unsubscribe methods when the component is destroyed.
|
|
|
|
|
onDestroy(() => {
|
|
|
|
|
unsubscribers.forEach((unsub) => unsub());
|
|
|
|
|
});
|
|
|
|
@ -148,27 +156,14 @@
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function getServiceProps(component: IComposite) {
|
|
|
|
|
const loadedServices = {};
|
|
|
|
|
if (component.services) {
|
|
|
|
|
for (const serviceName of component.services) {
|
|
|
|
|
const loadedService = await loadService(serviceName);
|
|
|
|
|
if (loadedService) {
|
|
|
|
|
loadedServices[serviceName] = loadedService;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return loadedServices;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadComponentAndService(component: IComposite) {
|
|
|
|
|
return await Promise.all([getComponent(component.component), getServiceProps(component)]);
|
|
|
|
|
return await getComponent(component.component);
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<div class={`grid w-full h-full ${composite?.layout?.tailwindClasses || ''}`} style={layoutStyle}>
|
|
|
|
|
{#if composite && 'component' in composite}
|
|
|
|
|
{#await loadComponentAndService(composite) then [Component]}
|
|
|
|
|
{#if composite?.servicesLoaded}
|
|
|
|
|
{#await loadComponentAndService(composite) then Component}
|
|
|
|
|
{#if Component}
|
|
|
|
|
<svelte:component
|
|
|
|
|
this={Component}
|
|
|
|
@ -176,7 +171,7 @@
|
|
|
|
|
store={getCompositeStore(composite.id)}
|
|
|
|
|
services={loadedServices}
|
|
|
|
|
/>
|
|
|
|
|
{:else if composite.component}
|
|
|
|
|
{:else}
|
|
|
|
|
<p>Component {composite.component} not found.</p>
|
|
|
|
|
{/if}
|
|
|
|
|
{/await}
|
|
|
|
@ -185,21 +180,25 @@
|
|
|
|
|
{#if composite?.children}
|
|
|
|
|
{#each composite.children as child (child.id)}
|
|
|
|
|
<div class="w-full h-full overflow-hidden" style={`grid-area: ${child.slot}`}>
|
|
|
|
|
{#await loadComponentAndService(child) then [ChildComponent]}
|
|
|
|
|
{#if ChildComponent}
|
|
|
|
|
<svelte:component
|
|
|
|
|
this={ChildComponent}
|
|
|
|
|
id={child.id}
|
|
|
|
|
store={getCompositeStore(child.id)}
|
|
|
|
|
services={loadedServices}
|
|
|
|
|
/>
|
|
|
|
|
{#if child.children && child.children.length}
|
|
|
|
|
<Composite composite={child} />
|
|
|
|
|
{#if child.servicesLoaded}
|
|
|
|
|
{#await loadComponentAndService(child) then ChildComponent}
|
|
|
|
|
{#if ChildComponent}
|
|
|
|
|
<svelte:component
|
|
|
|
|
this={ChildComponent}
|
|
|
|
|
id={child.id}
|
|
|
|
|
store={getCompositeStore(child.id)}
|
|
|
|
|
services={loadedServices}
|
|
|
|
|
/>
|
|
|
|
|
{#if child.children && child.children.length}
|
|
|
|
|
<Composite composite={child} />
|
|
|
|
|
{/if}
|
|
|
|
|
{:else}
|
|
|
|
|
<p>Component {child.component} not found.</p>
|
|
|
|
|
{/if}
|
|
|
|
|
{:else if child.component}
|
|
|
|
|
<p>Component {child.component} not found.</p>
|
|
|
|
|
{/if}
|
|
|
|
|
{/await}
|
|
|
|
|
{/await}
|
|
|
|
|
{:else}
|
|
|
|
|
<p>Loading services for child {child.id}...</p>
|
|
|
|
|
{/if}
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
{/if}
|
|
|
|
|