@libs-ui/utils 0.2.304 → 0.2.306

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- import { fromEvent, tap, takeUntil, mergeMap, startWith, finalize, Subject, filter, Observable } from 'rxjs';
2
- import Quill from 'quill';
3
1
  import DeviceDetector from 'device-detector-js';
2
+ import Quill from 'quill';
3
+ import { fromEvent, tap, takeUntil, mergeMap, startWith, finalize, lastValueFrom, timer, Subject, filter, Observable } from 'rxjs';
4
4
  import CryptoES from 'crypto-es';
5
5
  import { HttpParams } from '@angular/common/http';
6
6
  import { isSignal, TemplateRef, ElementRef, signal, InjectionToken } from '@angular/core';
@@ -352,18 +352,136 @@ const getDragEventByElement = (config) => {
352
352
  ? mousemove.pipe(startWith(e))
353
353
  : mousemove), takeUntil(config.onDestroy), finalize(removeClass));
354
354
  };
355
- const getHTMLFromQuill = (delta) => {
355
+ const getHTMLFromQuill = async (data, options) => {
356
+ const { replaceNewLineTo = '<br>', replaceTagBRTo, replaceTags, replaceBrToDiv } = options || {};
357
+ const delta = (typeof data === 'string') ? await getDeltaFromHTML(data) : data;
358
+ if (options?.functionReplaceDelta) {
359
+ options.functionReplaceDelta(delta);
360
+ }
356
361
  delta.ops.forEach((op) => {
357
362
  if (op.insert) {
358
363
  if (typeof op.insert === 'string') {
359
- op.insert = op.insert.replace(/\n/g, '<br>');
364
+ if (replaceNewLineTo) {
365
+ op.insert = op.insert.replace(/\n/g, replaceNewLineTo);
366
+ }
367
+ if (replaceTagBRTo) {
368
+ op.insert = op.insert.replace(/<br>/g, replaceTagBRTo);
369
+ }
370
+ if (replaceTags?.length) {
371
+ for (const tag of replaceTags) {
372
+ op.insert = op.insert.replace(new RegExp(`<${tag.tag}>`, 'g'), `<${tag.replaceTo}>`);
373
+ op.insert = op.insert.replace(new RegExp(`</${tag.tag}>`, 'g'), `</${tag.replaceTo}>`);
374
+ }
375
+ }
360
376
  }
361
377
  }
362
378
  });
363
379
  quill.setContents(delta);
364
- const htmlText = quill.root.firstElementChild?.innerHTML;
380
+ let htmlText = options?.getRootHtml ? quill.root.innerHTML : quill.root.firstElementChild?.innerHTML;
381
+ if (replaceBrToDiv) {
382
+ htmlText = convertHtmlToDivBlocks(htmlText || '');
383
+ }
365
384
  return decodeEscapeHtml(htmlText || '');
366
385
  };
386
+ const convertHtmlToDivBlocks = (html) => {
387
+ const BREAK_TOKEN = '<<<BREAK>>>';
388
+ // Bước 1: thay <br> thành token tạm
389
+ const normalizedHtml = html.replace(/<br\s*\/?>/gi, BREAK_TOKEN);
390
+ // Bước 2: tách theo token
391
+ const parts = normalizedHtml.split(BREAK_TOKEN);
392
+ const parser = new DOMParser();
393
+ const divs = [];
394
+ for (const raw of parts) {
395
+ const trimmed = raw.trim();
396
+ if (!trimmed)
397
+ continue;
398
+ // parse mỗi phần nhỏ như một document riêng
399
+ const doc = parser.parseFromString(trimmed, 'text/html');
400
+ const body = doc.body;
401
+ // Lấy lại nội dung bên trong body
402
+ divs.push(`<div>${body.innerHTML}</div>`);
403
+ }
404
+ return divs.join('');
405
+ };
406
+ const getDeltaFromHTML = async (html) => {
407
+ quill.root.innerHTML = html;
408
+ setTimeout(() => {
409
+ console.log(quill.getContents());
410
+ }, 1000);
411
+ await lastValueFrom(timer(1000));
412
+ return quill.getContents();
413
+ };
414
+ const processPasteData = async (e, config) => {
415
+ const element = config.element;
416
+ const files = e.clipboardData?.files;
417
+ if (files?.length) {
418
+ e.preventDefault();
419
+ config.handlerPasteFile?.(files);
420
+ config.callBack?.('file');
421
+ return;
422
+ }
423
+ // Lưu selection TRƯỚC khi prevent default
424
+ const selection = window.getSelection();
425
+ let savedRange = null;
426
+ if (selection && selection.rangeCount > 0) {
427
+ const range = selection.getRangeAt(0);
428
+ // Chỉ lưu nếu range nằm trong contentText element
429
+ const container = range.commonAncestorContainer;
430
+ const isInContentElement = element.contains(container.nodeType === Node.TEXT_NODE ? container.parentNode : container);
431
+ if (isInContentElement) {
432
+ savedRange = range.cloneRange();
433
+ }
434
+ }
435
+ // Prevent default để tự xử lý paste
436
+ e.preventDefault();
437
+ // Sử dụng Quill để clean HTML content
438
+ const htmlContent = e.clipboardData?.getData('text/html') || '';
439
+ const plainText = e.clipboardData?.getData('text/plain') || '';
440
+ let contentToInsert = (plainText || '').replace(/\n/g, '<br>');
441
+ if (htmlContent) {
442
+ contentToInsert = await getHTMLFromQuill(htmlContent);
443
+ }
444
+ if (!contentToInsert) {
445
+ config.callBack?.('no-content');
446
+ return;
447
+ }
448
+ if (savedRange) {
449
+ insertContentWithRange(contentToInsert, savedRange, element);
450
+ config.callBack?.('range');
451
+ return;
452
+ }
453
+ element.innerHTML += contentToInsert;
454
+ config.callBack?.('content');
455
+ };
456
+ const insertContentWithRange = (content, savedRange, element) => {
457
+ const selection = window.getSelection();
458
+ if (!selection) {
459
+ // Fallback: append vào cuối
460
+ element.innerHTML += content;
461
+ return;
462
+ }
463
+ // Restore selection
464
+ selection.removeAllRanges();
465
+ selection.addRange(savedRange);
466
+ // Xóa nội dung đã select (nếu có)
467
+ savedRange.deleteContents();
468
+ // Tạo document fragment từ HTML content
469
+ const tempDiv = document.createElement('div');
470
+ tempDiv.innerHTML = content;
471
+ const fragment = document.createDocumentFragment();
472
+ while (tempDiv.firstChild) {
473
+ fragment.appendChild(tempDiv.firstChild);
474
+ }
475
+ // Insert fragment tại vị trí range
476
+ savedRange.insertNode(fragment);
477
+ // Di chuyển cursor đến cuối nội dung vừa insert
478
+ if (fragment.lastChild) {
479
+ savedRange.setStartAfter(fragment.lastChild);
480
+ }
481
+ savedRange.collapse(true);
482
+ selection.removeAllRanges();
483
+ selection.addRange(savedRange);
484
+ };
367
485
 
368
486
  class UtilsUrlSearchParams {
369
487
  static instance;
@@ -1551,7 +1669,7 @@ const get = (obj, path, defaultValue = undefined, keepLastValueIfSignal) => {
1551
1669
  return obj;
1552
1670
  }
1553
1671
  if (obj instanceof HttpParams) {
1554
- return obj.get(`${path}`);
1672
+ return (obj.get(`${path}`) ?? defaultValue);
1555
1673
  }
1556
1674
  if (obj instanceof DOMRect) {
1557
1675
  return obj[path];
@@ -2778,5 +2896,5 @@ const createUniqueRandomIntGenerator = (min, max) => {
2778
2896
  * Generated bundle index. Do not edit.
2779
2897
  */
2780
2898
 
2781
- export { AudioExtList, CHARACTER_DATA_EMPTY, COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE, COMMUNICATE_MICRO_PREFIX_TYPE, DEFAULT_START_PAGE_0, DocumentExtList, ENCODE_URI_PATTERN, ERROR_MESSAGE_EMPTY_VALID, ERROR_MESSAGE_MAX_LENGTH, ERROR_MESSAGE_MAX_VALID, ERROR_MESSAGE_MIN_LENGTH, ERROR_MESSAGE_MIN_VALID, ERROR_MESSAGE_PATTEN_VALID, ExcelExtList, ImageExtList, LINK_IMAGE_ERROR_TOKEN_INJECT, PROCESS_BAR_STANDARD_CONFIG_DEFAULT_TOKEN_INJECT, PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT, UtilsCache, UtilsCommunicateMicro, UtilsCommunicateMicroKeyGlobal, UtilsHttpParamsRequest, UtilsHttpParamsRequestInstance, UtilsKeyCodeConstant, UtilsLanguageConstants, UtilsUrlSearchParams, VideoExtList, base64Decode, base64Encode, capitalize, checkMouseOverInContainer, checkViewInScreen, cloneDeep, cloneIBoundingClientRect, colorContrastFromOrigin, colorStepContrastFromOrigin, convertBase64ToBlob, convertBlobToFile, convertFileToBase64, convertFileToBase64_ObjectUrl, convertObjectToSignal, convertSignalToObject, convertUrlToFile, createUniqueRandomIntGenerator, decodeEscapeHtml, decodeURI, decrypt, decrypt3rd, deleteUnicode, downloadFileByUrl, downloadFileByUrlUseXmlRequest, downloadImageFromELement, encodeURI, encrypt, encrypt3rd, endCodeUrl, escapeHtml, firstLetterToUpperCase, formatDate, formatNumber, formatTextCompare, fullNameFormat, generateInterface, get, getColorById, getDayjs, getDeviceInfo, getDocumentByString, getDragEventByElement, getEventNameHandleClick, getFileExtension, getHTMLFromQuill, getKeyCacheByArrayObject, getLabelBySizeFile, getPlatFromBrowser, getSmartAxisScale, getViewport, groupBy, highlightByKeyword, isDifferenceDay, isDifferenceMonth, isDifferenceYear, isEmbedFrame, isEmpty, isEqual, isIncludeAudioExtList, isIncludeDocumentExtList, isIncludeImageExtList, isIncludeVideoExtList, isNil, isTypeAudio, isTypeFile, isTypeImage, isTypeVideo, keyBy, listColorDefine, md5, omitBy, patternAccount, patternDomain, patternEmail, patternEmoji, patternEncodeUri, patternGetFieldByRuleFieldReplace, patternHostUrl, patternKey, patternMobilePhone, patternName, patternNameProfile, patternNameSpecial, patternNameUtf8, patternNumber, patternPem, patternPhone, patternPhoneNormal, patternRuleFieldReplace, patternTax, patternUrl, protectString, range, removeEmoji, revealString, rgbToHex, set, setCaretPosition, setDefaultTimeZone, setKeyCrypto, setKeyCrypto3rd, setStylesElement, uniqBy, updateFunctionCheckEmbedFrame, updateFunctionFormatDate, updateFunctionXssFilter, uppercaseByPosition, uuid, viewDataNumberByLanguage, xssFilter };
2899
+ export { AudioExtList, CHARACTER_DATA_EMPTY, COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE, COMMUNICATE_MICRO_PREFIX_TYPE, DEFAULT_START_PAGE_0, DocumentExtList, ENCODE_URI_PATTERN, ERROR_MESSAGE_EMPTY_VALID, ERROR_MESSAGE_MAX_LENGTH, ERROR_MESSAGE_MAX_VALID, ERROR_MESSAGE_MIN_LENGTH, ERROR_MESSAGE_MIN_VALID, ERROR_MESSAGE_PATTEN_VALID, ExcelExtList, ImageExtList, LINK_IMAGE_ERROR_TOKEN_INJECT, PROCESS_BAR_STANDARD_CONFIG_DEFAULT_TOKEN_INJECT, PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT, UtilsCache, UtilsCommunicateMicro, UtilsCommunicateMicroKeyGlobal, UtilsHttpParamsRequest, UtilsHttpParamsRequestInstance, UtilsKeyCodeConstant, UtilsLanguageConstants, UtilsUrlSearchParams, VideoExtList, base64Decode, base64Encode, capitalize, checkMouseOverInContainer, checkViewInScreen, cloneDeep, cloneIBoundingClientRect, colorContrastFromOrigin, colorStepContrastFromOrigin, convertBase64ToBlob, convertBlobToFile, convertFileToBase64, convertFileToBase64_ObjectUrl, convertHtmlToDivBlocks, convertObjectToSignal, convertSignalToObject, convertUrlToFile, createUniqueRandomIntGenerator, decodeEscapeHtml, decodeURI, decrypt, decrypt3rd, deleteUnicode, downloadFileByUrl, downloadFileByUrlUseXmlRequest, downloadImageFromELement, encodeURI, encrypt, encrypt3rd, endCodeUrl, escapeHtml, firstLetterToUpperCase, formatDate, formatNumber, formatTextCompare, fullNameFormat, generateInterface, get, getColorById, getDayjs, getDeltaFromHTML, getDeviceInfo, getDocumentByString, getDragEventByElement, getEventNameHandleClick, getFileExtension, getHTMLFromQuill, getKeyCacheByArrayObject, getLabelBySizeFile, getPlatFromBrowser, getSmartAxisScale, getViewport, groupBy, highlightByKeyword, insertContentWithRange, isDifferenceDay, isDifferenceMonth, isDifferenceYear, isEmbedFrame, isEmpty, isEqual, isIncludeAudioExtList, isIncludeDocumentExtList, isIncludeImageExtList, isIncludeVideoExtList, isNil, isTypeAudio, isTypeFile, isTypeImage, isTypeVideo, keyBy, listColorDefine, md5, omitBy, patternAccount, patternDomain, patternEmail, patternEmoji, patternEncodeUri, patternGetFieldByRuleFieldReplace, patternHostUrl, patternKey, patternMobilePhone, patternName, patternNameProfile, patternNameSpecial, patternNameUtf8, patternNumber, patternPem, patternPhone, patternPhoneNormal, patternRuleFieldReplace, patternTax, patternUrl, processPasteData, protectString, range, removeEmoji, revealString, rgbToHex, set, setCaretPosition, setDefaultTimeZone, setKeyCrypto, setKeyCrypto3rd, setStylesElement, uniqBy, updateFunctionCheckEmbedFrame, updateFunctionFormatDate, updateFunctionXssFilter, uppercaseByPosition, uuid, viewDataNumberByLanguage, xssFilter };
2782
2900
  //# sourceMappingURL=libs-ui-utils.mjs.map