@atlaskit/app-provider
Version:
A top level provider for the Design System.
73 lines (70 loc) • 2.48 kB
JavaScript
import { getDocument } from '@atlaskit/browser-apis';
import { fg } from '@atlaskit/platform-feature-flags/fg';
import { themeImportMap } from '@atlaskit/tokens/artifacts/theme-import-map';
import { getThemeOverridePreferences } from '@atlaskit/tokens/get-theme-override-preferences';
import { getThemePreferences } from '@atlaskit/tokens/get-theme-preferences';
import { isThemeMounted } from './is-theme-mounted';
const loadThemeCss = async themeId => {
// Prevent duplicate loading of themes
if (isThemeMounted(themeId)) {
return;
}
// Gracefully handles falsy values like `null` and `undefined` when makers ignore
// TS to avoid loading default themes.
if (!themeId) {
return;
}
const {
default: themeCss
} = await themeImportMap[themeId]();
return themeCss;
};
const mountThemeCss = (css, themeId) => {
// SSR-safe: Only mount on client side
const doc = getDocument();
if (!doc) {
return;
}
const style = doc.createElement('style');
style.textContent = css;
style.dataset.theme = themeId;
// Prevent duplicate mounting of themes.
// It's possible the theme was already being loaded elsewhere.
if (isThemeMounted(themeId)) {
return;
}
doc.head.appendChild(style);
};
const loadAndMountThemeCss = async themeId => {
const themeCss = await loadThemeCss(themeId);
if (!themeCss) {
return;
}
mountThemeCss(themeCss, themeId);
};
export const loadAndMountThemes = async theme => {
if (fg('platform-dst-tokens-finesse')) {
const themeState = {
...theme,
// Keep both configured color themes available for subtree color-mode changes.
colorMode: 'auto',
contrastMode: 'no-preference'
};
const themeIds = [...new Set([...getThemePreferences(themeState), ...getThemeOverridePreferences(themeState)])];
const themes = await Promise.allSettled(themeIds.map(async id => ({
id,
css: await loadThemeCss(id)
})));
// Imports may resolve in any order. Mount bases before overrides, even when
// multiple providers are loading the same styles at the same time. A failed
// import must not prevent successfully loaded styles from mounting.
themes.forEach(result => {
if (result.status === 'fulfilled' && result.value.css) {
mountThemeCss(result.value.css, result.value.id);
}
});
return;
}
const themesToLoad = Object.values(theme).filter(themeId => !!themeId);
themesToLoad.forEach(loadAndMountThemeCss);
};