UNPKG

@crossed/styled

Version:

A universal & performant styling library for React Native, Next.js & React

164 lines (145 loc) 4.16 kB
/** * Copyright (c) Paymium. * * This source code is licensed under the MIT license found in the * LICENSE file in the root of this projects source tree. */ import type { Plugin, PluginContext, Themes } from '../types'; import { setTheme } from '../setTheme'; import { convertKeyToCss, normalizeUnitPixel } from '../plugins/utils'; import { isWeb as isWebFile } from '../isWeb'; export const parse = <T extends Record<string, any>>( t: T, parentName?: string, isWeb?: boolean ): { theme: T; values: Record<string, any>; } => { return !isWeb ? { theme: t, values: t } : Object.entries(t).reduce<{ theme: T; values: Record<string, any>; }>( (acc, [key, value]) => { if (Array.isArray(value)) { } else if (['number', 'string'].includes(typeof value)) { const name = convertKeyToCss( `${parentName ? `${parentName}-` : ''}${key}` ); (acc.theme as any)[key] = `var(--${name})`; acc.values[`--${name}`] = normalizeUnitPixel( 'marginTop', value, isWeb ); } else if (typeof value === 'object') { const toto = parse( value, convertKeyToCss(`${parentName ? `${parentName}-` : ''}${key}`), isWeb ); acc.theme = { ...acc.theme, ...Object.entries(toto.theme).reduce<T>( (acc2, [key2, value2]) => { (acc2 as any)[key] = { ...acc2[key], [key2]: value2 }; return acc2; }, {} as T ), }; acc.values = { ...acc.values, ...toto.values, }; } return acc; }, { theme: {} as T, values: {} } ); }; export class RegistryBridge { private plugins: Plugin<any>[] = []; private _debug = false; private _listen = new Set< (_themeName: keyof Themes) => Promise<void> | void >(); private themes?: Themes; private _themeName?: keyof Themes; setThemes(themes: Partial<Themes>) { this.themes = themes; return this; } get themeName() { return this._themeName; } setInitialThemeName(themeName: keyof Themes) { if (!this._themeName) { setTheme(this._themeName, themeName); this._themeName = themeName; } return this; } setThemeName(themeName: keyof Themes) { setTheme(this._themeName, themeName); this._themeName = themeName; this._listen.forEach((cb) => cb(themeName)); return this; } getTheme(web?: boolean) { if (!this.themes) { // throw new Error('Themes are not set'); console.warn('Themes are not set'); return {} as Themes[keyof Themes]; } return parse(this.themes[this.themeName] || {}, undefined, web ?? isWebFile) .theme as Themes[keyof Themes]; } getThemes() { return this.themes; } subscribe(cb: (_themeName: keyof Themes) => Promise<void> | void) { this._listen.add(cb); return () => { this._listen.delete(cb); }; } setDebug(d: boolean) { this._debug = d; return this; } addPlugin<S = any>(plugin: Plugin<S>) { this.plugins.push(plugin as any); return this; } getPlugins() { return this.plugins as typeof this.plugins; } log(e: string) { if (this._debug) { // eslint-disable-next-line no-console console.log(`[@crossed/styled] ${e}`); } } apply<T extends Record<string, any>>( params: () => T, options: Omit<PluginContext<Required<any>>, 'styles' | 'key'> ) { this.log(`Registry apply`); const par = params(); Object.keys(par).forEach((key: keyof T) => { const styles = par[key]; this.plugins.forEach(({ test, apply, name }) => { if (typeof key === 'string') { const keyFind = test.includes(key); if (keyFind) { this.log(`[${name}] Find "${key}" for "${name}" plugin`); apply?.({ ...options, key, styles }); } } }); }); } }