index.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /* eslint-disable max-len */
  2. /* argus-disable unPkgSensitiveInfo */
  3. import React from 'react';
  4. import { cloneDeepWith, set, get } from 'lodash-es';
  5. import warning from '@douyinfe/semi-foundation/utils/warning';
  6. import { findAll } from '@douyinfe/semi-foundation/utils/getHighlight';
  7. /**
  8. * stop propagation
  9. *
  10. * @param {React.MouseEvent<HTMLElement>} e React mouse event object
  11. * @param {boolean} noImmediate Skip stopping immediate propagation
  12. */
  13. export function stopPropagation(e: React.MouseEvent, noImmediate?: boolean) {
  14. if (e && typeof e.stopPropagation === 'function') {
  15. e.stopPropagation();
  16. }
  17. if (!noImmediate && e.nativeEvent && typeof e.nativeEvent.stopImmediatePropagation === 'function') {
  18. e.nativeEvent.stopImmediatePropagation();
  19. }
  20. }
  21. /**
  22. *
  23. * @param {any} value
  24. * @param {Function} customizer
  25. * @returns {any}
  26. * use in Table, Form, Navigation
  27. */
  28. export function cloneDeep(value: any, customizer?: (value: any) => void) {
  29. return cloneDeepWith(value, v => {
  30. if (typeof customizer === 'function') {
  31. return customizer(v);
  32. }
  33. if (typeof v === 'function' || React.isValidElement(v)) {
  34. return v;
  35. }
  36. if (Object.prototype.toString.call(v) === '[object Error]') {
  37. return v;
  38. }
  39. if (Array.isArray(v) && v.length === 0) {
  40. const keys: string[] = Object.keys(v);
  41. if (keys.length) {
  42. const newArray: any[] = [];
  43. keys.forEach(key => {
  44. set(newArray, key, v[key]);
  45. });
  46. // internal-issues:887
  47. warning(
  48. get(process, 'env.NODE_ENV') !== 'production',
  49. `[Semi] You may use an out-of-bounds array. In some cases, your program may not behave as expected.
  50. The maximum length of an array is 4294967295.
  51. Please check whether the array subscript in your data exceeds the maximum value of the JS array subscript`
  52. );
  53. return newArray;
  54. } else {
  55. return undefined;
  56. }
  57. }
  58. return undefined;
  59. });
  60. }
  61. /**
  62. * [getHighLightTextHTML description]
  63. *
  64. * @param {string} sourceString [source content text]
  65. * @param {Array<string>} searchWords [keywords to be highlighted]
  66. * @param {object} option
  67. * @param {true} option.highlightTag [The tag wrapped by the highlighted content, mark is used by default]
  68. * @param {true} option.highlightClassName
  69. * @param {true} option.highlightStyle
  70. * @param {boolean} option.caseSensitive
  71. *
  72. * @return {Array<object>}
  73. */
  74. export const getHighLightTextHTML = ({
  75. sourceString = '',
  76. searchWords = [],
  77. option = { autoEscape: true, caseSensitive: false }
  78. }: GetHighLightTextHTMLProps) => {
  79. const chunks: HighLightTextHTMLChunk[] = findAll({ sourceString, searchWords, ...option });
  80. const markEle = option.highlightTag || 'mark';
  81. const highlightClassName = option.highlightClassName || '';
  82. const highlightStyle = option.highlightStyle || {};
  83. return chunks.map((chunk: HighLightTextHTMLChunk) => {
  84. const { end, start, highlight } = chunk;
  85. const text = sourceString.substr(start, end - start);
  86. if (highlight) {
  87. return React.createElement(
  88. markEle,
  89. {
  90. style: highlightStyle,
  91. className: highlightClassName,
  92. },
  93. text
  94. );
  95. } else {
  96. return text;
  97. }
  98. });
  99. };
  100. export interface RegisterMediaQueryOption {
  101. match?: (e: MediaQueryList | MediaQueryListEvent) => void;
  102. unmatch?: (e: MediaQueryList | MediaQueryListEvent) => void;
  103. callInInit?: boolean;
  104. }
  105. /**
  106. * register matchFn and unMatchFn callback while media query
  107. * @param {string} media media string
  108. * @param {object} param param object
  109. * @returns function
  110. */
  111. export const registerMediaQuery = (media: string, { match, unmatch, callInInit = true }: RegisterMediaQueryOption): () => void => {
  112. if (typeof window !== 'undefined') {
  113. const mediaQueryList = window.matchMedia(media);
  114. function handlerMediaChange(e: MediaQueryList | MediaQueryListEvent): void {
  115. if (e.matches) {
  116. match && match(e);
  117. } else {
  118. unmatch && unmatch(e);
  119. }
  120. }
  121. callInInit && handlerMediaChange(mediaQueryList);
  122. mediaQueryList.addEventListener('change', handlerMediaChange);
  123. return (): void => mediaQueryList.removeEventListener('change', handlerMediaChange);
  124. }
  125. return null;
  126. };
  127. export interface GetHighLightTextHTMLProps {
  128. sourceString?: string;
  129. searchWords?: any[];
  130. option: HighLightTextHTMLOption;
  131. }
  132. export interface HighLightTextHTMLOption {
  133. highlightTag?: string;
  134. highlightClassName?: string;
  135. highlightStyle?: Record<string, any>;
  136. caseSensitive: boolean;
  137. autoEscape: boolean;
  138. }
  139. export interface HighLightTextHTMLChunk {
  140. start?: number;
  141. end?: number;
  142. highlight?: any;
  143. }
  144. /**
  145. * Determine whether the incoming element is a built-in icon
  146. * @param icon 元素
  147. * @returns boolean
  148. */
  149. export const isSemiIcon = (icon: any): boolean => React.isValidElement(icon) && get(icon.type, 'elementType') === 'Icon';