@uifabric/utilities
Version:
Fluent UI React utilities for building components.
139 lines • 6.18 kB
JavaScript
import { mergeCssSets, Stylesheet, } from '@uifabric/merge-styles';
import { getRTL } from './rtl';
import { getWindow } from './dom';
var MAX_CACHE_COUNT = 50;
var DEFAULT_SPECIFICITY_MULTIPLIER = 5;
var _memoizedClassNames = 0;
var stylesheet = Stylesheet.getInstance();
if (stylesheet && stylesheet.onReset) {
stylesheet.onReset(function () { return _memoizedClassNames++; });
}
// Note that because of the caching nature within the classNames memoization,
// I've disabled this rule to simply be able to work with any types.
/* eslint-disable @typescript-eslint/no-explicit-any */
// This represents a prop we attach to each Map to indicate the cached return value
// associated with the graph node.
var retVal = '__retval__';
/**
* Creates a getClassNames function which calls getStyles given the props, and injects them
* into mergeStyleSets.
*
* Note that the props you pass in on every render should be in the same order and
* immutable (numbers, strings, and booleans). This will allow the results to be memoized. Violating
* these will cause extra recalcs to occur.
*/
export function classNamesFunction(options) {
// We build a trie where each node is a Map. The map entry key represents an argument
// value, and the entry value is another node (Map). Each node has a `__retval__`
// property which is used to hold the cached response.
if (options === void 0) { options = {}; }
// To derive the response, we can simply ensure the arguments are added or already
// exist in the trie. At the last node, if there is a `__retval__` we return that. Otherwise
// we call the `getStyles` api to evaluate, cache on the property, and return that.
var map = new Map();
var styleCalcCount = 0;
var getClassNamesCount = 0;
var currentMemoizedClassNames = _memoizedClassNames;
var getClassNames = function (styleFunctionOrObject, styleProps) {
if (styleProps === void 0) { styleProps = {}; }
var _a, _b;
// If useStaticStyles is true, styleFunctionOrObject returns slot to classname mappings.
// If there is also no style overrides, we can skip merge styles completely and
// simply return the result from the style funcion.
if (options.useStaticStyles &&
typeof styleFunctionOrObject === 'function' &&
styleFunctionOrObject.__noStyleOverride__) {
return styleFunctionOrObject(styleProps);
}
getClassNamesCount++;
var current = map;
var theme = styleProps.theme;
var rtl = theme && theme.rtl !== undefined ? theme.rtl : getRTL();
var disableCaching = options.disableCaching;
// On reset of our stylesheet, reset memoized cache.
if (currentMemoizedClassNames !== _memoizedClassNames) {
currentMemoizedClassNames = _memoizedClassNames;
map = new Map();
styleCalcCount = 0;
}
if (!options.disableCaching) {
current = _traverseMap(map, styleFunctionOrObject);
current = _traverseMap(current, styleProps);
}
if (disableCaching || !current[retVal]) {
if (styleFunctionOrObject === undefined) {
current[retVal] = {};
}
else {
current[retVal] = mergeCssSets([
(typeof styleFunctionOrObject === 'function'
? styleFunctionOrObject(styleProps)
: styleFunctionOrObject),
], { rtl: !!rtl, specificityMultiplier: options.useStaticStyles ? DEFAULT_SPECIFICITY_MULTIPLIER : undefined });
}
if (!disableCaching) {
styleCalcCount++;
}
}
if (styleCalcCount > (options.cacheSize || MAX_CACHE_COUNT)) {
var win = getWindow();
if ((_b = (_a = win) === null || _a === void 0 ? void 0 : _a.FabricConfig) === null || _b === void 0 ? void 0 : _b.enableClassNameCacheFullWarning) {
// eslint-disable-next-line no-console
console.warn("Styles are being recalculated too frequently. Cache miss rate is " + styleCalcCount + "/" + getClassNamesCount + ".");
// eslint-disable-next-line no-console
console.trace();
}
map.clear();
styleCalcCount = 0;
// Mutate the options passed in, that's all we can do.
options.disableCaching = true;
}
// Note: the retVal is an attached property on the Map; not a key in the Map. We use this attached property to
// cache the return value for this branch of the graph.
return current[retVal];
};
return getClassNames;
}
function _traverseEdge(current, value) {
value = _normalizeValue(value);
if (!current.has(value)) {
current.set(value, new Map());
}
return current.get(value);
}
function _traverseMap(current, inputs) {
if (typeof inputs === 'function') {
var cachedInputsFromStyled = inputs.__cachedInputs__;
if (cachedInputsFromStyled) {
// The styled helper will generate the styles function and will attach the cached
// inputs (consisting of the default styles, customzied styles, and user provided styles.)
// These should be used as cache keys for deriving the memoized value.
for (var _i = 0, _a = inputs.__cachedInputs__; _i < _a.length; _i++) {
var input = _a[_i];
current = _traverseEdge(current, input);
}
}
else {
current = _traverseEdge(current, inputs);
}
}
else if (typeof inputs === 'object') {
for (var propName in inputs) {
if (inputs.hasOwnProperty(propName)) {
current = _traverseEdge(current, inputs[propName]);
}
}
}
return current;
}
function _normalizeValue(value) {
switch (value) {
case undefined:
return '__undefined__';
case null:
return '__null__';
default:
return value;
}
}
//# sourceMappingURL=classNamesFunction.js.map