ng-zorro-antd-core-util.mjs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. import { TemplateRef, numberAttribute } from '@angular/core';
  2. import { coerceBooleanProperty, coerceNumberProperty, coerceCssPixelValue } from '@angular/cdk/coercion';
  3. import { warn } from 'ng-zorro-antd/core/logger';
  4. import { Subject, isObservable, from, of, EMPTY, Observable, fromEvent } from 'rxjs';
  5. import { take } from 'rxjs/operators';
  6. /**
  7. * Use of this source code is governed by an MIT-style license that can be
  8. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  9. */
  10. function toArray(value) {
  11. let ret;
  12. if (value == null) {
  13. ret = [];
  14. }
  15. else if (!Array.isArray(value)) {
  16. ret = [value];
  17. }
  18. else {
  19. ret = value;
  20. }
  21. return ret;
  22. }
  23. function arraysEqual(array1, array2) {
  24. if (!array1 || !array2 || array1.length !== array2.length) {
  25. return false;
  26. }
  27. const len = array1.length;
  28. for (let i = 0; i < len; i++) {
  29. if (array1[i] !== array2[i]) {
  30. return false;
  31. }
  32. }
  33. return true;
  34. }
  35. function shallowCopyArray(source) {
  36. return source.slice();
  37. }
  38. /**
  39. * Use of this source code is governed by an MIT-style license that can be
  40. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  41. */
  42. function isNotNil(value) {
  43. return typeof value !== 'undefined' && value !== null;
  44. }
  45. function isNil(value) {
  46. return typeof value === 'undefined' || value === null;
  47. }
  48. /**
  49. * Examine if two objects are shallowly equaled.
  50. */
  51. function shallowEqual(objA, objB) {
  52. if (objA === objB) {
  53. return true;
  54. }
  55. if (typeof objA !== 'object' || !objA || typeof objB !== 'object' || !objB) {
  56. return false;
  57. }
  58. const keysA = Object.keys(objA);
  59. const keysB = Object.keys(objB);
  60. if (keysA.length !== keysB.length) {
  61. return false;
  62. }
  63. const bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB);
  64. // eslint-disable-next-line @typescript-eslint/prefer-for-of
  65. for (let idx = 0; idx < keysA.length; idx++) {
  66. const key = keysA[idx];
  67. if (!bHasOwnProperty(key)) {
  68. return false;
  69. }
  70. if (objA[key] !== objB[key]) {
  71. return false;
  72. }
  73. }
  74. return true;
  75. }
  76. function isNonEmptyString(value) {
  77. return typeof value === 'string' && value !== '';
  78. }
  79. function isTemplateRef(value) {
  80. return value instanceof TemplateRef;
  81. }
  82. /**
  83. * Use of this source code is governed by an MIT-style license that can be
  84. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  85. */
  86. function toBoolean(value) {
  87. return coerceBooleanProperty(value);
  88. }
  89. function numberAttributeWithZeroFallback(value) {
  90. return numberAttribute(value, 0);
  91. }
  92. function numberAttributeWithOneFallback(value) {
  93. return numberAttribute(value, 1);
  94. }
  95. function numberAttributeWithInfinityFallback(value) {
  96. return numberAttribute(value, Infinity);
  97. }
  98. function toNumber(value, fallbackValue = 0) {
  99. return coerceNumberProperty(value, fallbackValue);
  100. }
  101. function toCssPixel(value) {
  102. return coerceCssPixelValue(value);
  103. }
  104. // eslint-disable no-invalid-this
  105. /**
  106. * Get the function-property type's value
  107. */
  108. function valueFunctionProp(prop, ...args) {
  109. return typeof prop === 'function' ? prop(...args) : prop;
  110. }
  111. function propDecoratorFactory(name, fallback) {
  112. function propDecorator(target, propName, originalDescriptor) {
  113. const privatePropName = `$$__zorroPropDecorator__${propName}`;
  114. if (Object.prototype.hasOwnProperty.call(target, privatePropName)) {
  115. warn(`The prop "${privatePropName}" is already exist, it will be overrided by ${name} decorator.`);
  116. }
  117. Object.defineProperty(target, privatePropName, {
  118. configurable: true,
  119. writable: true
  120. });
  121. return {
  122. get() {
  123. return originalDescriptor && originalDescriptor.get
  124. ? originalDescriptor.get.bind(this)()
  125. : this[privatePropName];
  126. },
  127. set(value) {
  128. if (originalDescriptor && originalDescriptor.set) {
  129. originalDescriptor.set.bind(this)(fallback(value));
  130. }
  131. this[privatePropName] = fallback(value);
  132. }
  133. };
  134. }
  135. return propDecorator;
  136. }
  137. /**
  138. * @deprecated Use input transform instead: `@Input({ transform })`
  139. *
  140. * Input decorator that handle a prop to do get/set automatically with toBoolean
  141. *
  142. * Why not using @InputBoolean alone without @Input? AOT needs @Input to be visible
  143. *
  144. * @howToUse
  145. * ```
  146. * @Input() @InputBoolean() visible: boolean = false;
  147. *
  148. * // Act as below:
  149. * // @Input()
  150. * // get visible() { return this.__visible; }
  151. * // set visible(value) { this.__visible = value; }
  152. * // __visible = false;
  153. * ```
  154. */
  155. function InputBoolean() {
  156. return propDecoratorFactory('InputBoolean', toBoolean);
  157. }
  158. /**
  159. * @deprecated Use input transform instead: `@Input({ transform })`
  160. */
  161. function InputCssPixel() {
  162. return propDecoratorFactory('InputCssPixel', toCssPixel);
  163. }
  164. /**
  165. * @deprecated Use input transform instead: `@Input({ transform })`
  166. */
  167. function InputNumber(fallbackValue) {
  168. return propDecoratorFactory('InputNumber', (value) => toNumber(value, fallbackValue));
  169. }
  170. /**
  171. * Use of this source code is governed by an MIT-style license that can be
  172. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  173. */
  174. /**
  175. * Silent an event by stopping and preventing it.
  176. */
  177. function silentEvent(e) {
  178. e.stopPropagation();
  179. e.preventDefault();
  180. }
  181. function getElementOffset(elem) {
  182. if (!elem.getClientRects().length) {
  183. return { top: 0, left: 0 };
  184. }
  185. const rect = elem.getBoundingClientRect();
  186. const win = elem.ownerDocument.defaultView;
  187. return {
  188. top: rect.top + win.pageYOffset,
  189. left: rect.left + win.pageXOffset
  190. };
  191. }
  192. /**
  193. * Investigate if an event is a `TouchEvent`.
  194. */
  195. function isTouchEvent(event) {
  196. return event.type.startsWith('touch');
  197. }
  198. function getEventPosition(event) {
  199. return isTouchEvent(event) ? event.touches[0] || event.changedTouches[0] : event;
  200. }
  201. /**
  202. * Use of this source code is governed by an MIT-style license that can be
  203. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  204. */
  205. function getRegExp(prefix) {
  206. const prefixArray = Array.isArray(prefix) ? prefix : [prefix];
  207. let prefixToken = prefixArray.join('').replace(/(\$|\^)/g, '\\$1');
  208. if (prefixArray.length > 1) {
  209. prefixToken = `[${prefixToken}]`;
  210. }
  211. return new RegExp(`(\\s|^)(${prefixToken})[^\\s]*`, 'g');
  212. }
  213. function getMentions(value, prefix = '@') {
  214. if (typeof value !== 'string') {
  215. return [];
  216. }
  217. const regex = getRegExp(prefix);
  218. const mentions = value.match(regex);
  219. return mentions !== null ? mentions.map(e => e.trim()) : [];
  220. }
  221. /**
  222. * Use of this source code is governed by an MIT-style license that can be
  223. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  224. */
  225. /**
  226. * Much like lodash.
  227. */
  228. function padStart(toPad, length, element) {
  229. if (toPad.length > length) {
  230. return toPad;
  231. }
  232. const joined = `${getRepeatedElement(length, element)}${toPad}`;
  233. return joined.slice(joined.length - length, joined.length);
  234. }
  235. function padEnd(toPad, length, element) {
  236. const joined = `${toPad}${getRepeatedElement(length, element)}`;
  237. return joined.slice(0, length);
  238. }
  239. function getRepeatedElement(length, element) {
  240. return Array(length).fill(element).join('');
  241. }
  242. /**
  243. * Use of this source code is governed by an MIT-style license that can be
  244. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  245. */
  246. function isPromise(obj) {
  247. return !!obj && typeof obj.then === 'function' && typeof obj.catch === 'function';
  248. }
  249. /**
  250. * Use of this source code is governed by an MIT-style license that can be
  251. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  252. */
  253. function getPercent(min, max, value) {
  254. return ((value - min) / (max - min)) * 100;
  255. }
  256. function getPrecision(num) {
  257. const numStr = num.toString();
  258. const dotIndex = numStr.indexOf('.');
  259. return dotIndex >= 0 ? numStr.length - dotIndex - 1 : 0;
  260. }
  261. function ensureNumberInRange(num, min, max) {
  262. if (isNaN(num) || num < min) {
  263. return min;
  264. }
  265. else if (num > max) {
  266. return max;
  267. }
  268. else {
  269. return num;
  270. }
  271. }
  272. function isNumberFinite(value) {
  273. return typeof value === 'number' && isFinite(value);
  274. }
  275. function toDecimal(value, decimal) {
  276. return Math.round(value * Math.pow(10, decimal)) / Math.pow(10, decimal);
  277. }
  278. function sum(input, initial = 0) {
  279. return input.reduce((previous, current) => previous + current, initial);
  280. }
  281. /**
  282. * Use of this source code is governed by an MIT-style license that can be
  283. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  284. */
  285. function scrollIntoView(node) {
  286. const nodeAsAny = node;
  287. if (nodeAsAny.scrollIntoViewIfNeeded) {
  288. nodeAsAny.scrollIntoViewIfNeeded(false);
  289. return;
  290. }
  291. if (node.scrollIntoView) {
  292. node.scrollIntoView(false);
  293. return;
  294. }
  295. }
  296. /**
  297. * Use of this source code is governed by an MIT-style license that can be
  298. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  299. */
  300. // from https://github.com/component/textarea-caret-position
  301. // We'll copy the properties below into the mirror div.
  302. // Note that some browsers, such as Firefox, do not concatenate properties
  303. // into their shorthand (e.g. padding-top, padding-bottom etc. -> padding),
  304. // so we have to list every single property explicitly.
  305. const properties = [
  306. 'direction', // RTL support
  307. 'boxSizing',
  308. 'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
  309. 'height',
  310. 'overflowX',
  311. 'overflowY', // copy the scrollbar for IE
  312. 'borderTopWidth',
  313. 'borderRightWidth',
  314. 'borderBottomWidth',
  315. 'borderLeftWidth',
  316. 'borderStyle',
  317. 'paddingTop',
  318. 'paddingRight',
  319. 'paddingBottom',
  320. 'paddingLeft',
  321. // https://developer.mozilla.org/en-US/docs/Web/CSS/font
  322. 'fontStyle',
  323. 'fontVariant',
  324. 'fontWeight',
  325. 'fontStretch',
  326. 'fontSize',
  327. 'fontSizeAdjust',
  328. 'lineHeight',
  329. 'fontFamily',
  330. 'textAlign',
  331. 'textTransform',
  332. 'textIndent',
  333. 'textDecoration', // might not make a difference, but better be safe
  334. 'letterSpacing',
  335. 'wordSpacing',
  336. 'tabSize',
  337. 'MozTabSize'
  338. ];
  339. const isBrowser = typeof window !== 'undefined';
  340. const isFirefox = isBrowser && window.mozInnerScreenX != null;
  341. const _parseInt = (str) => parseInt(str, 10);
  342. function getCaretCoordinates(element, position, options) {
  343. if (!isBrowser) {
  344. throw new Error('textarea-caret-position#getCaretCoordinates should only be called in a browser');
  345. }
  346. const debug = (options && options.debug) || false;
  347. if (debug) {
  348. const el = document.querySelector('#input-textarea-caret-position-mirror-div');
  349. if (el) {
  350. el.parentNode.removeChild(el);
  351. }
  352. }
  353. // The mirror div will replicate the textarea's style
  354. const div = document.createElement('div');
  355. div.id = 'input-textarea-caret-position-mirror-div';
  356. document.body.appendChild(div);
  357. const style = div.style;
  358. const computed = window.getComputedStyle ? window.getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9
  359. const isInput = element.nodeName === 'INPUT';
  360. // Default textarea styles
  361. style.whiteSpace = 'pre-wrap';
  362. if (!isInput) {
  363. style.wordWrap = 'break-word'; // only for textarea-s
  364. }
  365. // Position off-screen
  366. style.position = 'absolute'; // required to return coordinates properly
  367. if (!debug) {
  368. style.visibility = 'hidden';
  369. } // not 'display: none' because we want rendering
  370. // Transfer the element's properties to the div
  371. properties.forEach((prop) => {
  372. if (isInput && prop === 'lineHeight') {
  373. // Special case for <input>s because text is rendered centered and line height may be != height
  374. style.lineHeight = computed.height;
  375. }
  376. else {
  377. // @ts-ignore
  378. style[prop] = computed[prop];
  379. }
  380. });
  381. if (isFirefox) {
  382. // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
  383. if (element.scrollHeight > _parseInt(computed.height)) {
  384. style.overflowY = 'scroll';
  385. }
  386. }
  387. else {
  388. style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
  389. }
  390. div.textContent = element.value.substring(0, position);
  391. // The second special handling for input type="text" vs textarea:
  392. // spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
  393. if (isInput) {
  394. div.textContent = div.textContent.replace(/\s/g, '\u00a0');
  395. }
  396. const span = document.createElement('span');
  397. // Wrapping must be replicated *exactly*, including when a long word gets
  398. // onto the next line, with whitespace at the end of the line before (#7).
  399. // The *only* reliable way to do that is to copy the *entire* rest of the
  400. // textarea's content into the <span> created at the caret position.
  401. // For inputs, just '.' would be enough, but no need to bother.
  402. span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all
  403. div.appendChild(span);
  404. const coordinates = {
  405. top: span.offsetTop + _parseInt(computed.borderTopWidth),
  406. left: span.offsetLeft + _parseInt(computed.borderLeftWidth),
  407. height: _parseInt(computed.lineHeight)
  408. };
  409. if (debug) {
  410. span.style.backgroundColor = '#eee';
  411. createDebugEle(element, coordinates);
  412. }
  413. else {
  414. document.body.removeChild(div);
  415. }
  416. return coordinates;
  417. }
  418. function createDebugEle(element, coordinates) {
  419. const fontSize = getComputedStyle(element).getPropertyValue('font-size');
  420. const rect = document.querySelector('#DEBUG') || document.createElement('div');
  421. document.body.appendChild(rect);
  422. rect.id = 'DEBUG';
  423. rect.style.position = 'absolute';
  424. rect.style.backgroundColor = 'red';
  425. rect.style.height = fontSize;
  426. rect.style.width = '1px';
  427. rect.style.top = `${element.getBoundingClientRect().top - element.scrollTop + window.pageYOffset + coordinates.top}px`;
  428. rect.style.left = `${element.getBoundingClientRect().left - element.scrollLeft + window.pageXOffset + coordinates.left}px`;
  429. }
  430. /**
  431. * Use of this source code is governed by an MIT-style license that can be
  432. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  433. */
  434. function isStyleSupport(styleName) {
  435. if (typeof window !== 'undefined' && window.document && window.document.documentElement) {
  436. const styleNameList = Array.isArray(styleName) ? styleName : [styleName];
  437. const { documentElement } = window.document;
  438. return styleNameList.some(name => name in documentElement.style);
  439. }
  440. return false;
  441. }
  442. function getStyleAsText(styles) {
  443. if (!styles) {
  444. return '';
  445. }
  446. return Object.keys(styles)
  447. .map(key => {
  448. const val = styles[key];
  449. return `${key}:${typeof val === 'string' ? val : `${val}px`}`;
  450. })
  451. .join(';');
  452. }
  453. /**
  454. * Use of this source code is governed by an MIT-style license that can be
  455. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  456. */
  457. // We only handle element & text node.
  458. const ELEMENT_NODE = 1;
  459. const TEXT_NODE = 3;
  460. const COMMENT_NODE = 8;
  461. let ellipsisContainer;
  462. const wrapperStyle = {
  463. padding: '0',
  464. margin: '0',
  465. display: 'inline',
  466. lineHeight: 'inherit'
  467. };
  468. function pxToNumber(value) {
  469. if (!value) {
  470. return 0;
  471. }
  472. const match = value.match(/^\d*(\.\d*)?/);
  473. return match ? Number(match[0]) : 0;
  474. }
  475. function styleToString(style) {
  476. // There are some different behavior between Firefox & Chrome.
  477. // We have to handle this ourself.
  478. const styleNames = Array.prototype.slice.apply(style);
  479. return styleNames.map(name => `${name}: ${style.getPropertyValue(name)};`).join('');
  480. }
  481. function mergeChildren(children) {
  482. const childList = [];
  483. children.forEach((child) => {
  484. const prevChild = childList[childList.length - 1];
  485. if (prevChild && child.nodeType === TEXT_NODE && prevChild.nodeType === TEXT_NODE) {
  486. prevChild.data += child.data;
  487. }
  488. else {
  489. childList.push(child);
  490. }
  491. });
  492. return childList;
  493. }
  494. function measure(originEle, rows, contentNodes, fixedContent, ellipsisStr, suffixStr = '') {
  495. if (!ellipsisContainer) {
  496. ellipsisContainer = document.createElement('div');
  497. ellipsisContainer.setAttribute('aria-hidden', 'true');
  498. document.body.appendChild(ellipsisContainer);
  499. }
  500. // Get origin style
  501. const originStyle = window.getComputedStyle(originEle);
  502. const originCSS = styleToString(originStyle);
  503. const lineHeight = pxToNumber(originStyle.lineHeight);
  504. const maxHeight = Math.round(lineHeight * (rows + 1) + pxToNumber(originStyle.paddingTop) + pxToNumber(originStyle.paddingBottom));
  505. // Set shadow
  506. ellipsisContainer.setAttribute('style', originCSS);
  507. ellipsisContainer.style.position = 'fixed';
  508. ellipsisContainer.style.left = '0';
  509. ellipsisContainer.style.height = 'auto';
  510. ellipsisContainer.style.minHeight = 'auto';
  511. ellipsisContainer.style.maxHeight = 'auto';
  512. ellipsisContainer.style.top = '-999999px';
  513. ellipsisContainer.style.zIndex = '-1000';
  514. // clean up css overflow
  515. ellipsisContainer.style.textOverflow = 'clip';
  516. ellipsisContainer.style.whiteSpace = 'normal';
  517. ellipsisContainer.style.webkitLineClamp = 'none';
  518. const contentList = mergeChildren(contentNodes);
  519. const container = document.createElement('div');
  520. const contentContainer = document.createElement('span');
  521. const suffixContainer = document.createTextNode(suffixStr);
  522. const fixedContainer = document.createElement('span');
  523. // Add styles in container
  524. Object.assign(container.style, wrapperStyle);
  525. Object.assign(contentContainer.style, wrapperStyle);
  526. Object.assign(fixedContainer.style, wrapperStyle);
  527. contentList.forEach(n => {
  528. contentContainer.appendChild(n);
  529. });
  530. contentContainer.appendChild(suffixContainer);
  531. fixedContent.forEach(node => {
  532. fixedContainer.appendChild(node.cloneNode(true));
  533. });
  534. container.appendChild(contentContainer);
  535. container.appendChild(fixedContainer);
  536. // Render in the fake container
  537. ellipsisContainer.appendChild(container);
  538. // Check if ellipsis in measure div is height enough for content
  539. function inRange() {
  540. return ellipsisContainer.offsetHeight < maxHeight;
  541. }
  542. if (inRange()) {
  543. const text = ellipsisContainer.innerHTML;
  544. ellipsisContainer.removeChild(container);
  545. return { contentNodes, text, ellipsis: false };
  546. }
  547. // We should clone the childNode since they're controlled by React and we can't reuse it without warning
  548. const childNodes = Array.prototype.slice
  549. .apply(ellipsisContainer.childNodes[0].childNodes[0].cloneNode(true).childNodes)
  550. .filter(({ nodeType }) => nodeType !== COMMENT_NODE);
  551. const fixedNodes = Array.prototype.slice.apply(ellipsisContainer.childNodes[0].childNodes[1].cloneNode(true).childNodes);
  552. ellipsisContainer.removeChild(container);
  553. // ========================= Find match ellipsis content =========================
  554. ellipsisContainer.innerHTML = '';
  555. // Create origin content holder
  556. const ellipsisContentHolder = document.createElement('span');
  557. ellipsisContainer.appendChild(ellipsisContentHolder);
  558. const ellipsisTextNode = document.createTextNode(ellipsisStr + suffixStr);
  559. ellipsisContentHolder.appendChild(ellipsisTextNode);
  560. fixedNodes.forEach(childNode => {
  561. ellipsisContainer.appendChild(childNode);
  562. });
  563. // Append before fixed nodes
  564. function appendChildNode(node) {
  565. ellipsisContentHolder.insertBefore(node, ellipsisTextNode);
  566. }
  567. // Get maximum text
  568. function measureText(textNode, fullText, startLoc = 0, endLoc = fullText.length, lastSuccessLoc = 0) {
  569. const midLoc = Math.floor((startLoc + endLoc) / 2);
  570. textNode.textContent = fullText.slice(0, midLoc);
  571. if (startLoc >= endLoc - 1) {
  572. // Loop when step is small
  573. for (let step = endLoc; step >= startLoc; step -= 1) {
  574. const currentStepText = fullText.slice(0, step);
  575. textNode.textContent = currentStepText;
  576. if (inRange() || !currentStepText) {
  577. return step === fullText.length
  578. ? {
  579. finished: false,
  580. node: document.createTextNode(fullText)
  581. }
  582. : {
  583. finished: true,
  584. node: document.createTextNode(currentStepText)
  585. };
  586. }
  587. }
  588. }
  589. if (inRange()) {
  590. return measureText(textNode, fullText, midLoc, endLoc, midLoc);
  591. }
  592. else {
  593. return measureText(textNode, fullText, startLoc, midLoc, lastSuccessLoc);
  594. }
  595. }
  596. function measureNode(childNode, index) {
  597. const type = childNode.nodeType;
  598. if (type === ELEMENT_NODE) {
  599. // We don't split element, it will keep if whole element can be displayed.
  600. // appendChildNode(childNode);
  601. if (inRange()) {
  602. return {
  603. finished: false,
  604. node: contentList[index]
  605. };
  606. }
  607. // Clean up if can not pull in
  608. ellipsisContentHolder.removeChild(childNode);
  609. return {
  610. finished: true,
  611. node: null
  612. };
  613. }
  614. else if (type === TEXT_NODE) {
  615. const fullText = childNode.textContent || '';
  616. const textNode = document.createTextNode(fullText);
  617. appendChildNode(textNode);
  618. return measureText(textNode, fullText);
  619. }
  620. // Not handle other type of content
  621. // PS: This code should not be attached after react 16
  622. return {
  623. finished: false,
  624. node: null
  625. };
  626. }
  627. const ellipsisNodes = [];
  628. childNodes.some((childNode, index) => {
  629. const { finished, node } = measureNode(childNode, index);
  630. if (node) {
  631. ellipsisNodes.push(node);
  632. }
  633. return finished;
  634. });
  635. const result = {
  636. contentNodes: ellipsisNodes,
  637. text: ellipsisContainer.innerHTML,
  638. ellipsis: true
  639. };
  640. while (ellipsisContainer.firstChild) {
  641. ellipsisContainer.removeChild(ellipsisContainer.firstChild);
  642. }
  643. return result;
  644. }
  645. /**
  646. * Use of this source code is governed by an MIT-style license that can be
  647. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  648. */
  649. let scrollbarVerticalSize;
  650. let scrollbarHorizontalSize;
  651. // Measure scrollbar width for padding body during modal show/hide
  652. const scrollbarMeasure = {
  653. position: 'absolute',
  654. top: '-9999px',
  655. width: '50px',
  656. height: '50px'
  657. };
  658. function measureScrollbar(direction = 'vertical', prefix = 'ant') {
  659. if (typeof document === 'undefined' || typeof window === 'undefined') {
  660. return 0;
  661. }
  662. const isVertical = direction === 'vertical';
  663. if (isVertical && scrollbarVerticalSize) {
  664. return scrollbarVerticalSize;
  665. }
  666. else if (!isVertical && scrollbarHorizontalSize) {
  667. return scrollbarHorizontalSize;
  668. }
  669. const scrollDiv = document.createElement('div');
  670. Object.keys(scrollbarMeasure).forEach(scrollProp => {
  671. // @ts-ignore
  672. scrollDiv.style[scrollProp] = scrollbarMeasure[scrollProp];
  673. });
  674. // apply hide scrollbar className ahead
  675. scrollDiv.className = `${prefix}-hide-scrollbar scroll-div-append-to-body`;
  676. // Append related overflow style
  677. if (isVertical) {
  678. scrollDiv.style.overflowY = 'scroll';
  679. }
  680. else {
  681. scrollDiv.style.overflowX = 'scroll';
  682. }
  683. document.body.appendChild(scrollDiv);
  684. let size = 0;
  685. if (isVertical) {
  686. size = scrollDiv.offsetWidth - scrollDiv.clientWidth;
  687. scrollbarVerticalSize = size;
  688. }
  689. else {
  690. size = scrollDiv.offsetHeight - scrollDiv.clientHeight;
  691. scrollbarHorizontalSize = size;
  692. }
  693. document.body.removeChild(scrollDiv);
  694. return size;
  695. }
  696. /**
  697. * Use of this source code is governed by an MIT-style license that can be
  698. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  699. */
  700. function ensureInBounds(value, boundValue) {
  701. return value ? (value < boundValue ? value : boundValue) : boundValue;
  702. }
  703. /**
  704. * Use of this source code is governed by an MIT-style license that can be
  705. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  706. */
  707. function inNextTick() {
  708. const timer = new Subject();
  709. Promise.resolve().then(() => timer.next());
  710. return timer.pipe(take(1));
  711. }
  712. /**
  713. * Use of this source code is governed by an MIT-style license that can be
  714. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  715. */
  716. function wrapIntoObservable(value) {
  717. if (isObservable(value)) {
  718. return value;
  719. }
  720. if (isPromise(value)) {
  721. // Use `Promise.resolve()` to wrap promise-like instances.
  722. return from(Promise.resolve(value));
  723. }
  724. return of(value);
  725. }
  726. /**
  727. * Use of this source code is governed by an MIT-style license that can be
  728. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  729. */
  730. /**
  731. * Sync from rc-util [https://github.com/react-component/util]
  732. */
  733. function canUseDom() {
  734. return !!(typeof window !== 'undefined' && window.document && window.document.createElement);
  735. }
  736. /**
  737. * Use of this source code is governed by an MIT-style license that can be
  738. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  739. */
  740. /**
  741. * Sync from rc-util [https://github.com/react-component/util]
  742. */
  743. const MARK_KEY = `rc-util-key`;
  744. function getMark({ mark } = {}) {
  745. if (mark) {
  746. return mark.startsWith('data-') ? mark : `data-${mark}`;
  747. }
  748. return MARK_KEY;
  749. }
  750. function getContainer(option) {
  751. if (option.attachTo) {
  752. return option.attachTo;
  753. }
  754. const head = document.querySelector('head');
  755. return head || document.body;
  756. }
  757. function injectCSS(css, options = {}) {
  758. if (!canUseDom()) {
  759. return null;
  760. }
  761. const styleNode = document.createElement('style');
  762. if (options.cspNonce) {
  763. styleNode.nonce = options.cspNonce;
  764. }
  765. styleNode.innerHTML = css;
  766. const container = getContainer(options);
  767. const { firstChild } = container;
  768. if (options.prepend && container.prepend) {
  769. // Use `prepend` first
  770. container.prepend(styleNode);
  771. }
  772. else if (options.prepend && firstChild) {
  773. // Fallback to `insertBefore` like IE not support `prepend`
  774. container.insertBefore(styleNode, firstChild);
  775. }
  776. else {
  777. container.appendChild(styleNode);
  778. }
  779. return styleNode;
  780. }
  781. const containerCache = new Map();
  782. function findExistNode(key, option = {}) {
  783. const container = getContainer(option);
  784. return Array.from(containerCache.get(container)?.children || []).find(node => node.tagName === 'STYLE' && node.getAttribute(getMark(option)) === key);
  785. }
  786. function removeCSS(key, option = {}) {
  787. const existNode = findExistNode(key, option);
  788. existNode?.parentNode?.removeChild(existNode);
  789. }
  790. function updateCSS(css, key, options = {}) {
  791. const container = getContainer(options);
  792. // Get real parent
  793. if (!containerCache.has(container)) {
  794. const placeholderStyle = injectCSS('', options);
  795. // @ts-ignore
  796. const { parentNode } = placeholderStyle;
  797. containerCache.set(container, parentNode);
  798. parentNode.removeChild(placeholderStyle);
  799. }
  800. const existNode = findExistNode(key, options);
  801. if (existNode) {
  802. if (options.cspNonce && existNode.nonce !== options.cspNonce) {
  803. existNode.nonce = options.cspNonce;
  804. }
  805. if (existNode.innerHTML !== css) {
  806. existNode.innerHTML = css;
  807. }
  808. return existNode;
  809. }
  810. const newNode = injectCSS(css, options);
  811. newNode?.setAttribute(getMark(options), key);
  812. return newNode;
  813. }
  814. /**
  815. * Use of this source code is governed by an MIT-style license that can be
  816. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  817. */
  818. function getStatusClassNames(prefixCls, status, hasFeedback) {
  819. return {
  820. [`${prefixCls}-status-success`]: status === 'success',
  821. [`${prefixCls}-status-warning`]: status === 'warning',
  822. [`${prefixCls}-status-error`]: status === 'error',
  823. [`${prefixCls}-status-validating`]: status === 'validating',
  824. [`${prefixCls}-has-feedback`]: hasFeedback
  825. };
  826. }
  827. /**
  828. * Use of this source code is governed by an MIT-style license that can be
  829. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  830. */
  831. function runOutsideAngular(fn) {
  832. // The function that does the same job as `NgZone.runOutsideAngular`.
  833. // The difference is that we don't need to rely on the `NgZone` service,
  834. // allowing `fromEventOutsideAngular` to function without requiring an explicit
  835. // injection context (where we might otherwise call `inject(NgZone)`).
  836. return typeof Zone !== 'undefined' ? Zone.root.run(fn) : fn();
  837. }
  838. /**
  839. * This function replaces `runOutsideAngular` with `fromEvent`, introducing a
  840. * lot of boilerplate where we need to inject the `NgZone` service and then subscribe
  841. * to `fromEvent` within the `runOutsideAngular` callback.
  842. */
  843. function fromEventOutsideAngular(target, name, options) {
  844. // Allow the event target to be nullable to avoid requiring callers to check
  845. // if the target exists. We simply complete the observable immediately,
  846. // as this might potentially be used within a `switchMap`.
  847. if (!target) {
  848. return EMPTY;
  849. }
  850. return new Observable(subscriber => {
  851. // Note that we're wrapping fromEvent with an observable because `fromEvent`
  852. // is eager and only calls `addEventListener` when a new subscriber comes in.
  853. // Therefore, we're wrapping the subscription with `runOutsideAngular` to ensure
  854. // that `addEventListener` is also called outside of Angular when there's a subscriber.
  855. return runOutsideAngular(() =>
  856. // Casting because the inferred overload is incorrect :(
  857. fromEvent(target, name, options).subscribe(subscriber));
  858. });
  859. }
  860. /**
  861. * Use of this source code is governed by an MIT-style license that can be
  862. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  863. */
  864. /**
  865. * Generated bundle index. Do not edit.
  866. */
  867. export { InputBoolean, InputCssPixel, InputNumber, arraysEqual, canUseDom, createDebugEle, ensureInBounds, ensureNumberInRange, fromEventOutsideAngular, getCaretCoordinates, getElementOffset, getEventPosition, getMentions, getPercent, getPrecision, getRegExp, getRepeatedElement, getStatusClassNames, getStyleAsText, inNextTick, injectCSS, isNil, isNonEmptyString, isNotNil, isNumberFinite, isPromise, isStyleSupport, isTemplateRef, isTouchEvent, measure, measureScrollbar, numberAttributeWithInfinityFallback, numberAttributeWithOneFallback, numberAttributeWithZeroFallback, padEnd, padStart, properties, pxToNumber, removeCSS, scrollIntoView, shallowCopyArray, shallowEqual, silentEvent, sum, toArray, toBoolean, toCssPixel, toDecimal, toNumber, updateCSS, valueFunctionProp, wrapIntoObservable };
  868. //# sourceMappingURL=ng-zorro-antd-core-util.mjs.map