Skip to content

Page Theme Plugin

This is a plugin which stores the theme of the page based on the typeHandle.

To archieve this we use the StagedWritable store which allows us to have a "staging" area for the new theme value which then gets commited in the render phase.

lib/pageTheme.ts

ts
import { getCrelte, type Crelte, type CrelteRequest } from 'crelte';
import type { Plugin, PluginCreator } from 'crelte/plugins';
import type { Request, Route } from 'crelte/routing';
import { StagedWritable } from 'crelte/std/stores';

export function createPageTheme(): PluginCreator {
	return _crelte => new PageThemePlugin();
}

export function getPageTheme(crelte?: Crelte): PageThemePlugin {
	crelte = crelte ?? getCrelte();
	return crelte.getPlugin('pageTheme') as PageThemePlugin;
}

export type PageTheme = 'color1' | 'color2' | 'color3';

export class PageThemePlugin implements Plugin {
	store: StagedWritable<PageTheme>;

	constructor(store?: StagedWritable<PageTheme>) {
		// by default we set color1 even tought that value will only be
		// visible in the first loadData call but never in a store read
		this.store = store ?? new StagedWritable('color1');
	}

	get name(): string {
		return 'pageTheme';
	}

	/**
	 * Subscribe to changes of the theme, for example in a component
	 */
	subscribe(
		fn: (val: PageTheme) => void,
		invalidate?: () => void,
	): () => void {
		return this.store.subscribe(fn, invalidate);
	}

	/**
	 * Get the current theme, if you don't need it to be reactive
	 */
	get(): PageTheme {
		// todo, maybe StagedWritable shoud have a getAsync like Globals has
		// so the plugin could expose a similar api, because at the loadData
		// call from a block or a template its not a give that this is
		// available
		return this.store.get();
	}

	toRequest(_req: Request): PageThemePlugin {
		// create a new instance of the plugin for the request
		// allowing requests to happen in parallel and the newest one to
		// "win"
		return new PageThemePlugin(this.store.stage());
	}

	loadData(cr: CrelteRequest) {
		// in loadData entry is already defined
		const entryType = cr.req.entry!.typeHandle;

		console.log('entryType', entryType);

		let newColor: PageTheme = this.store.get();
		switch (entryType) {
			case 'blog':
				newColor = 'color2';
				break;
			case 'project':
				newColor = 'color3';
				break;
			// default color is color 1
			default:
				newColor = 'color1';
		}

		// only change the color if it is different
		if (newColor !== this.store.get()) {
			this.store.set(newColor);
		}
	}

	render(_cr: CrelteRequest, _route: Route): void {
		// update the store to the new value
		this.store.commit();
	}
}