useCacheToken.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  2. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  3. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  4. import hash from '@emotion/hash';
  5. import { updateCSS } from "rc-util/es/Dom/dynamicCSS";
  6. import { useContext } from 'react';
  7. import StyleContext, { ATTR_MARK, ATTR_TOKEN, CSS_IN_JS_INSTANCE } from "../StyleContext";
  8. import { flattenToken, memoResult, token2key, toStyleStr } from "../util";
  9. import { transformToken } from "../util/css-variables";
  10. import useGlobalCache from "./useGlobalCache";
  11. var EMPTY_OVERRIDE = {};
  12. // Generate different prefix to make user selector break in production env.
  13. // This helps developer not to do style override directly on the hash id.
  14. var hashPrefix = process.env.NODE_ENV !== 'production' ? 'css-dev-only-do-not-override' : 'css';
  15. var tokenKeys = new Map();
  16. function recordCleanToken(tokenKey) {
  17. tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) + 1);
  18. }
  19. function removeStyleTags(key, instanceId) {
  20. if (typeof document !== 'undefined') {
  21. var styles = document.querySelectorAll("style[".concat(ATTR_TOKEN, "=\"").concat(key, "\"]"));
  22. styles.forEach(function (style) {
  23. if (style[CSS_IN_JS_INSTANCE] === instanceId) {
  24. var _style$parentNode;
  25. (_style$parentNode = style.parentNode) === null || _style$parentNode === void 0 || _style$parentNode.removeChild(style);
  26. }
  27. });
  28. }
  29. }
  30. var TOKEN_THRESHOLD = 0;
  31. // Remove will check current keys first
  32. function cleanTokenStyle(tokenKey, instanceId) {
  33. tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) - 1);
  34. var cleanableKeyList = new Set();
  35. tokenKeys.forEach(function (value, key) {
  36. if (value <= 0) cleanableKeyList.add(key);
  37. });
  38. // Should keep tokens under threshold for not to insert style too often
  39. if (tokenKeys.size - cleanableKeyList.size > TOKEN_THRESHOLD) {
  40. cleanableKeyList.forEach(function (key) {
  41. removeStyleTags(key, instanceId);
  42. tokenKeys.delete(key);
  43. });
  44. }
  45. }
  46. export var getComputedToken = function getComputedToken(originToken, overrideToken, theme, format) {
  47. var derivativeToken = theme.getDerivativeToken(originToken);
  48. // Merge with override
  49. var mergedDerivativeToken = _objectSpread(_objectSpread({}, derivativeToken), overrideToken);
  50. // Format if needed
  51. if (format) {
  52. mergedDerivativeToken = format(mergedDerivativeToken);
  53. }
  54. return mergedDerivativeToken;
  55. };
  56. export var TOKEN_PREFIX = 'token';
  57. /**
  58. * Cache theme derivative token as global shared one
  59. * @param theme Theme entity
  60. * @param tokens List of tokens, used for cache. Please do not dynamic generate object directly
  61. * @param option Additional config
  62. * @returns Call Theme.getDerivativeToken(tokenObject) to get token
  63. */
  64. export default function useCacheToken(theme, tokens) {
  65. var option = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  66. var _useContext = useContext(StyleContext),
  67. instanceId = _useContext.cache.instanceId,
  68. container = _useContext.container;
  69. var _option$salt = option.salt,
  70. salt = _option$salt === void 0 ? '' : _option$salt,
  71. _option$override = option.override,
  72. override = _option$override === void 0 ? EMPTY_OVERRIDE : _option$override,
  73. formatToken = option.formatToken,
  74. compute = option.getComputedToken,
  75. cssVar = option.cssVar;
  76. // Basic - We do basic cache here
  77. var mergedToken = memoResult(function () {
  78. return Object.assign.apply(Object, [{}].concat(_toConsumableArray(tokens)));
  79. }, tokens);
  80. var tokenStr = flattenToken(mergedToken);
  81. var overrideTokenStr = flattenToken(override);
  82. var cssVarStr = cssVar ? flattenToken(cssVar) : '';
  83. var cachedToken = useGlobalCache(TOKEN_PREFIX, [salt, theme.id, tokenStr, overrideTokenStr, cssVarStr], function () {
  84. var _cssVar$key;
  85. var mergedDerivativeToken = compute ? compute(mergedToken, override, theme) : getComputedToken(mergedToken, override, theme, formatToken);
  86. // Replace token value with css variables
  87. var actualToken = _objectSpread({}, mergedDerivativeToken);
  88. var cssVarsStr = '';
  89. if (!!cssVar) {
  90. var _transformToken = transformToken(mergedDerivativeToken, cssVar.key, {
  91. prefix: cssVar.prefix,
  92. ignore: cssVar.ignore,
  93. unitless: cssVar.unitless,
  94. preserve: cssVar.preserve
  95. });
  96. var _transformToken2 = _slicedToArray(_transformToken, 2);
  97. mergedDerivativeToken = _transformToken2[0];
  98. cssVarsStr = _transformToken2[1];
  99. }
  100. // Optimize for `useStyleRegister` performance
  101. var tokenKey = token2key(mergedDerivativeToken, salt);
  102. mergedDerivativeToken._tokenKey = tokenKey;
  103. actualToken._tokenKey = token2key(actualToken, salt);
  104. var themeKey = (_cssVar$key = cssVar === null || cssVar === void 0 ? void 0 : cssVar.key) !== null && _cssVar$key !== void 0 ? _cssVar$key : tokenKey;
  105. mergedDerivativeToken._themeKey = themeKey;
  106. recordCleanToken(themeKey);
  107. var hashId = "".concat(hashPrefix, "-").concat(hash(tokenKey));
  108. mergedDerivativeToken._hashId = hashId; // Not used
  109. return [mergedDerivativeToken, hashId, actualToken, cssVarsStr, (cssVar === null || cssVar === void 0 ? void 0 : cssVar.key) || ''];
  110. }, function (cache) {
  111. // Remove token will remove all related style
  112. cleanTokenStyle(cache[0]._themeKey, instanceId);
  113. }, function (_ref) {
  114. var _ref2 = _slicedToArray(_ref, 4),
  115. token = _ref2[0],
  116. cssVarsStr = _ref2[3];
  117. if (cssVar && cssVarsStr) {
  118. var style = updateCSS(cssVarsStr, hash("css-variables-".concat(token._themeKey)), {
  119. mark: ATTR_MARK,
  120. prepend: 'queue',
  121. attachTo: container,
  122. priority: -999
  123. });
  124. style[CSS_IN_JS_INSTANCE] = instanceId;
  125. // Used for `useCacheToken` to remove on batch when token removed
  126. style.setAttribute(ATTR_TOKEN, token._themeKey);
  127. }
  128. });
  129. return cachedToken;
  130. }
  131. export var extract = function extract(cache, effectStyles, options) {
  132. var _cache = _slicedToArray(cache, 5),
  133. realToken = _cache[2],
  134. styleStr = _cache[3],
  135. cssVarKey = _cache[4];
  136. var _ref3 = options || {},
  137. plain = _ref3.plain;
  138. if (!styleStr) {
  139. return null;
  140. }
  141. var styleId = realToken._tokenKey;
  142. var order = -999;
  143. // ====================== Style ======================
  144. // Used for rc-util
  145. var sharedAttrs = {
  146. 'data-rc-order': 'prependQueue',
  147. 'data-rc-priority': "".concat(order)
  148. };
  149. var styleText = toStyleStr(styleStr, cssVarKey, styleId, sharedAttrs, plain);
  150. return [order, styleId, styleText];
  151. };