breaking changes, major refactoring of Composite
This commit is contained in:
144
src/lib/core/Composite.svelte
Normal file
144
src/lib/core/Composite.svelte
Normal file
@ -0,0 +1,144 @@
|
||||
<script lang="ts">
|
||||
import Composite from './Composite.svelte';
|
||||
import components from '$lib/core/componentLoader';
|
||||
import services from '$lib/core/servicesLoader';
|
||||
import { createCompositeStore, getCompositeStore } from '$lib/core/compositeStores';
|
||||
|
||||
interface IComposite {
|
||||
layout?: ICompositeLayout;
|
||||
id: string;
|
||||
slot?: string;
|
||||
component: string;
|
||||
services?: string[];
|
||||
map?: Record<string, string>;
|
||||
store?: Record<string, any>;
|
||||
children?: IComposite[];
|
||||
}
|
||||
|
||||
interface ICompositeLayout {
|
||||
areas: string;
|
||||
columns?: string;
|
||||
rows?: string;
|
||||
gap?: string;
|
||||
tailwindClasses?: string;
|
||||
}
|
||||
|
||||
export let composite: IComposite;
|
||||
|
||||
$: layoutStyle = composite?.layout
|
||||
? `
|
||||
grid-template-areas: ${composite.layout.areas};
|
||||
${composite.layout.gap ? `gap: ${composite.layout.gap};` : ''}
|
||||
${composite.layout.columns ? `grid-template-columns: ${composite.layout.columns};` : ''}
|
||||
${composite.layout.rows ? `grid-template-rows: ${composite.layout.rows};` : ''}
|
||||
`
|
||||
: '';
|
||||
|
||||
function initializeCompositeState(child: IComposite) {
|
||||
if (child.id) {
|
||||
child.store = createCompositeStore(child.id, child.store || {});
|
||||
}
|
||||
|
||||
if (child.children) {
|
||||
child.children.forEach(initializeCompositeState);
|
||||
}
|
||||
}
|
||||
|
||||
$: if (composite && composite.children) {
|
||||
composite.children.forEach(initializeCompositeState);
|
||||
}
|
||||
|
||||
function mapAndSubscribe(component: IComposite) {
|
||||
console.log('Mapping and subscribing for:', component.id);
|
||||
|
||||
if (component.map) {
|
||||
const localStore = getCompositeStore(component.id);
|
||||
|
||||
for (const [localKey, external] of Object.entries(component.map)) {
|
||||
const [externalID, externalKey] = external.split('.').map((item) => item.trim());
|
||||
const externalStore = getCompositeStore(externalID);
|
||||
if (externalStore) {
|
||||
externalStore.subscribe((externalState) => {
|
||||
console.log('External state:', externalState);
|
||||
if (externalState && externalKey in externalState) {
|
||||
localStore.update((storeValue) => {
|
||||
storeValue = storeValue || {};
|
||||
if (storeValue[localKey] !== externalState[externalKey]) {
|
||||
storeValue[localKey] = externalState[externalKey];
|
||||
return storeValue;
|
||||
}
|
||||
return storeValue;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (component.children) {
|
||||
component.children.forEach(mapAndSubscribe);
|
||||
}
|
||||
}
|
||||
|
||||
$: if (composite && composite.children) {
|
||||
composite.children.forEach(mapAndSubscribe);
|
||||
}
|
||||
|
||||
async function getComponent(componentName: string) {
|
||||
if (components[componentName]) {
|
||||
const module = await components[componentName]();
|
||||
return module.default;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function loadService(serviceName: string) {
|
||||
if (services[serviceName]) {
|
||||
const module = await services[serviceName]();
|
||||
return module.default || module;
|
||||
}
|
||||
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;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={composite?.layout
|
||||
? `grid w-full h-full ${composite.layout.tailwindClasses}`
|
||||
: 'grid w-full h-full'}
|
||||
style={layoutStyle}
|
||||
>
|
||||
{#if composite && composite.children}
|
||||
{#each composite.children as child (child.id)}
|
||||
<div class={`w-full h-full overflow-hidden`} style={`grid-area: ${child.slot}`}>
|
||||
{#await Promise.all( [getComponent(child.component), getServiceProps(child)] ) then [Component, serviceProps]}
|
||||
{#if Component}
|
||||
<svelte:component
|
||||
this={Component}
|
||||
id={child.id}
|
||||
{...child.store}
|
||||
services={serviceProps}
|
||||
/>
|
||||
{#if child.children && child.children.length}
|
||||
<Composite composite={child} />
|
||||
{/if}
|
||||
{:else}
|
||||
<p>Component {child.component} not found.</p>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
10
src/lib/core/componentLoader.ts
Normal file
10
src/lib/core/componentLoader.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import componentNames from 'virtual:components-list';
|
||||
|
||||
const components = {};
|
||||
|
||||
componentNames.forEach(path => {
|
||||
const name = path.split('/').pop(); // Extract just the file name from the path
|
||||
components[name] = () => import( /* @vite-ignore */ `/src/lib/components/${path}.svelte`);
|
||||
});
|
||||
|
||||
export default components;
|
16
src/lib/core/compositeStores.ts
Normal file
16
src/lib/core/compositeStores.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
const compositeStores = new Map();
|
||||
|
||||
// Create or retrieve a composite store
|
||||
export function createCompositeStore(compositeId: string, initialState = {}) {
|
||||
if (!compositeStores.has(compositeId)) {
|
||||
compositeStores.set(compositeId, writable(initialState));
|
||||
}
|
||||
return compositeStores.get(compositeId);
|
||||
}
|
||||
|
||||
// Get composite store or create a default empty one if not exists
|
||||
export function getCompositeStore(compositeId: string) {
|
||||
return compositeStores.get(compositeId) || createCompositeStore(compositeId);
|
||||
}
|
16
src/lib/core/servicesLoader.ts
Normal file
16
src/lib/core/servicesLoader.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import serviceNames from 'virtual:services-list';
|
||||
|
||||
const services: { [key: string]: Function } = {};
|
||||
|
||||
serviceNames.forEach(path => {
|
||||
const serviceName = path.split('/').pop().replace('.ts', '');
|
||||
services[serviceName] = () => import(/* @vite-ignore */ `/src/lib/services/${path}`)
|
||||
.then(mod => {
|
||||
console.log(`Loaded service ${serviceName} with the following properties:`, Object.keys(mod));
|
||||
if (mod.default) return mod.default;
|
||||
return mod;
|
||||
});
|
||||
});
|
||||
|
||||
export default services;
|
||||
|
Reference in New Issue
Block a user