diff --git a/src/@fuse/tailwind/plugins/theming.js b/src/@fuse/tailwind/plugins/theming.js index 244b5acf..e9a709fe 100644 --- a/src/@fuse/tailwind/plugins/theming.js +++ b/src/@fuse/tailwind/plugins/theming.js @@ -11,7 +11,9 @@ const generateContrasts = require(path.resolve(__dirname, ('../utils/generate-co // ----------------------------------------------------------------------------------------------------- /** - * Normalize the provided theme + * Normalizes the provided theme by omitting empty values and values that + * start with "on" from each palette. Also sets the correct DEFAULT value + * of each palette. * * @param theme */ @@ -28,67 +30,6 @@ const normalizeTheme = (theme) => )); }; -/** - * Generates variable colors for the 'colors' - * configuration from the provided theme - * - * @param theme - */ -const generateVariableColors = (theme) => -{ - // https://github.com/adamwathan/tailwind-css-variable-text-opacity-demo - const customPropertiesWithOpacity = (name) => ({ - opacityVariable, - opacityValue - }) => - { - if ( opacityValue ) - { - return `rgba(var(--fuse-${name}-rgb), ${opacityValue})`; - } - if ( opacityVariable ) - { - return `rgba(var(--fuse-${name}-rgb), var(${opacityVariable}, 1))`; - } - return `rgb(var(--fuse-${name}-rgb))`; - }; - - return _.fromPairs(_.flatten(_.map(_.keys(flattenColorPalette(normalizeTheme(theme))), (name) => [ - [name, customPropertiesWithOpacity(name)], - [`on-${name}`, customPropertiesWithOpacity(`on-${name}`)] - ]))); -}; - -/** - * Generate and return themes object with theme name and colors/ - * This is useful for accessing themes from Angular (Typescript). - * - * @param themes - * @returns {unknown[]} - */ -function generateThemesObject(themes) -{ - const normalizedDefaultTheme = normalizeTheme(themes.default); - return _.map(_.cloneDeep(themes), (value, key) => - { - const theme = normalizeTheme(value); - const primary = (theme && theme.primary && theme.primary.DEFAULT) ? theme.primary.DEFAULT : normalizedDefaultTheme.primary.DEFAULT; - const accent = (theme && theme.accent && theme.accent.DEFAULT) ? theme.accent.DEFAULT : normalizedDefaultTheme.accent.DEFAULT; - const warn = (theme && theme.warn && theme.warn.DEFAULT) ? theme.warn.DEFAULT : normalizedDefaultTheme.warn.DEFAULT; - - return _.fromPairs([ - [ - key, - { - primary, - accent, - warn - } - ] - ]); - }); -} - // ----------------------------------------------------------------------------------------------------- // @ FUSE TailwindCSS Main Plugin // ----------------------------------------------------------------------------------------------------- @@ -98,25 +39,27 @@ const theming = plugin.withOptions((options) => ({ theme }) => { - // ----------------------------------------------------------------------------------------------------- - // @ Map variable colors - // ----------------------------------------------------------------------------------------------------- - const mapVariableColors = _.fromPairs(_.map(options.themes, (theme, themeName) => [ - themeName === 'default' ? 'body, .theme-default' : `.theme-${e(themeName)}`, - _.fromPairs(_.flatten(_.map(flattenColorPalette(_.fromPairs(_.flatten(_.map(normalizeTheme(theme), (palette, paletteName) => [ - [ - e(paletteName), - palette - ], - [ - `on-${e(paletteName)}`, - _.fromPairs(_.map(generateContrasts(palette), (color, hue) => [hue, _.get(theme, [`on-${paletteName}`, hue]) || color])) - ] - ]) - ))), (value, key) => [[`--fuse-${e(key)}`, value], [`--fuse-${e(key)}-rgb`, chroma(value).rgb().join(',')]]))) - ])); - - addComponents(mapVariableColors); + /** + * Iterate through the user's themes and build Tailwind components containing + * CSS Custom Properties using the colors from them. This allows switching + * themes by simply replacing a class name as well as nesting them. + */ + addComponents( + _.fromPairs(_.map(options.themes, (theme, themeName) => [ + themeName === 'default' ? 'body, .theme-default' : `.theme-${e(themeName)}`, + _.fromPairs(_.flatten(_.map(flattenColorPalette(_.fromPairs(_.flatten(_.map(normalizeTheme(theme), (palette, paletteName) => [ + [ + e(paletteName), + palette + ], + [ + `on-${e(paletteName)}`, + _.fromPairs(_.map(generateContrasts(palette), (color, hue) => [hue, _.get(theme, [`on-${paletteName}`, hue]) || color])) + ] + ]) + ))), (value, key) => [[`--fuse-${e(key)}`, value], [`--fuse-${e(key)}-rgb`, chroma(value).rgb().join(',')]]))) + ])) + ); // ----------------------------------------------------------------------------------------------------- // @ Generate scheme based css custom properties and utility classes @@ -141,7 +84,7 @@ const theming = plugin.withOptions((options) => ({ * If we set '--is-dark' as "true" on dark themes, the above rule * won't work because of the said "fallback value" logic. Therefore, * we set the '--is-dark' to "false" on light themes and not set it - * all on dark themes so that the fallback value can be used on + * at all on dark themes so that the fallback value can be used on * dark themes. * * On light themes, since '--is-dark' exists, the above rule will be @@ -175,9 +118,17 @@ const theming = plugin.withOptions((options) => ({ (options) => { return { - theme : { + theme: { extend: { - colors: generateVariableColors(options.themes.default) + /** + * Add 'Primary', 'Accent' and 'Warn' palettes as colors so all color utilities + * are generated for them; "bg-primary", "text-on-primary", "bg-accent-600" etc. + * This will also allow using arbitrary values with them such as opacity and such. + */ + colors: _.fromPairs(_.flatten(_.map(_.keys(flattenColorPalette(normalizeTheme(options.themes.default))), (name) => [ + [name, `rgba(var(--fuse-${name}-rgb), )`], + [`on-${name}`, `rgba(var(--fuse-on-${name}-rgb), )`] + ]))) }, fuse : { customProps: { @@ -221,8 +172,7 @@ const theming = plugin.withOptions((options) => ({ 'mat-icon' : colors.slate[400] } } - }, - themes : generateThemesObject(options.themes) + } } } };