UNPKG

@uifabric/utilities

Version:

Fluent UI React utilities for building components.

139 lines 6.18 kB
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