StyleContext.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import { provide, defineComponent, unref, inject, watch, shallowRef, getCurrentInstance } from 'vue';
  3. import CacheEntity from './Cache';
  4. import { arrayType, booleanType, objectType, someType, stringType, withInstall } from '../type';
  5. export const ATTR_TOKEN = 'data-token-hash';
  6. export const ATTR_MARK = 'data-css-hash';
  7. export const ATTR_CACHE_PATH = 'data-cache-path';
  8. // Mark css-in-js instance in style element
  9. export const CSS_IN_JS_INSTANCE = '__cssinjs_instance__';
  10. export function createCache() {
  11. const cssinjsInstanceId = Math.random().toString(12).slice(2);
  12. // Tricky SSR: Move all inline style to the head.
  13. // PS: We do not recommend tricky mode.
  14. if (typeof document !== 'undefined' && document.head && document.body) {
  15. const styles = document.body.querySelectorAll(`style[${ATTR_MARK}]`) || [];
  16. const {
  17. firstChild
  18. } = document.head;
  19. Array.from(styles).forEach(style => {
  20. style[CSS_IN_JS_INSTANCE] = style[CSS_IN_JS_INSTANCE] || cssinjsInstanceId;
  21. // Not force move if no head
  22. // Not force move if no head
  23. if (style[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
  24. document.head.insertBefore(style, firstChild);
  25. }
  26. });
  27. // Deduplicate of moved styles
  28. const styleHash = {};
  29. Array.from(document.querySelectorAll(`style[${ATTR_MARK}]`)).forEach(style => {
  30. var _a;
  31. const hash = style.getAttribute(ATTR_MARK);
  32. if (styleHash[hash]) {
  33. if (style[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
  34. (_a = style.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(style);
  35. }
  36. } else {
  37. styleHash[hash] = true;
  38. }
  39. });
  40. }
  41. return new CacheEntity(cssinjsInstanceId);
  42. }
  43. const StyleContextKey = Symbol('StyleContextKey');
  44. // fix: https://github.com/vueComponent/ant-design-vue/issues/7023
  45. const getCache = () => {
  46. var _a, _b, _c;
  47. const instance = getCurrentInstance();
  48. let cache;
  49. if (instance && instance.appContext) {
  50. const globalCache = (_c = (_b = (_a = instance.appContext) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.globalProperties) === null || _c === void 0 ? void 0 : _c.__ANTDV_CSSINJS_CACHE__;
  51. if (globalCache) {
  52. cache = globalCache;
  53. } else {
  54. cache = createCache();
  55. if (instance.appContext.config.globalProperties) {
  56. instance.appContext.config.globalProperties.__ANTDV_CSSINJS_CACHE__ = cache;
  57. }
  58. }
  59. } else {
  60. cache = createCache();
  61. }
  62. return cache;
  63. };
  64. const defaultStyleContext = {
  65. cache: createCache(),
  66. defaultCache: true,
  67. hashPriority: 'low'
  68. };
  69. // fix: https://github.com/vueComponent/ant-design-vue/issues/6912
  70. export const useStyleInject = () => {
  71. const cache = getCache();
  72. return inject(StyleContextKey, shallowRef(_extends(_extends({}, defaultStyleContext), {
  73. cache
  74. })));
  75. };
  76. export const useStyleProvider = props => {
  77. const parentContext = useStyleInject();
  78. const context = shallowRef(_extends(_extends({}, defaultStyleContext), {
  79. cache: createCache()
  80. }));
  81. watch([() => unref(props), parentContext], () => {
  82. const mergedContext = _extends({}, parentContext.value);
  83. const propsValue = unref(props);
  84. Object.keys(propsValue).forEach(key => {
  85. const value = propsValue[key];
  86. if (propsValue[key] !== undefined) {
  87. mergedContext[key] = value;
  88. }
  89. });
  90. const {
  91. cache
  92. } = propsValue;
  93. mergedContext.cache = mergedContext.cache || createCache();
  94. mergedContext.defaultCache = !cache && parentContext.value.defaultCache;
  95. context.value = mergedContext;
  96. }, {
  97. immediate: true
  98. });
  99. provide(StyleContextKey, context);
  100. return context;
  101. };
  102. export const styleProviderProps = () => ({
  103. autoClear: booleanType(),
  104. /** @private Test only. Not work in production. */
  105. mock: stringType(),
  106. /**
  107. * Only set when you need ssr to extract style on you own.
  108. * If not provided, it will auto create <style /> on the end of Provider in server side.
  109. */
  110. cache: objectType(),
  111. /** Tell children that this context is default generated context */
  112. defaultCache: booleanType(),
  113. /** Use `:where` selector to reduce hashId css selector priority */
  114. hashPriority: stringType(),
  115. /** Tell cssinjs where to inject style in */
  116. container: someType(),
  117. /** Component wil render inline `<style />` for fallback in SSR. Not recommend. */
  118. ssrInline: booleanType(),
  119. /** Transform css before inject in document. Please note that `transformers` do not support dynamic update */
  120. transformers: arrayType(),
  121. /**
  122. * Linters to lint css before inject in document.
  123. * Styles will be linted after transforming.
  124. * Please note that `linters` do not support dynamic update.
  125. */
  126. linters: arrayType()
  127. });
  128. export const StyleProvider = withInstall(defineComponent({
  129. name: 'AStyleProvider',
  130. inheritAttrs: false,
  131. props: styleProviderProps(),
  132. setup(props, _ref) {
  133. let {
  134. slots
  135. } = _ref;
  136. useStyleProvider(props);
  137. return () => {
  138. var _a;
  139. return (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots);
  140. };
  141. }
  142. }));
  143. export default {
  144. useStyleInject,
  145. useStyleProvider,
  146. StyleProvider
  147. };