Skip to content

Commit

Permalink
inject app theme into plugin instead of styles
Browse files Browse the repository at this point in the history
  • Loading branch information
imolorhe committed Nov 9, 2024
1 parent 1630ab9 commit ae2be67
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { mock } from '../../../../../testing';

describe('ThemeDirective', () => {
it('should create an instance', () => {
const directive = new ThemeDirective(mock(), mock());
const directive = new ThemeDirective(mock());
expect(directive).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
hexToRgbStr,
ICustomTheme,
ITheme,
getCSS,
} from 'altair-graphql-core/build/theme';

import { css } from '@emotion/css';
Expand All @@ -20,10 +21,7 @@ export class ThemeDirective implements OnInit, OnChanges {

private className = '';

constructor(
private themeRegistry: ThemeRegistryService,
private nzConfigService: NzConfigService
) {}
constructor(private nzConfigService: NzConfigService) {}

ngOnInit() {
this.applyTheme(this.appTheme, this.appDarkTheme, this.appAccentColor);
Expand All @@ -43,118 +41,12 @@ export class ThemeDirective implements OnInit, OnChanges {
}
}

getCssString(theme: ITheme) {
return `
--baseline-size: ${theme.type.fontSize.base};
--rem-base: ${theme.type.fontSize.remBase};
--body-font-size: ${theme.type.fontSize.body};
--app-easing: ${theme.easing};
--black-color: ${theme.colors.black};
--dark-grey-color: ${theme.colors.darkGray};
--grey-color: ${theme.colors.gray};
--light-grey-color: ${theme.colors.lightGray};
--white-color: ${theme.colors.white};
--green-color: ${theme.colors.green};
--blue-color: ${theme.colors.blue};
--cerise-color: ${theme.colors.cerise};
--red-color: ${theme.colors.red};
--rose-color: ${theme.colors.rose};
--orange-color: ${theme.colors.orange};
--yellow-color: ${theme.colors.yellow};
--light-red-color: ${theme.colors.lightRed};
--dark-purple-color: ${theme.colors.darkPurple};
--primary-color: ${theme.colors.primary};
--secondary-color: ${theme.colors.secondary};
--tertiary-color: ${theme.colors.tertiary};
--shadow-bg: rgba(${hexToRgbStr(theme.shadow.color)}, ${theme.shadow.opacity});
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", 'Helvetica Neue', Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
--rgb-black: ${hexToRgbStr(theme.colors.black)};
--rgb-dark-grey: ${hexToRgbStr(theme.colors.darkGray)};
--rgb-grey: ${hexToRgbStr(theme.colors.gray)};
--rgb-light-grey: ${hexToRgbStr(theme.colors.lightGray)};
--rgb-white: ${hexToRgbStr(theme.colors.white)};
--rgb-green: ${hexToRgbStr(theme.colors.green)};
--rgb-blue: ${hexToRgbStr(theme.colors.blue)};
--rgb-cerise: ${hexToRgbStr(theme.colors.cerise)};
--rgb-red: ${hexToRgbStr(theme.colors.red)};
--rgb-orange: ${hexToRgbStr(theme.colors.orange)};
--rgb-yellow: ${hexToRgbStr(theme.colors.yellow)};
--rgb-light-red: ${hexToRgbStr(theme.colors.lightRed)};
--rgb-dark-purple: ${hexToRgbStr(theme.colors.darkPurple)};
--editor-font-family: ${theme.editor.fontFamily.default};
--editor-font-size: ${theme.editor.fontSize};
--theme-bg-color: ${theme.colors.bg};
--theme-off-bg-color: ${theme.colors.offBg};
--theme-font-color: ${theme.colors.font};
--theme-off-font-color: ${theme.colors.offFont};
--theme-border-color: ${theme.colors.border};
--theme-off-border-color: ${theme.colors.offBorder};
--header-bg-color: ${theme.colors.headerBg || theme.colors.offBg};
--rgb-primary: ${hexToRgbStr(theme.colors.primary)};
--rgb-secondary: ${hexToRgbStr(theme.colors.secondary)};
--rgb-tertiary: ${hexToRgbStr(theme.colors.tertiary)};
--rgb-theme-bg: ${hexToRgbStr(theme.colors.bg)};
--rgb-theme-off-bg: ${hexToRgbStr(theme.colors.offBg)};
--rgb-theme-font: ${hexToRgbStr(theme.colors.font)};
--rgb-theme-off-font: ${hexToRgbStr(theme.colors.offFont)};
--rgb-theme-border: ${hexToRgbStr(theme.colors.border)};
--rgb-theme-off-border: ${hexToRgbStr(theme.colors.offBorder)};
--rgb-header-bg: ${hexToRgbStr(theme.colors.headerBg || theme.colors.offBg)};
--editor-comment-color: ${theme.editor.colors.comment};
--editor-string-color: ${theme.editor.colors.string};
--editor-number-color: ${theme.editor.colors.number};
--editor-variable-color: ${theme.editor.colors.variable};
--editor-attribute-color: ${theme.editor.colors.attribute};
--editor-keyword-color: ${theme.editor.colors.keyword};
--editor-atom-color: ${theme.editor.colors.atom};
--editor-property-color: ${theme.editor.colors.property};
--editor-punctuation-color: ${theme.editor.colors.punctuation};
--editor-cursor-color: ${theme.editor.colors.cursor};
--editor-def-color: ${theme.editor.colors.definition};
--editor-builtin-color: ${theme.editor.colors.builtin};
`;
}

getDynamicClassName(
appTheme: ICustomTheme,
appDarkTheme?: ICustomTheme,
accentColor?: string
) {
const extraTheme = accentColor ? { colors: { primary: accentColor } } : {};
if (appTheme && appDarkTheme) {
return css(`
${this.getCssString(createTheme(appTheme, extraTheme))}
@media (prefers-color-scheme: dark) {
${this.getCssString(createTheme(appDarkTheme, extraTheme))}
}
`);
}

if (!appTheme || appTheme.isSystem) {
return css(`
${this.getCssString(
createTheme(this.themeRegistry.getTheme('light')!, appTheme, extraTheme)
)}
@media (prefers-color-scheme: dark) {
${this.getCssString(
createTheme(this.themeRegistry.getTheme('dark')!, appTheme, extraTheme)
)}
}
`);
}

return css(this.getCssString(createTheme(appTheme, extraTheme)));
return css(getCSS(appTheme, appDarkTheme, accentColor));
}

applyTheme(theme: ICustomTheme, darkTheme?: ICustomTheme, accentColor?: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,19 @@ export class PluginContextService implements PluginContextGenerator {
const sanitized = DOMPurify.default.sanitize(manifest.icon.src);
iconSvg = self.sanitizer.bypassSecurityTrustHtml(sanitized);
}
const engine = new PluginParentEngine(panelWorker);

const settings = await firstValueFrom(
self.store.select('settings').pipe(take(1))
);
const selectedTheme = self.themeRegistryService.getTheme(settings.theme) || {
isSystem: true,
};
const settingsThemeConfig = settings.themeConfig || {};
const theme = self.themeRegistryService.mergeThemes(
selectedTheme,
settingsThemeConfig
);
const engine = new PluginParentEngine(panelWorker, { theme });
const panel = new AltairPanel(
title,
panelWorker.getIframe(),
Expand Down
10 changes: 8 additions & 2 deletions packages/altair-core/src/plugin/v3/panel.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ICustomTheme, getCSS } from '../../theme';
import { PluginV3Context } from './context';

interface StylesData {
styleUrls: string[];
styles: string[];
htmlClasses: string[];
theme?: ICustomTheme;
}
export abstract class AltairV3Panel {
abstract create(ctx: PluginV3Context, container: HTMLElement): void;
Expand Down Expand Up @@ -35,7 +37,11 @@ export abstract class AltairV3Panel {
document.head.appendChild(link);
});

this.injectCSS(data.styles.join('\n'));
if (data.styles.length) {
this.injectCSS(data.styles.join('\n'));
} else if (data.theme) {
this.injectCSS(getCSS(data.theme));
}

// set the background color of the panel to the theme background color
this.injectCSS(`
Expand All @@ -51,7 +57,7 @@ export abstract class AltairV3Panel {

private injectCSS(css: string) {
let el = document.createElement('style');
el.innerText = css;
el.innerText = css.replace(/[\n\r]/g, '');
document.head.appendChild(el);
return el;
}
Expand Down
38 changes: 27 additions & 11 deletions packages/altair-core/src/plugin/v3/parent-engine.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ICustomTheme } from '../../theme';
import { CreateActionOptions } from '../context/context.interface';
import { PluginEventPayloadMap } from '../event/event.interfaces';
import { PluginV3Context } from './context';
Expand All @@ -20,11 +21,33 @@ const mainInstanceOnlyEvents: (keyof PluginV3Context)[] = [
// methods to be excluded from the generic listener creation since they are handled specially
const speciallyHandledMethods: (keyof PluginV3Context)[] = ['on', 'createAction'];

function getCssStyles(relevantClasses: string[]) {
try {
const styleSheets = Array.from(document.styleSheets);
return styleSheets
.map((sheet) => {
return Array.from(sheet.cssRules)
.map((rule) => rule.cssText)
.join('');
})
.filter((css) => {
return relevantClasses.some((htmlClass) => css.includes(`.${htmlClass}`));
});
} catch {
return [];
}
}
interface PluginParentEngineOptions {
theme?: ICustomTheme;
}
export class PluginParentEngine {
private context?: PluginV3Context;
subscribedEvents: string[] = [];

constructor(private worker: PluginParentWorker) {}
constructor(
private worker: PluginParentWorker,
private opts?: PluginParentEngineOptions
) {}

start(context: PluginV3Context) {
this.context = context;
Expand Down Expand Up @@ -101,17 +124,10 @@ export class PluginParentEngine {

const htmlClasses = Array.from(document.documentElement.classList);
// Get the styles that are applicable to the current theme of the page
const styles = styleSheets
.map((sheet) => {
return Array.from(sheet.cssRules)
.map((rule) => rule.cssText)
.join('');
})
.filter((css) => {
return htmlClasses.some((htmlClass) => css.includes(`.${htmlClass}`));
});
// Doesn't work crossorigin cases. e.g. when loading from CDN. Fallback to theme instead.
const styles = getCssStyles(htmlClasses);

return { styleUrls, styles, htmlClasses };
return { styleUrls, styles, htmlClasses, theme: this.opts?.theme };
});
}

Expand Down
115 changes: 115 additions & 0 deletions packages/altair-core/src/theme/css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import lightTheme from './defaults/light';
import darkTheme from './defaults/dark';
import { ICustomTheme, ITheme, createTheme, hexToRgbStr } from './theme';

const asCSSVariablesString = (theme: ITheme) => {
return `
:root {
--baseline-size: ${theme.type.fontSize.base};
--rem-base: ${theme.type.fontSize.remBase};
--body-font-size: ${theme.type.fontSize.body};
--app-easing: ${theme.easing};
--black-color: ${theme.colors.black};
--dark-grey-color: ${theme.colors.darkGray};
--grey-color: ${theme.colors.gray};
--light-grey-color: ${theme.colors.lightGray};
--white-color: ${theme.colors.white};
--green-color: ${theme.colors.green};
--blue-color: ${theme.colors.blue};
--cerise-color: ${theme.colors.cerise};
--red-color: ${theme.colors.red};
--rose-color: ${theme.colors.rose};
--orange-color: ${theme.colors.orange};
--yellow-color: ${theme.colors.yellow};
--light-red-color: ${theme.colors.lightRed};
--dark-purple-color: ${theme.colors.darkPurple};
--primary-color: ${theme.colors.primary};
--secondary-color: ${theme.colors.secondary};
--tertiary-color: ${theme.colors.tertiary};
--shadow-bg: rgba(${hexToRgbStr(theme.shadow.color)}, ${theme.shadow.opacity});
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", 'Helvetica Neue', Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
--rgb-black: ${hexToRgbStr(theme.colors.black)};
--rgb-dark-grey: ${hexToRgbStr(theme.colors.darkGray)};
--rgb-grey: ${hexToRgbStr(theme.colors.gray)};
--rgb-light-grey: ${hexToRgbStr(theme.colors.lightGray)};
--rgb-white: ${hexToRgbStr(theme.colors.white)};
--rgb-green: ${hexToRgbStr(theme.colors.green)};
--rgb-blue: ${hexToRgbStr(theme.colors.blue)};
--rgb-cerise: ${hexToRgbStr(theme.colors.cerise)};
--rgb-red: ${hexToRgbStr(theme.colors.red)};
--rgb-orange: ${hexToRgbStr(theme.colors.orange)};
--rgb-yellow: ${hexToRgbStr(theme.colors.yellow)};
--rgb-light-red: ${hexToRgbStr(theme.colors.lightRed)};
--rgb-dark-purple: ${hexToRgbStr(theme.colors.darkPurple)};
--editor-font-family: ${theme.editor.fontFamily.default};
--editor-font-size: ${theme.editor.fontSize};
--theme-bg-color: ${theme.colors.bg};
--theme-off-bg-color: ${theme.colors.offBg};
--theme-font-color: ${theme.colors.font};
--theme-off-font-color: ${theme.colors.offFont};
--theme-border-color: ${theme.colors.border};
--theme-off-border-color: ${theme.colors.offBorder};
--header-bg-color: ${theme.colors.headerBg || theme.colors.offBg};
--rgb-primary: ${hexToRgbStr(theme.colors.primary)};
--rgb-secondary: ${hexToRgbStr(theme.colors.secondary)};
--rgb-tertiary: ${hexToRgbStr(theme.colors.tertiary)};
--rgb-theme-bg: ${hexToRgbStr(theme.colors.bg)};
--rgb-theme-off-bg: ${hexToRgbStr(theme.colors.offBg)};
--rgb-theme-font: ${hexToRgbStr(theme.colors.font)};
--rgb-theme-off-font: ${hexToRgbStr(theme.colors.offFont)};
--rgb-theme-border: ${hexToRgbStr(theme.colors.border)};
--rgb-theme-off-border: ${hexToRgbStr(theme.colors.offBorder)};
--rgb-header-bg: ${hexToRgbStr(theme.colors.headerBg || theme.colors.offBg)};
--editor-comment-color: ${theme.editor.colors.comment};
--editor-string-color: ${theme.editor.colors.string};
--editor-number-color: ${theme.editor.colors.number};
--editor-variable-color: ${theme.editor.colors.variable};
--editor-attribute-color: ${theme.editor.colors.attribute};
--editor-keyword-color: ${theme.editor.colors.keyword};
--editor-atom-color: ${theme.editor.colors.atom};
--editor-property-color: ${theme.editor.colors.property};
--editor-punctuation-color: ${theme.editor.colors.punctuation};
--editor-cursor-color: ${theme.editor.colors.cursor};
--editor-def-color: ${theme.editor.colors.definition};
--editor-builtin-color: ${theme.editor.colors.builtin};
}
`;
};

export const getCSS = (
appTheme: ICustomTheme,
appDarkTheme?: ICustomTheme,
accentColor?: string
) => {
const extraTheme = accentColor ? { colors: { primary: accentColor } } : {};
if (appTheme && appDarkTheme) {
return `
${asCSSVariablesString(createTheme(appTheme, extraTheme))}
@media (prefers-color-scheme: dark) {
${asCSSVariablesString(createTheme(appDarkTheme, extraTheme))}
}
`;
}

if (!appTheme || appTheme.isSystem) {
return `
${asCSSVariablesString(createTheme(lightTheme, appTheme, extraTheme))}
@media (prefers-color-scheme: dark) {
${asCSSVariablesString(createTheme(darkTheme, appTheme, extraTheme))}
}
`;
}

return asCSSVariablesString(createTheme(appTheme, extraTheme));
};
1 change: 1 addition & 0 deletions packages/altair-core/src/theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import darkTheme from './defaults/dark';
import draculaTheme from './defaults/dracula';

export * from './theme';
export * from './css';
export const light = lightTheme;
export const dark = darkTheme;
export const dracula = draculaTheme;

0 comments on commit ae2be67

Please sign in to comment.