@v4fire/client
Version:
V4Fire client core library
332 lines (284 loc) • 8.24 kB
JavaScript
;
/*!
* V4Fire Client Core
* https://github.com/V4Fire/Client
*
* Released under the MIT license
* https://github.com/V4Fire/Client/blob/master/LICENSE
*/
const
$C = require('collection.js');
const
{getThemes} = include('build/ds'),
{getThemedPathChunks, checkDeprecated, checkRequiredThemes} = include('build/stylus/ds/helpers');
/**
* Returns a function to register Stylus plugins by the specified options
*
* @param {object} opts
* @param {DesignSystem} opts.ds - the design system object prepared to use with Stylus
* @param {object} opts.cssVariables - a dictionary of CSS variables
* @param {boolean} [opts.useCSSVarsInRuntime] - true, if the design system object values provided
* to style files as css-variables
*
* @param {object} [opts.detectUserPreferences] - a map of user preference parameters that
* will be automatically detected based on system settings.
*
* @param {string} [opts.theme] - the current theme value
* @param {(Array<string>|boolean)} [opts.includeThemes] - a list of themes to include or
* `true` (will include all available themes)
*
* @param {string} [opts.themeAttribute] - an attribute name to set the theme value to the root element
*
* @param {object} [opts.stylus] - a link to the Stylus package instance
* @returns {Function}
*/
module.exports = function getPlugins({
ds,
cssVariables,
useCSSVarsInRuntime,
detectUserPreferences,
theme,
includeThemes,
themeAttribute,
stylus = require('stylus')
}) {
const
isBuildHasTheme = Object.isString(theme),
themedFields = $C(ds).get('meta.themedFields') || undefined;
let
buildThemes = includeThemes;
if (!buildThemes) {
buildThemes = isBuildHasTheme ? [theme] : [];
}
const
themesList = getThemes(ds.raw, buildThemes),
isThemesIncluded = themesList != null && themesList.length > 0,
isOneTheme = Object.isArray(themesList) && themesList.length === 1 && themesList[0] === theme;
checkRequiredThemes({detectUserPreferences, themesList});
if (!isThemesIncluded) {
if (Object.isString(theme)) {
console.log(`[stylus] Warning: the design system package has no theme "${theme}"`);
}
if (includeThemes != null) {
console.log(
`[stylus] Warning: the design system package has no themes for the provided "includeThemes" value: "${includeThemes}"`
);
}
}
const
isFieldThemed = (name) => Object.isArray(themedFields) ? themedFields.includes(name) : true;
return function addPlugins(api) {
/**
* Injects additional options to component mixin options ($p)
*
* @param {string} string - component name
* @returns {!Object}
*
* @example
* ```stylus
* injector('bButton')
*
* // If `useCSSVarsInRuntime` is enabled
* //
* // {
* // values: {
* // mods: {
* // size: {
* // s: {
* // offset: {
* // top: 'var(--bButton-mods-size-s-offset-top)'
* // }
* // }
* // }
* // }
* // }
*
* // Otherwise
* //
* // {
* // values: {
* // mods: {
* // size: {
* // s: {
* // offset: {
* // top: 5px
* // }
* // }
* // }
* // }
* // }
* ```
*/
api.define('injector', ({string}) => {
const
values = $C(useCSSVarsInRuntime || isThemesIncluded ? cssVariables : ds).get(`components.${string}`);
if (values) {
const
__diffVars__ = $C(cssVariables).get(`diff.components.${string}`);
return stylus.utils.coerce({
values,
__diffVars__
}, true);
}
return {};
});
/**
* Returns design system CSS variables with their values
*
* @param {string} [theme]
* @returns {!Object}
*
* @example
* ```stylus
* getDSVariables()
*
* // {
* // '--colors-primary': #0F9
* // }
* ```
*/
api.define('getDSVariables', ({string: theme} = {}) => {
const
obj = {},
iterator = Object.isString(theme) ? cssVariables.map[theme] : cssVariables.map;
Object.forEach(iterator, (val) => {
const [key, value] = val;
obj[key] = value;
});
return stylus.utils.coerce(obj, true);
});
/**
* Returns a value from the design system by the specified group and path.
* If passed only the first argument, the function returns parameters for the whole group,
* but not just the one value. If no arguments are passed, it returns the whole design system object.
*
* @param {string} [group] - first level field name (colors, rounding, etc.)
* @param {!Object} [path] - dot-delimited path to the value
* @returns {!Object}
*
* @example
* ```stylus
* getDSValue(colors "green.0") // rgba(0, 255, 0, 1)
* ```
*/
api.define('getDSValue', ({string: group} = {}, {string: path} = {}) => {
if (group === undefined) {
return ds;
}
checkDeprecated(ds, group);
const
getCSSVar = () => $C(cssVariables).get([].concat([group], path || []).join('.'));
if (isOneTheme || !isBuildHasTheme) {
return useCSSVarsInRuntime ?
stylus.utils.coerce(getCSSVar()) :
$C(ds).get([].concat(getThemedPathChunks(group, theme, isFieldThemed(group)), path || []).join('.'));
}
return stylus.utils.coerce(getCSSVar());
});
/**
* Returns an object with text styles for the specified style name
*
* @param {string} name
* @returns {!Object}
*
* @example
* ```stylus
* getDSTextStyles(Small)
*
* // Notice, all values are Stylus types
* //
* // {
* // fontFamily: 'Roboto',
* // fontWeight: 400,
* // fontSize: '14px',
* // lineHeight: '16px'
* // }
* ```
*/
api.define('getDSTextStyles', ({string: name}) => {
const
head = 'text',
isThemed = isFieldThemed(head),
path = [...getThemedPathChunks(head, theme, isThemed), name];
checkDeprecated(ds, path);
if (!isOneTheme && isThemesIncluded && isThemed) {
const
initial = $C(ds).get(path);
if (!Object.isDictionary(initial)) {
throw new Error(`getDSTextStyles: the design system has no "${theme}" styles for the specified name: ${name}`);
}
const
res = {};
Object.forEach(initial, (value, key) => {
res[key] = $C(cssVariables).get([head, name, key]);
});
return stylus.utils.coerce(res, true);
}
const
from = useCSSVarsInRuntime ? cssVariables : ds;
return stylus.utils.coerce($C(from).get(path), true);
});
/**
* Returns color(s) from the design system by the specified name and identifier (optional)
*
* @param {!Object} name
* @param {!Object} [id]
* @returns {(!Object|!Array)}
*
* @example
* ```stylus
* getDSColor("blue", 1) // rgba(0, 0, 255, 1)
* ```
*/
api.define('getDSColor', (name, id) => {
name = name.string || name.name;
if (!name) {
return;
}
name = name.includes('/') ?
name.split('/').map((s) => s.includes(' ') ? s.camelize(false) : s) :
name.split('.');
const
path = isOneTheme ? getThemedPathChunks('colors', theme, isFieldThemed('colors')) : ['colors'];
if (id) {
id = id.string || id.val;
if (Object.isNumber(id)) {
id -= 1;
}
}
path.push(...name);
if (id !== undefined) {
path.push(String(id));
}
checkDeprecated(ds, path);
return isThemesIncluded || useCSSVarsInRuntime ?
stylus.utils.coerce($C(cssVariables).get(path)) :
$C(ds).get(path);
});
/**
* Returns the current theme value
* @returns {!string}
*/
api.define('defaultTheme', () => theme);
/**
* Returns a list of available themes
* @returns {!Array<string>}
*/
api.define('availableThemes', () => themesList);
/**
* Returns the attribute name to set the theme value to the root element
* @returns {string}
*/
api.define('themeAttribute', () => themeAttribute);
/**
* Returns a dark theme name
* @returns {string}
*/
api.define('darkThemeName', () => detectUserPreferences.prefersColorScheme.aliases?.dark ?? 'dark');
/**
* Returns a light theme name
* @returns {string}
*/
api.define('lightThemeName', () => detectUserPreferences.prefersColorScheme.aliases?.light ?? 'light');
};
};