@libs-ui/utils 0.2.300 → 0.2.303

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,4 +1,4 @@
1
- import { fromEvent, tap, takeUntil, mergeMap, startWith, finalize, Observable, Subject, filter } from 'rxjs';
1
+ import { fromEvent, tap, takeUntil, mergeMap, startWith, finalize, Subject, filter, Observable } from 'rxjs';
2
2
  import Quill from 'quill';
3
3
  import DeviceDetector from 'device-detector-js';
4
4
  import CryptoES from 'crypto-es';
@@ -598,1422 +598,1607 @@ class UtilsHttpParamsRequest extends HttpParams {
598
598
  // "pathVariable-name": '124',
599
599
  // };
600
600
 
601
- /* eslint-disable @typescript-eslint/no-explicit-any */
602
- /**Các hàm tương tự thư viện lodash */
603
- /**
604
- * Kiểm tra xem một giá trị có phải là null hoặc undefined hay không
605
- * @param value Giá trị cần kiểm tra
606
- * @returns true nếu giá trị là null hoặc undefined, false nếu không
607
- * @example
608
- * isNil(null); // true
609
- * isNil(undefined); // true
610
- * isNil(0); // false
611
- * isNil('hello'); // false
612
- */
613
- const isNil = (value) => {
614
- return value === null || value === undefined;
615
- };
601
+ let key = '12~@#loqwsxacva(3rdhaq12';
616
602
  /**
617
- * Kiểm tra xem một giá trị có phải là rỗng hay không
618
- * @param value Giá trị cần kiểm tra
619
- * @returns true nếu giá trị là null, rỗng hoặc undefined, false nếu không
620
- * @example
621
- * isEmpty(null); // true
622
- * isEmpty(''); // true
623
- * isEmpty(undefined); // true
624
- * isEmpty({}); // true
625
- * isEmpty([]); // true
626
- * isEmpty([1, 2, 3]); // false
627
- * isEmpty({ a: 1 }); // false
603
+ * @description Thiết lập key hóa
604
+ * @param value key hóa, độ dài bằng 24 hoặc 32 ký tự.
628
605
  */
629
- const isEmpty = (value) => {
630
- while (isSignal(value)) {
631
- value = value();
606
+ const setKeyCrypto3rd = (value) => {
607
+ if (value.length !== 24 && value.length !== 32) {
608
+ throw Error(`key.length = ${value.length}; Key phải là 1 chuỗi dài 24 hoặc 32 ký tự`);
632
609
  }
633
- return value === null || value === '' || value === undefined || (typeof value === 'object' && (JSON.stringify(value) === '{}' || JSON.stringify(value) === '[]'));
610
+ key = value;
634
611
  };
635
- /**
636
- * Loại bỏ các thuộc tính của đối tượng dựa trên một hàm điều kiện
637
- * @param objData Đối tượng cần xử lý
638
- * @param predicate Hàm điều kiện để kiểm tra giá trị của thuộc tính. Nếu hàm trả về true thì thuộc tính đó sẽ bị loại bỏ
639
- * @returns Đối tượng mới sau khi đã loại bỏ các thuộc tính thỏa mãn điều kiện
640
- * @example
641
- * const obj = { a: 1, b: null, c: 3, d: undefined };
642
- * omitBy(obj, isNil); // { a: 1, c: 3 }
643
- */
644
- const omitBy = (objData, predicate) => {
645
- if (!objData || typeof objData !== 'object' || Array.isArray(objData)) {
646
- return objData;
612
+ const keyStore = () => {
613
+ return key;
614
+ };
615
+ const encrypt3rd = (plainData) => {
616
+ if (!keyStore()) {
617
+ throw Error("lỗi chưa setup key mã hóa");
647
618
  }
648
- const newObj = {};
649
- Object.keys(objData).forEach(key => {
650
- const valueOfKey = get(objData, key);
651
- if (!predicate(valueOfKey)) {
652
- set(newObj, key, valueOfKey);
653
- }
654
- });
655
- return newObj;
619
+ const key = CryptoES.enc.Hex.parse(keyStore());
620
+ const iv = CryptoES.enc.Hex.parse(keyStore());
621
+ const mode = CryptoES.mode.CBC;
622
+ const padding = CryptoES.pad.Pkcs7;
623
+ const options = { iv: iv, mode: mode, padding: padding };
624
+ return CryptoES.AES.encrypt(plainData, key, options).toString();
656
625
  };
657
- /**
658
- * Lấy giá trị từ đối tượng theo đường dẫn chỉ định
659
- *
660
- * Hàm này giúp bạn truy cập vào các thuộc tính sâu bên trong một đối tượng một cách an toàn,
661
- * tránh lỗi khi thuộc tính không tồn tại.
662
- *
663
- * @param obj Đối tượng nguồn cần lấy giá trị
664
- * @param path Đường dẫn đến thuộc tính cần lấy. Có thể là:
665
- * - Chuỗi: 'user.profile.name' hoặc 'items[0].title'
666
- * - Mảng: ['user', 'profile', 'name'] hoặc ['items', '0', 'title']
667
- * - Chuỗi rỗng '': trả về chính đối tượng gốc
668
- * @param defaultValue Giá trị mặc định trả về khi không tìm thấy thuộc tính (mặc định: undefined)
669
- * @param keepLastValueIfSignal Có giữ nguyên signal cuối cùng hay không (mặc định: false - sẽ gọi signal())
670
- * @returns Giá trị tìm được hoặc giá trị mặc định
671
- *
672
- * @example
673
- * // Ví dụ cơ bản
674
- * const user = { name: 'John', age: 30 };
675
- * get(user, 'name'); // 'John'
676
- * get(user, 'email'); // undefined
677
- * get(user, 'email', 'no-email'); // 'no-email'
678
- *
679
- * @example
680
- * // Truyền path rỗng - trả về chính đối tượng gốc
681
- * const data = { name: 'Alice', age: 25 };
682
- * get(data, ''); // { name: 'Alice', age: 25 } (chính đối tượng data)
683
- * get(data, '', 'default'); // { name: 'Alice', age: 25 } (bỏ qua defaultValue)
684
- *
685
- * @example
686
- * // Truy cập thuộc tính sâu
687
- * const data = {
688
- * user: {
689
- * profile: {
690
- * name: 'Alice',
691
- * settings: { theme: 'dark' }
692
- * }
693
- * }
694
- * };
695
- * get(data, 'user.profile.name'); // 'Alice'
696
- * get(data, 'user.profile.settings.theme'); // 'dark'
697
- * get(data, 'user.profile.avatar', 'default.jpg'); // 'default.jpg'
698
- *
699
- * @example
700
- * // Truy cập mảng
701
- * const items = [
702
- * { name: 'Item 1', price: 100 },
703
- * { name: 'Item 2', price: 200 }
704
- * ];
705
- * get(items, '[0].name'); // 'Item 1'
706
- * get(items, '[1].name'); // 'Item 2'
707
- * get(items, '[2].name', 'Not found'); // 'Not found'
708
- *
709
- * @example
710
- * // Sử dụng với mảng path
711
- * const nested = { a: { b: { c: 'deep value' } } };
712
- * get(nested, ['a', 'b', 'c']); // 'deep value'
713
- * get(nested, ['a', 'b', 'd'], 'default'); // 'default'
714
- *
715
- * @example
716
- * // Trường hợp đặc biệt
717
- * get(null, 'any.path'); // undefined
718
- * get(undefined, 'any.path', 'fallback'); // 'fallback'
719
- */
720
- const get = (obj, path, defaultValue = undefined, keepLastValueIfSignal) => {
721
- if (isNil(obj)) {
722
- return defaultValue;
626
+ const decrypt3rd = (encryptedData) => {
627
+ if (!keyStore()) {
628
+ throw Error("lỗi chưa setup key mã hóa");
723
629
  }
724
- while (isSignal(obj)) {
725
- obj = obj();
630
+ const key = CryptoES.enc.Hex.parse(keyStore());
631
+ const iv = CryptoES.enc.Hex.parse(keyStore());
632
+ const mode = CryptoES.mode.CBC;
633
+ const padding = CryptoES.pad.Pkcs7;
634
+ const options = { iv: iv, mode: mode, padding: padding };
635
+ return CryptoES.AES.decrypt(encryptedData, key, options).toString(CryptoES.enc.Utf8);
636
+ };
637
+
638
+ let functionCheck = () => {
639
+ return window.parent !== window.top;
640
+ };
641
+ const updateFunctionCheckEmbedFrame = (functionCustom) => {
642
+ functionCheck = functionCustom;
643
+ };
644
+ const isEmbedFrame = () => {
645
+ return functionCheck();
646
+ };
647
+
648
+ const ERROR_MESSAGE_EMPTY_VALID = 'i18n_valid_empty_message';
649
+ const ERROR_MESSAGE_PATTEN_VALID = 'i18n_valid_pattern_message';
650
+ const ERROR_MESSAGE_MIN_VALID = 'i18n_message_error_input_min_value';
651
+ const ERROR_MESSAGE_MAX_VALID = 'i18n_message_error_input_max_value';
652
+ const ERROR_MESSAGE_MIN_LENGTH = 'i18n_message_error_input_min_length';
653
+ const ERROR_MESSAGE_MAX_LENGTH = 'i18n_message_error_input_max_length';
654
+ const CHARACTER_DATA_EMPTY = '__';
655
+ const DEFAULT_START_PAGE_0 = 'defaultStartPage0';
656
+ const COMMUNICATE_MICRO_PREFIX_TYPE = '3RD_INTEGRATE_MICRO_SITE_';
657
+ const COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE = 'MICRO_SITES_ALL_MESSAGE';
658
+
659
+ /* eslint-disable @typescript-eslint/no-explicit-any */
660
+ class UtilsCommunicateMicroKeyGlobal {
661
+ static KEY_MESSAGE_MODAL = 'LIBS_UI_MODEL_EVENT';
662
+ }
663
+ class UtilsCommunicateMicro {
664
+ static initdEvent;
665
+ static subs = new Map();
666
+ static allMessageSub = new Subject();
667
+ static initEvent(currentWindow, onDestroy) {
668
+ if (this.initdEvent) {
669
+ return;
670
+ }
671
+ this.initdEvent = true;
672
+ if (!this.subs.get(COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE)) {
673
+ this.subs.set(COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE, UtilsCommunicateMicro.allMessageSub);
674
+ }
675
+ fromEvent(currentWindow, 'message').pipe(takeUntil(onDestroy)).subscribe(e => {
676
+ const event = { data: { ...e.data } };
677
+ const data = event.data;
678
+ const type = data.type;
679
+ if (!type) {
680
+ return this.allMessageSub.next(event);
681
+ }
682
+ const sub = UtilsCommunicateMicro.GetMessage(type);
683
+ if (!type || !data.response) {
684
+ return;
685
+ }
686
+ try {
687
+ if (type.includes(COMMUNICATE_MICRO_PREFIX_TYPE)) {
688
+ data.response = JSON.parse(decrypt3rd(data.response));
689
+ sub.next(event);
690
+ this.allMessageSub.next(event);
691
+ return;
692
+ }
693
+ data.response = JSON.parse(decrypt(data.response));
694
+ sub.next(event);
695
+ this.allMessageSub.next(event);
696
+ }
697
+ catch (error) {
698
+ console.log(error);
699
+ sub.next(event);
700
+ this.allMessageSub.next(event);
701
+ }
702
+ });
703
+ UtilsCommunicateMicro.GetMessage(UtilsCache.typeKeyClearLocalStorage).pipe(filter(e => e.data.response !== UtilsCache.idService), takeUntil(onDestroy)).subscribe(() => {
704
+ console.log('clear all cache by event');
705
+ UtilsCache.ClearAll();
706
+ });
726
707
  }
727
- if (path === '') {
728
- return obj;
708
+ static PostMessageToParent(data) {
709
+ data = this.convertData(data);
710
+ try {
711
+ if (isEmbedFrame()) {
712
+ window?.parent?.postMessage(data, "*");
713
+ return;
714
+ }
715
+ window?.top?.postMessage(data, "*");
716
+ }
717
+ catch (error) {
718
+ console.log(error);
719
+ }
729
720
  }
730
- if (obj instanceof HttpParams) {
731
- return obj.get(`${path}`);
721
+ static PostMessageToChildren(data) {
722
+ data = this.convertData(data);
723
+ const iframes = document.querySelectorAll("iframe");
724
+ Array.from(iframes).forEach(iframe => {
725
+ iframe?.contentWindow?.postMessage(data, '*');
726
+ });
732
727
  }
733
- if (obj instanceof DOMRect) {
734
- return obj[path];
728
+ static PostMessageToOpener(data) {
729
+ if (!window.opener) {
730
+ return;
731
+ }
732
+ data = this.convertData(data);
733
+ window.opener.postMessage(data, '*');
735
734
  }
736
- const paths = Array.isArray(path) ? path : path.replace(/\[(\d+)]/g, '.$1').split('.').filter(key => key);
737
- for (const index in paths) {
738
- const key = paths[index];
739
- if (obj instanceof CSSStyleDeclaration) {
740
- obj = obj[key];
741
- continue;
735
+ static convertData(data) {
736
+ if (!data || !data.type || !data.response) {
737
+ return data;
742
738
  }
743
- if (obj instanceof HTMLElement) {
744
- obj = obj[key];
745
- continue;
739
+ data = { ...data };
740
+ const type = data.type;
741
+ if (type.includes(COMMUNICATE_MICRO_PREFIX_TYPE)) {
742
+ try {
743
+ JSON.parse(decrypt3rd(data.response));
744
+ return data;
745
+ }
746
+ catch (_) {
747
+ data.response = encrypt3rd(JSON.stringify(data.response));
748
+ return data;
749
+ }
746
750
  }
747
- if (isNil(obj) || !Object.prototype.hasOwnProperty.call(obj, key)) {
748
- return defaultValue;
751
+ try {
752
+ JSON.parse(decrypt(data.response));
753
+ return data;
754
+ }
755
+ catch (_) {
756
+ data.response = encrypt(JSON.stringify(data.response));
757
+ return data;
749
758
  }
750
- const val = isSignal(obj[key]) && !keepLastValueIfSignal ? obj[key]() : obj[key];
751
- obj = val;
752
- }
753
- return obj;
754
- };
755
- /**
756
- * Thiết lập giá trị cho một thuộc tính trong đối tượng theo đường dẫn chỉ định
757
- * @param obj Đối tượng cần thiết lập giá trị
758
- * @param path Đường dẫn đến thuộc tính, có thể là chuỗi (vd: 'a.b.c') hoặc mảng (vd: ['a', 'b', 'c'])
759
- * @param value Giá trị cần thiết lập
760
- * @returns Đối tượng sau khi đã thiết lập giá trị
761
- * @throws Error nếu tham số đầu tiên không phải là đối tượng
762
- * @example
763
- * const obj = { a: { b: 1 } };
764
- * set(obj, 'a.b', 2); // { a: { b: 2 } }
765
- */
766
- const set = (obj, path, value) => {
767
- if (!obj || (typeof obj !== "object" && !isSignal(obj)) || (isSignal(obj) && typeof obj() !== "object")) {
768
- throw new Error("The first argument must be an object");
769
- }
770
- if (obj instanceof HttpParams) {
771
- return obj.set(`${path}`, value);
772
759
  }
773
- const pathArray = Array.isArray(path) ? path : path.replace(/\[(\d+)]/g, '.[$1]').split('.').filter(key => key);
774
- let currentObjectByKey = isSignal(obj) ? obj() : obj;
775
- let preObjectByKey = obj;
776
- pathArray.forEach((key, index) => {
777
- if (index < pathArray.length - 1) {
778
- if (!(key in currentObjectByKey) || (typeof currentObjectByKey[key] !== "object" && !isSignal(currentObjectByKey[key]))) {
779
- const nextKey = pathArray[index + 1];
780
- key = key.replace(/\[(\d+)]/g, '$1');
781
- currentObjectByKey[key] = /\[(\d+)]/g.test(nextKey) ? [] : {};
760
+ static GetMessage(messageType) {
761
+ if (!this.initdEvent) {
762
+ throw Error("chưa khơi tạo hàm lắng nghe sự kiện, gọi UtilsCommunicateMicro.initEvent(window) tại root component");
763
+ }
764
+ if (typeof messageType === 'string') {
765
+ let sub = this.subs.get(messageType);
766
+ if (!sub) {
767
+ sub = new Subject();
768
+ this.subs.set(messageType, sub);
769
+ return sub;
782
770
  }
783
- currentObjectByKey = key ? currentObjectByKey[key] : currentObjectByKey;
784
- preObjectByKey = currentObjectByKey;
785
- currentObjectByKey = isSignal(currentObjectByKey) ? currentObjectByKey() : currentObjectByKey;
786
- return;
771
+ return sub;
787
772
  }
788
- if (typeof currentObjectByKey !== "object") {
789
- return;
773
+ if (!Array.isArray(messageType) || !messageType.length) {
774
+ throw new Error('message type empty');
790
775
  }
791
- // Gán giá trị ở cuối đường dẫn
792
- key = key.replace(/\[(\d+)]/g, '$1');
793
- const valueOfKey = currentObjectByKey[key];
794
- const valueOfKeyIsSignal = isSignal(valueOfKey);
795
- if (valueOfKeyIsSignal && (typeof valueOfKey() !== 'object' || valueOfKey() === null)) {
796
- valueOfKey.set(isSignal(value) ? value() : value);
797
- return;
776
+ if (messageType.length === 1) {
777
+ return this.GetMessage(messageType[0]);
798
778
  }
799
- if (isSignal(preObjectByKey)) {
800
- preObjectByKey.update((data) => {
801
- const dataOfKey = data[key];
802
- if (isNil(dataOfKey) || !valueOfKeyIsSignal) {
803
- data[key] = value;
804
- }
805
- if (valueOfKeyIsSignal) {
806
- valueOfKey.set(isSignal(value) ? value() : value);
807
- }
808
- if (Array.isArray(data)) {
809
- return [...data];
810
- }
811
- return { ...data };
779
+ const types = messageType.sort().join(';');
780
+ let sub = this.subs.get(types);
781
+ if (sub) {
782
+ return sub;
783
+ }
784
+ sub = new Subject();
785
+ this.subs.set(types, sub);
786
+ this.initSubject(sub, messageType);
787
+ return sub;
788
+ }
789
+ static initSubject(subRoot, messageType) {
790
+ messageType.forEach(key => {
791
+ this.GetMessage(key).subscribe(e => {
792
+ subRoot.next(e);
812
793
  });
794
+ });
795
+ }
796
+ }
797
+
798
+ class UtilsLanguageConstants {
799
+ static VI = "vi"; // Tiếng Việt
800
+ static EN = "en"; // Tiếng Anh
801
+ static FR = "fr"; // Tiếng Pháp
802
+ static DE = "de"; // Tiếng Đức
803
+ static ES = "es"; // Tiếng Tây Ban Nha
804
+ static ZH = "zh"; // Tiếng Trung (Giản thể)
805
+ static ZH_TW = "zh-TW"; // Tiếng Trung (Phồn thể)
806
+ static JA = "ja"; // Tiếng Nhật
807
+ static KO = "ko"; // Tiếng Hàn
808
+ static RU = "ru"; // Tiếng Nga
809
+ static IT = "it"; // Tiếng Ý
810
+ static PT = "pt"; // Tiếng Bồ Đào Nha
811
+ static TH = "th"; // Tiếng Thái
812
+ static ID = "id"; // Tiếng Indonesia
813
+ static MS = "ms"; // Tiếng Malaysia
814
+ static AR = "ar"; // Tiếng Ả Rập
815
+ static HI = "hi"; // Tiếng Hindi
816
+ static BN = "bn"; // Tiếng Bengal
817
+ static TR = "tr"; // Tiếng Thổ Nhĩ Kỳ
818
+ static NL = "nl"; // Tiếng Hà Lan
819
+ static KM = "km"; // Tiếng Khmer (Campuchia)
820
+ static LO = "lo"; // Tiếng Lào
821
+ static MY = "my"; // Tiếng Miến Điện (Myanmar)
822
+ static TL = "tl"; // Tiếng Tagalog (Philippines)
823
+ static CEB = "ceb"; // Tiếng Cebuano (Philippines)
824
+ static JV = "jv"; // Tiếng Java (Indonesia)
825
+ static SU = "su"; // Tiếng Sundanese (Indonesia)
826
+ // Ngôn ngữ mặc định
827
+ static defaultLang = UtilsLanguageConstants.VI;
828
+ // Danh sách các ngôn ngữ được hỗ trợ
829
+ static supportedLanguages = new Set([
830
+ UtilsLanguageConstants.VI,
831
+ UtilsLanguageConstants.EN,
832
+ UtilsLanguageConstants.FR,
833
+ UtilsLanguageConstants.DE,
834
+ UtilsLanguageConstants.ES,
835
+ UtilsLanguageConstants.ZH,
836
+ UtilsLanguageConstants.ZH_TW,
837
+ UtilsLanguageConstants.JA,
838
+ UtilsLanguageConstants.KO,
839
+ UtilsLanguageConstants.RU,
840
+ UtilsLanguageConstants.IT,
841
+ UtilsLanguageConstants.PT,
842
+ UtilsLanguageConstants.TH,
843
+ UtilsLanguageConstants.ID,
844
+ UtilsLanguageConstants.MS,
845
+ UtilsLanguageConstants.AR,
846
+ UtilsLanguageConstants.HI,
847
+ UtilsLanguageConstants.BN,
848
+ UtilsLanguageConstants.TR,
849
+ UtilsLanguageConstants.NL,
850
+ UtilsLanguageConstants.KM,
851
+ UtilsLanguageConstants.LO,
852
+ UtilsLanguageConstants.MY,
853
+ UtilsLanguageConstants.TL,
854
+ UtilsLanguageConstants.CEB,
855
+ UtilsLanguageConstants.JV,
856
+ UtilsLanguageConstants.SU,
857
+ ]);
858
+ /**
859
+ * Kiểm tra xem ngôn ngữ đầu vào có được hỗ trợ không
860
+ * @param lang Mã ngôn ngữ cần kiểm tra
861
+ * @returns True nếu được hỗ trợ, False nếu không
862
+ */
863
+ static isSupported(lang) {
864
+ return UtilsLanguageConstants.supportedLanguages.has(lang);
865
+ }
866
+ }
867
+
868
+ /* eslint-disable no-async-promise-executor */
869
+ /* eslint-disable @typescript-eslint/no-explicit-any */
870
+ class UtilsCache {
871
+ static CACHE_EXPIRE_TIME_DEFAULT = 5 * 60;
872
+ static CACHE_EXPIRE_NONE = -1;
873
+ static idService = uuid();
874
+ static typeKeyClearLocalStorage = 'LIBS_UI_CLEAR_LOCAL_STORAGE_EVENT';
875
+ static languageKeyCache = 'SR3xQKxHgffiCevPQRralg';
876
+ static listKeyKeepWhenClearALll = Array();
877
+ static initdEvent;
878
+ static storage;
879
+ static dbName = 'libs-ui-cache';
880
+ static itemIndexByKey = 'key';
881
+ static dbVersion = 1;
882
+ static db = null;
883
+ static init(config) {
884
+ if (this.initdEvent) {
813
885
  return;
814
886
  }
815
- if (valueOfKeyIsSignal) {
816
- valueOfKey.set(isSignal(value) ? value() : value);
817
- return;
887
+ this.initdEvent = true;
888
+ if (config.indexedDBName) {
889
+ this.dbName = config.indexedDBName;
890
+ }
891
+ if (config.typeKeyClearLocalStorage) {
892
+ this.typeKeyClearLocalStorage = config.typeKeyClearLocalStorage;
893
+ }
894
+ if (config.listKeyKeepWhenClearAll) {
895
+ this.listKeyKeepWhenClearALll = config.listKeyKeepWhenClearAll;
896
+ }
897
+ if (config.languageKeyCache) {
898
+ this.languageKeyCache = config.languageKeyCache;
818
899
  }
819
- currentObjectByKey[key] = value;
820
- });
821
- return obj;
822
- };
823
- /**
824
- * Tạo một bản sao sâu của một đối tượng hoặc giá trị bất kỳ
825
- * @param data Dữ liệu cần sao chép
826
- * @param options Tùy chọn cấu hình
827
- * @param options.ignoreSignal Nếu true, sẽ không sao chép các signal mà trả về signal gốc
828
- * @param seen WeakMap để theo dõi các tham chiếu đã được sao chép, tránh lặp vô hạn với các tham chiếu vòng
829
- * @returns Bản sao sâu của dữ liệu đầu vào
830
- * @example
831
- * const obj = {
832
- * a: 1,
833
- * b: { c: 2 },
834
- * d: [1, 2, 3]
835
- * };
836
- * const clone = cloneDeep(obj);
837
- * // clone là một bản sao hoàn toàn độc lập của obj
838
- */
839
- const cloneDeep = (data, options = { ignoreSignal: false }, seen = new WeakMap()) => {
840
- if (data === null || (typeof data !== 'object' && !isSignal(data))) {
841
- return data;
842
- }
843
- if (seen.has(data)) {
844
- return seen.get(data);
845
- }
846
- if (data instanceof HttpParams) {
847
- return new UtilsHttpParamsRequest(undefined, data);
848
900
  }
849
- if (data instanceof Date) {
850
- return new Date(data.getTime());
901
+ static setLang(lang) {
902
+ if (!UtilsLanguageConstants.isSupported(lang)) {
903
+ throw Error(`Language not support ${lang}`);
904
+ }
905
+ this.Set(this.languageKeyCache, lang, this.CACHE_EXPIRE_NONE);
851
906
  }
852
- if (data instanceof RegExp) {
853
- return new RegExp(data.source, data.flags);
907
+ static getLang() {
908
+ return this.Get(this.languageKeyCache, UtilsLanguageConstants.defaultLang);
854
909
  }
855
- if (data instanceof Map) {
856
- const mapCopy = new Map();
857
- seen.set(data, mapCopy);
858
- data.forEach((val, key) => {
859
- mapCopy.set(cloneDeep(key, options, seen), cloneDeep(val, options, seen));
860
- });
861
- return mapCopy;
862
- }
863
- if (data instanceof Set) {
864
- const setCopy = new Set();
865
- seen.set(data, setCopy);
866
- data.forEach(val => {
867
- setCopy.add(cloneDeep(val, options, seen));
910
+ static openDB() {
911
+ return new Promise(resolve => {
912
+ const request = indexedDB.open(this.dbName, this.dbVersion);
913
+ request.onupgradeneeded = (event) => {
914
+ const db = event.target.result;
915
+ if (!db.objectStoreNames.contains(this.dbName)) {
916
+ const objectStore = db.createObjectStore(this.dbName, { keyPath: this.itemIndexByKey });
917
+ objectStore.createIndex(this.itemIndexByKey, this.itemIndexByKey, { unique: true });
918
+ }
919
+ };
920
+ request.onsuccess = () => {
921
+ this.db = request.result;
922
+ resolve(true);
923
+ };
924
+ request.onerror = (event) => {
925
+ console.trace('Error opening IndexedDB:', event);
926
+ resolve(false);
927
+ };
868
928
  });
869
- return setCopy;
870
- }
871
- if (Array.isArray(data)) {
872
- seen.set(data, data.map(item => cloneDeep(item, options, seen)));
873
- return seen.get(data);
874
- }
875
- if (data instanceof File || data instanceof Blob || Object.prototype.toString.call(data) === '[object File]') {
876
- return data;
877
- }
878
- if (data instanceof TemplateRef || data instanceof ElementRef || data instanceof Element || data instanceof Promise || data instanceof Observable) {
879
- return data;
880
929
  }
881
- if (isSignal(data)) {
882
- if (options?.ignoreSignal) {
883
- return data;
930
+ static async getObjectStore() {
931
+ if (!this.db) {
932
+ await this.openDB();
884
933
  }
885
- seen.set(data, signal(cloneDeep(data(), options, seen)));
886
- return seen.get(data);
934
+ const transaction = this.db?.transaction([this.dbName], 'readwrite');
935
+ if (!transaction) {
936
+ return null;
937
+ }
938
+ return transaction.objectStore(this.dbName);
887
939
  }
888
- const result = {};
889
- seen.set(data, result);
890
- for (const key in data) {
891
- const value = data[key];
892
- if (value instanceof HttpParams) {
893
- result[key] = new UtilsHttpParamsRequest(undefined, value);
894
- continue;
940
+ static get LocalStorage() {
941
+ try {
942
+ if (typeof window.localStorage !== 'undefined') {
943
+ const OS = getPlatFromBrowser();
944
+ if (OS.includes('Safari') && parseFloat(OS.split(' ').pop() || '0') >= 16) {
945
+ return this.getLocalStorageFakeOnSafari();
946
+ }
947
+ return localStorage;
948
+ }
949
+ return this.getLocalStorageFake();
895
950
  }
896
- if (value instanceof TemplateRef || value instanceof ElementRef || value instanceof Element || value instanceof Promise || value instanceof Observable) {
897
- result[key] = value;
898
- continue;
951
+ catch (error) {
952
+ console.trace(`LocalStorage `, error);
953
+ return this.getLocalStorageFake();
899
954
  }
900
- if (Object.prototype.hasOwnProperty.call(data, key)) {
901
- result[key] = cloneDeep(value, options, seen);
955
+ }
956
+ static getLocalStorageFakeOnSafari() {
957
+ if (typeof window.localStorage !== 'undefined' && !this.storage && Object.keys(localStorage).length) {
958
+ this.storage = { ...localStorage };
902
959
  }
960
+ return {
961
+ setItem: (key, value) => {
962
+ localStorage.setItem(key, value);
963
+ this.storage[key] = value;
964
+ },
965
+ getItem: (key) => {
966
+ const value = localStorage.getItem(key);
967
+ if (value) {
968
+ return value;
969
+ }
970
+ if (!this.storage || isNil(this.storage[key])) {
971
+ return null;
972
+ }
973
+ localStorage.setItem(key, this.storage[key]);
974
+ return this.storage?.[key];
975
+ },
976
+ removeItem: (key) => {
977
+ delete this.storage?.[key];
978
+ localStorage.removeItem(key);
979
+ },
980
+ clear: () => {
981
+ this.storage = {};
982
+ localStorage.clear();
983
+ }
984
+ };
903
985
  }
904
- return result;
905
- };
906
- /**
907
- * Chuyển đổi một mảng các đối tượng thành một đối tượng với khóa được chỉ định
908
- * @param data Mảng các đối tượng cần chuyển đổi
909
- * @param key Tên thuộc tính được sử dụng làm khóa trong đối tượng kết quả
910
- * @returns Đối tượng với các giá trị từ mảng được đánh key theo thuộc tính đã chỉ định
911
- * @example
912
- * const data = [
913
- * { id: 1, name: 'John' },
914
- * { id: 2, name: 'Jane' }
915
- * ];
916
- * keyBy(data, 'id');
917
- * // Kết quả: {
918
- * // '1': { id: 1, name: 'John' },
919
- * // '2': { id: 2, name: 'Jane' }
920
- * // }
921
- */
922
- const keyBy = (data, key) => {
923
- if (!data || !data.length || !key) {
924
- return {};
986
+ static getLocalStorageFake() {
987
+ if (!this.storage) {
988
+ this.storage = {};
989
+ }
990
+ return {
991
+ setItem: (key, value) => {
992
+ this.storage[key] = value;
993
+ },
994
+ getItem: (key) => {
995
+ return this.storage?.[key];
996
+ },
997
+ removeItem: (key) => {
998
+ delete this.storage?.[key];
999
+ },
1000
+ clear: () => {
1001
+ this.storage = {};
1002
+ }
1003
+ };
925
1004
  }
926
- return data.reduce((dir, nextItem) => {
927
- const valueOfKey = get(nextItem, key);
928
- if (typeof valueOfKey !== 'string' && typeof valueOfKey !== 'number' && typeof valueOfKey !== 'boolean') {
929
- return dir;
1005
+ static async GetAsync(key, default_value, isKeyMD5 = false) {
1006
+ key = isKeyMD5 ? key : md5(key);
1007
+ return new Promise(async (resolve) => {
1008
+ const objectStore = await this.getObjectStore();
1009
+ if (!objectStore) {
1010
+ return resolve(default_value);
1011
+ }
1012
+ const request = objectStore.get(key);
1013
+ request.onsuccess = () => {
1014
+ if (!request.result) {
1015
+ return resolve(default_value);
1016
+ }
1017
+ const data = JSON.parse(decrypt(request.result.value));
1018
+ if (data.expire === this.CACHE_EXPIRE_NONE) {
1019
+ return resolve(data.json);
1020
+ }
1021
+ const currentMillisecond = (new Date().valueOf() / 1000);
1022
+ if (data.expire < currentMillisecond) {
1023
+ return resolve(default_value);
1024
+ }
1025
+ return resolve(data.json);
1026
+ };
1027
+ request.onerror = () => {
1028
+ console.trace(`Get key ${key} Error, return default value: ${default_value}`);
1029
+ return resolve(default_value);
1030
+ };
1031
+ });
1032
+ }
1033
+ static Get(key, default_value) {
1034
+ if (!key) {
1035
+ return this.GetDefaultValueBySpecificKey(key, default_value);
930
1036
  }
931
- if (!Object.keys(dir).includes(`${valueOfKey}`)) {
932
- dir[`${valueOfKey}`] = nextItem;
1037
+ const cachedData = this.LocalStorage.getItem(key);
1038
+ if (!cachedData) {
1039
+ return this.GetDefaultValueBySpecificKey(key, default_value);
933
1040
  }
934
- return dir;
935
- }, {});
936
- };
937
- /**
938
- * Nhóm các đối tượng trong một mảng thành các nhóm dựa trên một thuộc tính cụ thể
939
- * @param data Mảng các đối tượng cần nhóm
940
- * @param key Tên thuộc tính được sử dụng làm khóa nhóm
941
- * @returns Đối tượng với các giá trị từ mảng được nhóm theo thuộc tính đã chỉ định
942
- * @example
943
- * const data = [
944
- * { id: 1, name: 'John' },
945
- * { id: 2, name: 'Jane' },
946
- * { id: 1, name: 'John' }
947
- * ];
948
- * groupBy(data, 'id');
949
- * // Kết quả: {
950
- * // '1': [
951
- * // { id: 1, name: 'John' },
952
- * // { id: 1, name: 'John' }
953
- * // ],
954
- * // '2': [
955
- * // { id: 2, name: 'Jane' }
956
- * // }
957
- */
958
- const groupBy = (data, key) => {
959
- if (!data || !data.length || !Object.keys(get(data, '0')).length || !key) {
960
- return {};
961
- }
962
- return data.reduce((dir, nextItem) => {
963
- const valueOfKey = get(nextItem, key);
964
- if (typeof valueOfKey !== 'string' && typeof valueOfKey !== 'number' && typeof valueOfKey !== 'boolean') {
965
- return dir;
1041
+ try {
1042
+ const data = JSON.parse(decrypt(cachedData));
1043
+ if (data.expire === this.CACHE_EXPIRE_NONE) {
1044
+ return data.value ?? default_value;
1045
+ }
1046
+ const currentMillisecond = (new Date().valueOf() / 1000);
1047
+ if (data.expire < currentMillisecond) {
1048
+ return this.GetDefaultValueBySpecificKey(key, default_value);
1049
+ }
1050
+ return data.value;
966
1051
  }
967
- if (!Object.keys(dir).includes(`${valueOfKey}`)) {
968
- dir[`${valueOfKey}`] = [];
1052
+ catch (error) {
1053
+ console.trace(`Get key ${key} Error, return default value: ${default_value}`, error);
1054
+ return this.GetDefaultValueBySpecificKey(key, default_value);
969
1055
  }
970
- dir[`${valueOfKey}`].push(nextItem);
971
- return dir;
972
- }, {});
973
- };
974
- /**
975
- * Tạo một mảng các số từ giá trị bắt đầu đến giá trị kết thúc với bước nhảy tùy chọn
976
- * @param start Giá trị bắt đầu của dãy số. Nếu chỉ có một tham số, đây sẽ là giá trị kết thúc và giá trị bắt đầu sẽ là 0
977
- * @param end Giá trị kết thúc của dãy số (tùy chọn)
978
- * @param step Bước nhảy giữa các số trong dãy (tùy chọn). Mặc định là 1 nếu end > start, -1 nếu end < start
979
- * @returns Mảng các số từ start đến end với bước nhảy step
980
- * @example
981
- * range(4); // [0, 1, 2, 3]
982
- * range(1, 5); // [1, 2, 3, 4]
983
- * range(0, 20, 5); // [0, 5, 10, 15]
984
- * range(5, 2); // [5, 4, 3]
985
- */
986
- const range = (start, end, step) => {
987
- if (end === undefined || end === null) {
988
- end = start;
989
- start = 0;
990
1056
  }
991
- if (!step) {
992
- step = end < 0 ? -1 : 1;
993
- }
994
- if (end < start && step > 0) {
995
- step *= -1;
1057
+ static GetDefaultValueBySpecificKey(key, default_value) {
1058
+ return default_value;
996
1059
  }
997
- const valueStartByStep = step + start;
998
- const direction = start < end ? 'asc' : 'desc';
999
- if ((direction === 'asc' && (valueStartByStep < start || valueStartByStep > end)) || (direction === 'desc' && (valueStartByStep > start || valueStartByStep < end))) {
1000
- return [start];
1060
+ static async SetAsync(key, value, expireTimeBySecond = this.CACHE_EXPIRE_TIME_DEFAULT, isKeyMD5 = false) {
1061
+ return new Promise(async (resolve) => {
1062
+ const objectStore = await this.getObjectStore();
1063
+ key = isKeyMD5 ? key : md5(key);
1064
+ try {
1065
+ const currentMillisecond = expireTimeBySecond === this.CACHE_EXPIRE_NONE ? this.CACHE_EXPIRE_NONE : (new Date().valueOf() / 1000) + expireTimeBySecond;
1066
+ const data = {
1067
+ value: encrypt(JSON.stringify({ json: value, expire: currentMillisecond })),
1068
+ };
1069
+ data[this.itemIndexByKey] = key;
1070
+ if (!objectStore) {
1071
+ console.trace(`Can not open object store`);
1072
+ return resolve({ key, messageError: 'Can not open object store' });
1073
+ }
1074
+ const request = objectStore?.put(data);
1075
+ request.onsuccess = () => {
1076
+ console.log(`Set key ${key} Success`);
1077
+ resolve(request.result);
1078
+ };
1079
+ request.onerror = (error) => {
1080
+ console.trace(`Set key ${key} Error`);
1081
+ resolve({ key, messageError: get(error, 'message') });
1082
+ };
1083
+ }
1084
+ catch (error) {
1085
+ console.trace(`Set key ${key} Error`);
1086
+ resolve({ key, messageError: get(error, 'message') });
1087
+ }
1088
+ });
1001
1089
  }
1002
- const arr = new Array();
1003
- for (let index = 0; index < Math.abs(end - start); index++) {
1004
- let value = start + index * step;
1005
- if (index === 0) {
1006
- value = start;
1090
+ static Set(key, value, expireTimeBySecond = this.CACHE_EXPIRE_TIME_DEFAULT) {
1091
+ const currentMillisecond = expireTimeBySecond === this.CACHE_EXPIRE_NONE ? this.CACHE_EXPIRE_NONE : (new Date().valueOf() / 1000) + expireTimeBySecond;
1092
+ const data = {
1093
+ value: value,
1094
+ expire: currentMillisecond
1095
+ };
1096
+ try {
1097
+ this.LocalStorage.setItem(key, encrypt(JSON.stringify(data)));
1098
+ return true;
1007
1099
  }
1008
- if ((direction === 'asc' && (value < start || value > end)) || (direction === 'desc' && (value > start || value < end))) {
1009
- return arr;
1100
+ catch (error) {
1101
+ console.trace(`Set key ${key} Error`, error);
1102
+ return false;
1010
1103
  }
1011
- arr.push(value);
1012
1104
  }
1013
- return arr;
1014
- };
1015
- /**
1016
- * So sánh hai giá trị bất kỳ có bằng nhau hay không
1017
- * @param value1 Giá trị thứ nhất cần so sánh
1018
- * @param value2 Giá trị thứ hai cần so sánh
1019
- * @param exactlyPosition so sánh chính xác vị trí các phần tử trong mảng hay không
1020
- * @returns true nếu hai giá trị bằng nhau, false nếu không bằng nhau
1021
- * @example
1022
- * isEqual([1,2,3], [1,2,3]); // true
1023
- * isEqual([1,2,3], [3,2,1]); // true khi exactlyPosition = false
1024
- * isEqual([1,2,3], [3,2,1]); // false khi exactlyPosition = true
1025
- * isEqual({a:1}, {a:1}); // true
1026
- */
1027
- const isEqual = (value1, value2, options) => {
1028
- const { exactlyPosition = false, ignoreExactlyDataType = false } = options || {};
1029
- if (value1 === value2 || (value1 === null && value2 === null) || (value1 === undefined && value2 === undefined)) {
1030
- return true;
1105
+ static async ClearAsync(key, isMD5 = false) {
1106
+ return new Promise(async (resolve) => {
1107
+ const objectStore = await this.getObjectStore();
1108
+ if (!objectStore) {
1109
+ return resolve();
1110
+ }
1111
+ const request = objectStore.delete(isMD5 ? key : md5(key));
1112
+ request.onsuccess = () => {
1113
+ resolve({ clearSuccess: true });
1114
+ };
1115
+ request.onerror = (event) => {
1116
+ console.trace('Error deleting Key:', get(event.target.error, 'message'));
1117
+ resolve({ messageError: get(event.target.error, 'message'), clearSuccess: false });
1118
+ };
1119
+ });
1031
1120
  }
1032
- if (ignoreExactlyDataType) {
1033
- return isEqual(isNil(value1) ? undefined : `${value1}`, isNil(value2) ? undefined : `${value2}`);
1121
+ static Clear(key) {
1122
+ if (key.includes('kc-callback-')) {
1123
+ return;
1124
+ }
1125
+ this.LocalStorage.removeItem(key);
1034
1126
  }
1035
- // Handle signals
1036
- while (isSignal(value1)) {
1037
- value1 = value1();
1127
+ static ClearAllAsync() {
1128
+ return new Promise(async (resolve) => {
1129
+ const objectStore = await this.getObjectStore();
1130
+ if (!objectStore) {
1131
+ return resolve();
1132
+ }
1133
+ const request = objectStore.clear();
1134
+ request.onsuccess = () => {
1135
+ console.log('clear all successfully');
1136
+ resolve({ clearSuccess: true });
1137
+ };
1138
+ request.onerror = (event) => {
1139
+ console.trace('Error deleting key:', get(event.target.error, 'message'));
1140
+ resolve({ messageError: get(event.target.error, 'message'), clearSuccess: false });
1141
+ };
1142
+ });
1038
1143
  }
1039
- while (isSignal(value2)) {
1040
- value2 = value2();
1144
+ static ClearAll() {
1145
+ if (isEmbedFrame()) {
1146
+ const data = {
1147
+ type: this.typeKeyClearLocalStorage,
1148
+ response: {
1149
+ idEvent: this.idService
1150
+ }
1151
+ };
1152
+ UtilsCommunicateMicro.PostMessageToParent(data);
1153
+ }
1154
+ const keys = [...this.listKeyKeepWhenClearALll];
1155
+ Object.keys(this.LocalStorage).forEach(key => {
1156
+ if (key.includes('kc-callback-')) {
1157
+ keys.push(key);
1158
+ }
1159
+ });
1160
+ const stores = this.GetDataByKeys(Array.from(keys));
1161
+ this.LocalStorage.clear();
1162
+ this.SetDataByKey(stores);
1041
1163
  }
1042
- if (typeof value1 !== 'object' || typeof value2 !== 'object' || (Array.isArray(value1) && !Array.isArray(value2)) || (!Array.isArray(value1) && Array.isArray(value2))) {
1043
- return false;
1164
+ static GetDataByKeys(keys) {
1165
+ const stores = new Map();
1166
+ keys.forEach((key) => {
1167
+ if (key.includes('kc-callback-')) {
1168
+ stores.set(key, this.LocalStorage.getItem(key));
1169
+ return;
1170
+ }
1171
+ stores.set(key, this.Get(key));
1172
+ });
1173
+ return stores;
1044
1174
  }
1045
- if (Array.isArray(value1)) {
1046
- if (value1.length !== value2.length) {
1047
- return false;
1048
- }
1049
- if (!exactlyPosition) {
1050
- return !value1.some(item => !value2.includes(item));
1175
+ static SetDataByKey(stores) {
1176
+ stores.forEach((value, key) => {
1177
+ if (key.includes('kc-callback-')) {
1178
+ this.LocalStorage.setItem(key, value);
1179
+ return;
1180
+ }
1181
+ if (key === this.languageKeyCache) {
1182
+ return this.setLang(value);
1183
+ }
1184
+ this.Set(key, value, this.CACHE_EXPIRE_NONE);
1185
+ });
1186
+ }
1187
+ static DeleteKeyStartWithAsync(keyCacheStartWith, isKeyMD5 = false) {
1188
+ return new Promise(async (resolve) => {
1189
+ const objectStore = await this.getObjectStore();
1190
+ if (!objectStore) {
1191
+ return resolve({});
1192
+ }
1193
+ // Lấy tất cả các keys từ index
1194
+ const request = objectStore.getAll();
1195
+ keyCacheStartWith = isKeyMD5 ? keyCacheStartWith : md5(keyCacheStartWith);
1196
+ request.onsuccess = (e) => {
1197
+ const data = e.target.result;
1198
+ if (!Array.isArray(data)) {
1199
+ return resolve({});
1200
+ }
1201
+ data.forEach(obj => {
1202
+ if (obj[this.itemIndexByKey].startsWith(keyCacheStartWith)) {
1203
+ this.ClearAsync(obj[this.itemIndexByKey], true);
1204
+ }
1205
+ });
1206
+ return resolve({});
1207
+ };
1208
+ request.onerror = () => {
1209
+ return resolve({});
1210
+ };
1211
+ });
1212
+ }
1213
+ static DeleteKeyStartWith(keyCache, isMD5 = false) {
1214
+ keyCache = isMD5 ? md5(keyCache) : keyCache;
1215
+ const keys = Object.keys(this.LocalStorage);
1216
+ if (!keys || !keys.length) {
1217
+ return;
1051
1218
  }
1052
- return !value1.some((item, index) => !isEqual(item, value2[index], options));
1219
+ keys.forEach(key => {
1220
+ if (key.startsWith(keyCache)) {
1221
+ this.Clear(key);
1222
+ }
1223
+ });
1053
1224
  }
1054
- if (Object.keys(value1).length !== Object.keys(value2).length) {
1055
- return false;
1225
+ static DeleteDatabaseIndexDB(dbName) {
1226
+ return new Promise((resolve) => {
1227
+ dbName = (dbName || this.dbName);
1228
+ const request = indexedDB.deleteDatabase(dbName);
1229
+ request.onsuccess = () => {
1230
+ console.trace('Database deleted successfully');
1231
+ resolve({ deleteSuccess: true });
1232
+ };
1233
+ request.onerror = (event) => {
1234
+ console.trace('Error deleting database:', event.target.error);
1235
+ resolve({ messageError: get(event.target.error, 'message'), deleteSuccess: false });
1236
+ };
1237
+ request.onblocked = () => {
1238
+ console.trace('Delete request is blocked');
1239
+ resolve({ messageError: 'Delete request is blocked', deleteSuccess: false });
1240
+ };
1241
+ });
1056
1242
  }
1057
- return !Object.keys(value1).some((key) => !isEqual(value1[key], value2[key], options));
1243
+ }
1244
+
1245
+ dayjs.extend(localeData);
1246
+ dayjs.extend(updateLocale);
1247
+ dayjs.extend(utc);
1248
+ dayjs.extend(timezone);
1249
+ dayjs.extend(customParseFormat);
1250
+ let timeZoneSetup = "Asia/Ho_Chi_Minh";
1251
+ const setDefaultTimeZone = (localeZone = timeZoneSetup) => {
1252
+ timeZoneSetup = localeZone;
1253
+ dayjs.tz.setDefault(localeZone);
1254
+ };
1255
+ let functionFormatDate = undefined;
1256
+ const updateFunctionFormatDate = (functionCustom) => {
1257
+ functionFormatDate = functionCustom;
1058
1258
  };
1059
1259
  /**
1060
- * Loại bỏ các phần tử trùng lặp trong mảng dựa trên một thuộc tính chỉ định
1061
- * @param data Mảng dữ liệu cần xử
1062
- * @param key Tên thuộc tính dùng để so sánh trùng lặp. Nếu không key thì so sánh trực tiếp giá trị
1063
- * @returns Mảng mới chứa các phần tử không trùng lặp
1064
- * @example
1065
- * const arr = [
1066
- * { id: 1, name: 'A' },
1067
- * { id: 2, name: 'B' },
1068
- * { id: 1, name: 'C' }
1069
- * ];
1070
- * uniqBy(arr, 'id'); // [{ id: 1, name: 'A' }, { id: 2, name: 'B' }]
1071
- *
1072
- * const numbers = [1, 2, 2, 3, 3];
1073
- * uniqBy(numbers); // [1, 2, 3]
1074
- *
1075
- * const numbersSignal = [signal(1), signal(2), signal(3), signal(2), signal(5), signal(4), signal(1), signal(6), signal(7), signal(6)];
1076
- * uniqBy(numbersSignal); // [signal(1), signal(2), signal(3), signal(5), signal(4), signal(6), signal(7)]
1077
- */
1078
- const uniqBy = (data, key) => {
1079
- if (!key || !data?.length || typeof get(data, '0') !== 'object') {
1080
- // Xử lý mảng chứa signal values
1081
- if (data[0] && isSignal(data[0])) {
1082
- const seen = new Set();
1083
- return data.filter(item => {
1084
- const value = `${get(item, '')}`;
1085
- if (seen.has(value)) {
1086
- return false;
1087
- }
1088
- seen.add(value);
1089
- return true;
1090
- });
1091
- }
1092
- // Xử lý mảng primitive values
1093
- return Array.from(new Set(data));
1260
+ * @description Lấy đối tượng dayjs theo config
1261
+ * @param config nếu không config sẽ trả về đối tượng dayjs là thời gian hiện tại
1262
+ * @param config.date thời gian cần lấy
1263
+ * @param config.returnDayjsIfConfigDateNotExist true nếu muốn trả về đối tượng dayjs nếu config.date không
1264
+ * @param config.utc true nếu muốn lấy thời gian UTC
1265
+ * @param config.formatOfDate định dạng thời gian đang được truyền vào
1266
+ */
1267
+ const getDayjs = (config) => {
1268
+ if (!config) {
1269
+ return dayjs().tz();
1094
1270
  }
1095
- const dataUnique = keyBy(data, key);
1096
- return Object.keys(dataUnique).map(key => dataUnique[key]);
1097
- };
1098
- const generateInterface = (obj, interfaceName) => {
1099
- const generateType = (value) => {
1100
- if (value === null) {
1101
- return 'null';
1102
- }
1103
- const type = typeof value;
1104
- if (type === 'string') {
1105
- return 'string';
1106
- }
1107
- if (type === 'number') {
1108
- return 'number';
1109
- }
1110
- if (type === 'boolean') {
1111
- return 'boolean';
1112
- }
1113
- if (type === 'undefined') {
1114
- return 'any';
1115
- }
1116
- if (value instanceof Date) {
1117
- return 'Date';
1118
- }
1119
- if (value instanceof RegExp) {
1120
- return 'RegExp';
1121
- }
1122
- if (Array.isArray(value)) {
1123
- if (value.length === 0) {
1124
- return 'Array<any>';
1125
- }
1126
- return `Array<${generateType(value[0])}>`;
1127
- }
1128
- if (type === 'object') {
1129
- let interfaceStr = '{\n';
1130
- for (const key in value) {
1131
- if (Object.prototype.hasOwnProperty.call(value, key)) {
1132
- const valueType = generateType(value[key]);
1133
- interfaceStr += ` ${key}: ${valueType};\n`;
1134
- }
1135
- }
1136
- interfaceStr += '}';
1137
- return interfaceStr;
1138
- }
1139
- return 'any';
1140
- };
1141
- const interfaceStr = `interface ${interfaceName} ${generateType(obj)}`;
1142
- return interfaceStr;
1143
- };
1144
-
1145
- ;
1146
- const step = 20;
1147
- const percent = 0.05;
1148
- const colorStepContrastFromOrigin = (color, stepNumber) => {
1149
- return colorContrastFromOrigin(color).find(item => item.step === stepNumber);
1150
- };
1151
- const colorContrastFromOrigin = (color) => {
1152
- const parsedColorsArray = parseColorValues(color);
1153
- const colors = [];
1154
- let calculatedShades = [];
1155
- let calculatedTints = [];
1156
- if (parsedColorsArray !== null) {
1157
- for (let i = 0; i < parsedColorsArray.length; i++) {
1158
- calculatedShades = calculateShades(parsedColorsArray[i]);
1159
- calculatedTints = calculateTints(parsedColorsArray[i]);
1271
+ config.date = !config.date && config.returnDayjsIfConfigDateNotExist ? dayjs().tz() : config.date;
1272
+ if (typeof config.date === 'number') {
1273
+ config.date = dayjs.unix(config.date);
1274
+ }
1275
+ if (!config.date) {
1276
+ return undefined;
1277
+ }
1278
+ let { date, utc, formatOfDate } = config;
1279
+ while (isSignal(date)) {
1280
+ date = date();
1281
+ }
1282
+ while (isSignal(utc)) {
1283
+ utc = utc();
1284
+ }
1285
+ while (isSignal(formatOfDate)) {
1286
+ formatOfDate = formatOfDate();
1287
+ }
1288
+ if (utc) {
1289
+ if (formatOfDate) {
1290
+ return dayjs(date, formatOfDate).utc();
1160
1291
  }
1161
- for (let i = 0; i <= step; i++) {
1162
- colors.push({ step: i * 5, label: `${i * 5}%`, dark: `#${calculatedShades[i]}`, light: `#${calculatedTints[i]}` });
1292
+ const dateInputIsUTC = (dayjs.isDayjs(date) && date.isUTC()) || (typeof date === 'string' && date.includes('Z'));
1293
+ if (dateInputIsUTC) {
1294
+ return dayjs(date);
1163
1295
  }
1296
+ return dayjs(date).utc();
1164
1297
  }
1165
- return colors;
1166
- };
1167
- const parseColorValues = (colorValues) => {
1168
- let colorValuesArray = colorValues.match(/\b[0-9A-Fa-f]{3}\b|[0-9A-Fa-f]{6}\b/g);
1169
- if (colorValuesArray) {
1170
- colorValuesArray = colorValuesArray.map((item) => {
1171
- if (item.length === 3) {
1172
- let newItem = item.toString().split('');
1173
- newItem = newItem.reduce((acc, it) => {
1174
- return acc + it + it;
1175
- }, '');
1176
- return newItem;
1177
- }
1178
- return item;
1179
- });
1298
+ if (typeof date === 'string' && !date.includes('Z') && !date.includes('+')) {
1299
+ return (formatOfDate ? dayjs.tz(date, formatOfDate, config.localeZone || timeZoneSetup) : dayjs.tz(date, config.localeZone || timeZoneSetup));
1180
1300
  }
1181
- return colorValuesArray;
1182
- };
1183
- const calculateShades = (colorValue) => {
1184
- return calculate(colorValue, rgbShade).concat("000000");
1185
- };
1186
- const calculateTints = (colorValue) => {
1187
- return calculate(colorValue, rgbTint).concat("ffffff");
1301
+ return (formatOfDate ? dayjs(date, formatOfDate) : dayjs(date)).tz();
1188
1302
  };
1189
- const calculate = (colorValue, shadeOrTint) => {
1190
- const color = hexToRGB(colorValue);
1191
- const shadeValues = [];
1192
- for (let i = 0; i < step; i++) {
1193
- shadeValues[i] = rgbToHex(shadeOrTint(color, i));
1303
+ /**
1304
+ * @description Kiểm tra xem hai ngày có khác nhau không (khác ngày trong tuần)
1305
+ * @param date1 ngày thứ nhất cần so sánh
1306
+ * @param date2 ngày thứ hai cần so sánh
1307
+ * @returns true nếu hai ngày khác nhau, false nếu giống nhau hoặc không thể so sánh
1308
+ */
1309
+ const isDifferenceDay = (date1, date2) => {
1310
+ if (isDifferenceMonth(date1, date2)) {
1311
+ return true;
1194
1312
  }
1195
- return shadeValues;
1313
+ const day1 = getDayjs({ date: date1 })?.day();
1314
+ const day2 = getDayjs({ date: date2 })?.day();
1315
+ return day1 !== day2;
1196
1316
  };
1197
- const rgbShade = (rgb, i) => { return { red: rgb.red * (1 - percent * i), green: rgb.green * (1 - percent * i), blue: rgb.blue * (1 - percent * i) }; };
1198
- const rgbTint = (rgb, i) => { return { red: rgb.red + (255 - rgb.red) * i * percent, green: rgb.green + (255 - rgb.green) * i * percent, blue: rgb.blue + (255 - rgb.blue) * i * percent }; };
1199
- const rgbToHex = (rgb) => { return intToHex(rgb.red) + intToHex(rgb.green) + intToHex(rgb.blue); };
1200
- const hexToRGB = (colorValue) => { return { red: parseInt(colorValue.substr(0, 2), 16), green: parseInt(colorValue.substr(2, 2), 16), blue: parseInt(colorValue.substr(4, 2), 16) }; };
1201
- const intToHex = (rgbint) => { return pad(Math.min(Math.max(Math.round(rgbint), 0), 255).toString(16), 2); };
1202
- const pad = (number, length) => {
1203
- let str = '' + number;
1204
- while (str.length < length) {
1205
- str = '0' + str;
1317
+ /**
1318
+ * @description Kiểm tra xem hai ngày khác tháng không
1319
+ * @param date1 ngày thứ nhất cần so sánh
1320
+ * @param date2 ngày thứ hai cần so sánh
1321
+ * @returns true nếu hai ngày khác tháng, false nếu cùng tháng hoặc không thể so sánh
1322
+ */
1323
+ const isDifferenceMonth = (date1, date2) => {
1324
+ if (isDifferenceYear(date1, date2)) {
1325
+ return true;
1206
1326
  }
1207
- return str;
1327
+ const month1 = getDayjs({ date: date1 })?.month();
1328
+ const month2 = getDayjs({ date: date2 })?.month();
1329
+ return month1 !== month2;
1208
1330
  };
1209
- const listColorDefine = ['#E62222', '#B81B1B', '#EB4E4E', '#F97316', '#C75C12', '#FA8F45', '#FFB700', '#CC9200', '#FFC533', '#84CC16', '#6AA312', '#9dd645', '#00BC62', '#00A757', '#33DA8A', '#06B6D4', '#1B59C4', '#4E8CF7', '#0EA5E9',
1210
- '#1B59C4', '#4E8CF7', '#226FF5', '#1B59C4', '#4E8CF7', '#6366F1', '#4F52C1', '#8285F4', '#5B04B3', '#49038F', '#7C36C2', '#D946EF', '#AE38BF', '#E16BF2', '#EC4899', '#BD3A7A', '#F06DAD', '#F43F5E', '#C3324B', '#F6657E', '#757380', '#5E5C66', '#918F99',
1211
- '#202020', '#1A1A1A', '#4D4D4D'
1212
- ];
1213
- const getColorById = (str) => {
1214
- let hashString = 0;
1215
- if (!str) {
1216
- return '';
1217
- }
1218
- for (let i = 0; i < str.length; i++) {
1219
- const char = str.charCodeAt(i);
1220
- hashString = ((hashString << 5) - hashString) + char;
1221
- hashString = hashString & hashString;
1222
- }
1223
- return listColorDefine[Math.abs(hashString) % listColorDefine.length];
1331
+ /**
1332
+ * @description Kiểm tra xem hai ngày khác năm không
1333
+ * @param date1 ngày thứ nhất cần so sánh
1334
+ * @param date2 ngày thứ hai cần so sánh
1335
+ * @returns true nếu hai ngày khác năm, false nếu cùng năm hoặc không thể so sánh
1336
+ */
1337
+ const isDifferenceYear = (date1, date2) => {
1338
+ const year1 = getDayjs({ date: date1 })?.year();
1339
+ const year2 = getDayjs({ date: date2 })?.year();
1340
+ return year1 !== year2;
1224
1341
  };
1225
-
1226
- let key = '12~@#loqwsxacva(3rdhaq12';
1227
1342
  /**
1228
- * @description Thiết lập key hóa
1229
- * @param value key hóa, độ dài bằng 24 hoặc 32 ký tự.
1343
+ * @description Lấy ra chuỗi thời gian hiển thị theo định dạng và ngôn ngữ
1344
+ * @param date thời gian cần định dạng
1345
+ * @param format định dạng thời gian muốn lấy ra
1346
+ * @param lang lấy theo ngôn ngữ
1230
1347
  */
1231
- const setKeyCrypto3rd = (value) => {
1232
- if (value.length !== 24 && value.length !== 32) {
1233
- throw Error(`key.length = ${value.length}; Key phải là 1 chuỗi dài 24 hoặc 32 ký tự`);
1348
+ const formatDate = (date, formatOutput = 'YYYY/MM/DD HH:mm', lang, formatInput) => {
1349
+ if (!date) {
1350
+ return '';
1234
1351
  }
1235
- key = value;
1236
- };
1237
- const keyStore = () => {
1238
- return key;
1239
- };
1240
- const encrypt3rd = (plainData) => {
1241
- if (!keyStore()) {
1242
- throw Error("lỗi chưa setup key mã hóa");
1352
+ lang = lang || UtilsCache.getLang();
1353
+ if (functionFormatDate) {
1354
+ return functionFormatDate(date, formatOutput, lang);
1243
1355
  }
1244
- const key = CryptoES.enc.Hex.parse(keyStore());
1245
- const iv = CryptoES.enc.Hex.parse(keyStore());
1246
- const mode = CryptoES.mode.CBC;
1247
- const padding = CryptoES.pad.Pkcs7;
1248
- const options = { iv: iv, mode: mode, padding: padding };
1249
- return CryptoES.AES.encrypt(plainData, key, options).toString();
1250
- };
1251
- const decrypt3rd = (encryptedData) => {
1252
- if (!keyStore()) {
1253
- throw Error("lỗi chưa setup key hóa");
1356
+ date = getDayjs({ date, formatOfDate: formatInput }).locale(lang);
1357
+ if (lang === 'vi') {
1358
+ dayjs.updateLocale('vi', {
1359
+ monthsShort: 'Thg 1_Thg 2_Thg 3_Thg 4_Thg 5_Thg 6_Thg 7_Thg 8_Thg 9_Thg 10_Thg 11_Thg 12'.split('_')
1360
+ });
1361
+ }
1362
+ return date.format(getFormatData(getTypeByFormat(formatOutput), lang)) || '';
1363
+ };
1364
+ const getTypeByFormat = (format) => {
1365
+ if (format === 'dm' || format === 'dmy' || format === 'dmy hm' || format === 'my') {
1366
+ return format;
1367
+ }
1368
+ if (format === 'DD/MM/YYYY' || format === 'YYYY-MM-DD' || format === 'dd/MM/yyyy' || format === 'dd-MM-yyyy' || format === 'dd/mm/yyyy') {
1369
+ return 'dmy';
1370
+ }
1371
+ if (format === 'MM-DD' || format === 'dd/MM' || format === 'dd/mm') {
1372
+ return 'dm';
1373
+ }
1374
+ if (format === 'M/YYYY' || format === 'YYYY-MM' || format === 'MM/yyyy') {
1375
+ return 'my';
1376
+ }
1377
+ if (format === 'YYYY/MM/DD hh:mm:ss' || format === 'dmy hms' || format === 'dd/mm/yyyy hh:mm:ss') {
1378
+ return 'dmy hms';
1379
+ }
1380
+ if (format === 'hh:mm' || format === 'HH:mm') {
1381
+ return 'hh:mm';
1382
+ }
1383
+ if (format === 'hh:mm A' || format === 'HH:mm A') {
1384
+ return 'hh:mm A';
1385
+ }
1386
+ return 'dmy hm';
1387
+ };
1388
+ const getFormatData = (type, lang) => {
1389
+ switch (type) {
1390
+ case 'dm':
1391
+ if (lang === 'vi') {
1392
+ return 'D MMM';
1393
+ }
1394
+ return 'MMM D';
1395
+ case 'dmy':
1396
+ if (lang === 'vi') {
1397
+ return 'D MMM, YYYY';
1398
+ }
1399
+ return 'MMM D, YYYY';
1400
+ case 'dmy hm':
1401
+ if (lang === 'vi') {
1402
+ return 'D MMM, YYYY HH:mm';
1403
+ }
1404
+ return 'MMM D, YYYY HH:mm';
1405
+ case 'my':
1406
+ if (lang === 'vi') {
1407
+ return 'MMM, YYYY';
1408
+ }
1409
+ return 'MMM YYYY ';
1410
+ case 'dmy hms':
1411
+ if (lang === 'vi') {
1412
+ return 'D MMM, YYYY HH:mm:ss';
1413
+ }
1414
+ return 'MMM D, YYYY HH:mm:ss';
1415
+ case 'hh:mm':
1416
+ case 'HH:mm':
1417
+ return 'HH:mm';
1418
+ case 'hh:mm A':
1419
+ case 'HH:mm A':
1420
+ return 'HH:mm A';
1254
1421
  }
1255
- const key = CryptoES.enc.Hex.parse(keyStore());
1256
- const iv = CryptoES.enc.Hex.parse(keyStore());
1257
- const mode = CryptoES.mode.CBC;
1258
- const padding = CryptoES.pad.Pkcs7;
1259
- const options = { iv: iv, mode: mode, padding: padding };
1260
- return CryptoES.AES.decrypt(encryptedData, key, options).toString(CryptoES.enc.Utf8);
1261
1422
  };
1262
1423
 
1263
- class UtilsKeyCodeConstant {
1264
- static MAC_ENTER = 3;
1265
- static BACKSPACE = 8;
1266
- static TAB = 9;
1267
- static NUM_CENTER = 12;
1268
- static ENTER = 13;
1269
- static SHIFT = 16;
1270
- static CONTROL = 17;
1271
- static ALT = 18;
1272
- static PAUSE = 19;
1273
- static CAPS_LOCK = 20;
1274
- static ESCAPE = 27;
1275
- static SPACE = 32;
1276
- static PAGE_UP = 33;
1277
- static PAGE_DOWN = 34;
1278
- static END = 35;
1279
- static HOME = 36;
1280
- static LEFT_ARROW = 37;
1281
- static UP_ARROW = 38;
1282
- static RIGHT_ARROW = 39;
1283
- static DOWN_ARROW = 40;
1284
- static PLUS_SIGN = 43;
1285
- static PRINT_SCREEN = 44;
1286
- static INSERT = 45;
1287
- static DELETE = 46;
1288
- static ZERO = 48;
1289
- static ONE = 49;
1290
- static TWO = 50;
1291
- static THREE = 51;
1292
- static FOUR = 52;
1293
- static FIVE = 53;
1294
- static SIX = 54;
1295
- static SEVEN = 55;
1296
- static EIGHT = 56;
1297
- static NINE = 57;
1298
- static FF_SEMICOLON = 59; // Firefox (Gecko) fires this for semicolon instead of 186
1299
- static FF_EQUALS = 61; // Firefox (Gecko) fires this for equals instead of 187
1300
- static QUESTION_MARK = 63;
1301
- static AT_SIGN = 64;
1302
- static A = 65;
1303
- static B = 66;
1304
- static C = 67;
1305
- static D = 68;
1306
- static E = 69;
1307
- static F = 70;
1308
- static G = 71;
1309
- static H = 72;
1310
- static I = 73;
1311
- static J = 74;
1312
- static K = 75;
1313
- static L = 76;
1314
- static M = 77;
1315
- static N = 78;
1316
- static O = 79;
1317
- static P = 80;
1318
- static Q = 81;
1319
- static R = 82;
1320
- static S = 83;
1321
- static T = 84;
1322
- static U = 85;
1323
- static V = 86;
1324
- static W = 87;
1325
- static X = 88;
1326
- static Y = 89;
1327
- static Z = 90;
1328
- static META = 91; // WIN_KEY_LEFT
1329
- static MAC_WK_CMD_LEFT = 91;
1330
- static MAC_WK_CMD_RIGHT = 93;
1331
- static CONTEXT_MENU = 93;
1332
- static NUMPAD_ZERO = 96;
1333
- static NUMPAD_ONE = 97;
1334
- static NUMPAD_TWO = 98;
1335
- static NUMPAD_THREE = 99;
1336
- static NUMPAD_FOUR = 100;
1337
- static NUMPAD_FIVE = 101;
1338
- static NUMPAD_SIX = 102;
1339
- static NUMPAD_SEVEN = 103;
1340
- static NUMPAD_EIGHT = 104;
1341
- static NUMPAD_NINE = 105;
1342
- static NUMPAD_MULTIPLY = 106;
1343
- static NUMPAD_PLUS = 107;
1344
- static NUMPAD_MINUS = 109;
1345
- static NUMPAD_PERIOD = 110;
1346
- static NUMPAD_DIVIDE = 111;
1347
- static F1 = 112;
1348
- static F2 = 113;
1349
- static F3 = 114;
1350
- static F4 = 115;
1351
- static F5 = 116;
1352
- static F6 = 117;
1353
- static F7 = 118;
1354
- static F8 = 119;
1355
- static F9 = 120;
1356
- static F10 = 121;
1357
- static F11 = 122;
1358
- static F12 = 123;
1359
- static NUM_LOCK = 144;
1360
- static SCROLL_LOCK = 145;
1361
- static FIRST_MEDIA = 166;
1362
- static FF_MINUS = 173;
1363
- static MUTE = 173; // Firefox (Gecko) fires 181 for MUTE
1364
- static VOLUME_DOWN = 174; // Firefox (Gecko) fires 182 for VOLUME_DOWN
1365
- static VOLUME_UP = 175; // Firefox (Gecko) fires 183 for VOLUME_UP
1366
- static FF_MUTE = 181;
1367
- static FF_VOLUME_DOWN = 182;
1368
- static LAST_MEDIA = 183;
1369
- static FF_VOLUME_UP = 183;
1370
- static SEMICOLON = 186; // Firefox (Gecko) fires 59 for SEMICOLON
1371
- static EQUALS = 187; // Firefox (Gecko) fires 61 for EQUALS
1372
- static COMMA = 188;
1373
- static DASH = 189; // Firefox (Gecko) fires 173 for DASH/MINUS
1374
- static PERIOD = 190;
1375
- static SLASH = 191;
1376
- static APOSTROPHE = 192;
1377
- static TILDE = 192;
1378
- static OPEN_SQUARE_BRACKET = 219;
1379
- static BACKSLASH = 220;
1380
- static CLOSE_SQUARE_BRACKET = 221;
1381
- static SINGLE_QUOTE = 222;
1382
- static MAC_META = 224;
1383
- }
1384
-
1385
- class UtilsLanguageConstants {
1386
- static VI = "vi"; // Tiếng Việt
1387
- static EN = "en"; // Tiếng Anh
1388
- static FR = "fr"; // Tiếng Pháp
1389
- static DE = "de"; // Tiếng Đức
1390
- static ES = "es"; // Tiếng Tây Ban Nha
1391
- static ZH = "zh"; // Tiếng Trung (Giản thể)
1392
- static ZH_TW = "zh-TW"; // Tiếng Trung (Phồn thể)
1393
- static JA = "ja"; // Tiếng Nhật
1394
- static KO = "ko"; // Tiếng Hàn
1395
- static RU = "ru"; // Tiếng Nga
1396
- static IT = "it"; // Tiếng Ý
1397
- static PT = "pt"; // Tiếng Bồ Đào Nha
1398
- static TH = "th"; // Tiếng Thái
1399
- static ID = "id"; // Tiếng Indonesia
1400
- static MS = "ms"; // Tiếng Malaysia
1401
- static AR = "ar"; // Tiếng Ả Rập
1402
- static HI = "hi"; // Tiếng Hindi
1403
- static BN = "bn"; // Tiếng Bengal
1404
- static TR = "tr"; // Tiếng Thổ Nhĩ Kỳ
1405
- static NL = "nl"; // Tiếng Hà Lan
1406
- static KM = "km"; // Tiếng Khmer (Campuchia)
1407
- static LO = "lo"; // Tiếng Lào
1408
- static MY = "my"; // Tiếng Miến Điện (Myanmar)
1409
- static TL = "tl"; // Tiếng Tagalog (Philippines)
1410
- static CEB = "ceb"; // Tiếng Cebuano (Philippines)
1411
- static JV = "jv"; // Tiếng Java (Indonesia)
1412
- static SU = "su"; // Tiếng Sundanese (Indonesia)
1413
- // Ngôn ngữ mặc định
1414
- static defaultLang = UtilsLanguageConstants.VI;
1415
- // Danh sách các ngôn ngữ được hỗ trợ
1416
- static supportedLanguages = new Set([
1417
- UtilsLanguageConstants.VI,
1418
- UtilsLanguageConstants.EN,
1419
- UtilsLanguageConstants.FR,
1420
- UtilsLanguageConstants.DE,
1421
- UtilsLanguageConstants.ES,
1422
- UtilsLanguageConstants.ZH,
1423
- UtilsLanguageConstants.ZH_TW,
1424
- UtilsLanguageConstants.JA,
1425
- UtilsLanguageConstants.KO,
1426
- UtilsLanguageConstants.RU,
1427
- UtilsLanguageConstants.IT,
1428
- UtilsLanguageConstants.PT,
1429
- UtilsLanguageConstants.TH,
1430
- UtilsLanguageConstants.ID,
1431
- UtilsLanguageConstants.MS,
1432
- UtilsLanguageConstants.AR,
1433
- UtilsLanguageConstants.HI,
1434
- UtilsLanguageConstants.BN,
1435
- UtilsLanguageConstants.TR,
1436
- UtilsLanguageConstants.NL,
1437
- UtilsLanguageConstants.KM,
1438
- UtilsLanguageConstants.LO,
1439
- UtilsLanguageConstants.MY,
1440
- UtilsLanguageConstants.TL,
1441
- UtilsLanguageConstants.CEB,
1442
- UtilsLanguageConstants.JV,
1443
- UtilsLanguageConstants.SU,
1444
- ]);
1445
- /**
1446
- * Kiểm tra xem ngôn ngữ đầu vào có được hỗ trợ không
1447
- * @param lang Mã ngôn ngữ cần kiểm tra
1448
- * @returns True nếu được hỗ trợ, False nếu không
1449
- */
1450
- static isSupported(lang) {
1451
- return UtilsLanguageConstants.supportedLanguages.has(lang);
1424
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1425
+ /**Các hàm tương tự thư viện lodash */
1426
+ /**
1427
+ * Kiểm tra xem một giá trị có phải là null hoặc undefined hay không
1428
+ * @param value Giá trị cần kiểm tra
1429
+ * @returns true nếu giá trị là null hoặc undefined, false nếu không
1430
+ * @example
1431
+ * isNil(null); // true
1432
+ * isNil(undefined); // true
1433
+ * isNil(0); // false
1434
+ * isNil('hello'); // false
1435
+ */
1436
+ const isNil = (value) => {
1437
+ return value === null || value === undefined;
1438
+ };
1439
+ /**
1440
+ * Kiểm tra xem một giá trị có phải là rỗng hay không
1441
+ * @param value Giá trị cần kiểm tra
1442
+ * @returns true nếu giá trị là null, rỗng hoặc undefined, false nếu không
1443
+ * @example
1444
+ * isEmpty(null); // true
1445
+ * isEmpty(''); // true
1446
+ * isEmpty(undefined); // true
1447
+ * isEmpty({}); // true
1448
+ * isEmpty([]); // true
1449
+ * isEmpty([1, 2, 3]); // false
1450
+ * isEmpty({ a: 1 }); // false
1451
+ */
1452
+ const isEmpty = (value) => {
1453
+ while (isSignal(value)) {
1454
+ value = value();
1455
+ }
1456
+ return value === null || value === '' || value === undefined || (typeof value === 'object' && (JSON.stringify(value) === '{}' || JSON.stringify(value) === '[]'));
1457
+ };
1458
+ /**
1459
+ * Loại bỏ các thuộc tính của đối tượng dựa trên một hàm điều kiện
1460
+ * @param objData Đối tượng cần xử
1461
+ * @param predicate Hàm điều kiện để kiểm tra giá trị của thuộc tính. Nếu hàm trả về true thì thuộc tính đó sẽ bị loại bỏ
1462
+ * @returns Đối tượng mới sau khi đã loại bỏ các thuộc tính thỏa mãn điều kiện
1463
+ * @example
1464
+ * const obj = { a: 1, b: null, c: 3, d: undefined };
1465
+ * omitBy(obj, isNil); // { a: 1, c: 3 }
1466
+ */
1467
+ const omitBy = (objData, predicate) => {
1468
+ if (!objData || typeof objData !== 'object' || Array.isArray(objData)) {
1469
+ return objData;
1470
+ }
1471
+ const newObj = {};
1472
+ Object.keys(objData).forEach(key => {
1473
+ const valueOfKey = get(objData, key);
1474
+ if (!predicate(valueOfKey)) {
1475
+ set(newObj, key, valueOfKey);
1476
+ }
1477
+ });
1478
+ return newObj;
1479
+ };
1480
+ /**
1481
+ * Lấy giá trị từ đối tượng theo đường dẫn chỉ định
1482
+ *
1483
+ * Hàm này giúp bạn truy cập vào các thuộc tính sâu bên trong một đối tượng một cách an toàn,
1484
+ * tránh lỗi khi thuộc tính không tồn tại.
1485
+ *
1486
+ * @param obj Đối tượng nguồn cần lấy giá trị
1487
+ * @param path Đường dẫn đến thuộc tính cần lấy. Có thể là:
1488
+ * - Chuỗi: 'user.profile.name' hoặc 'items[0].title'
1489
+ * - Mảng: ['user', 'profile', 'name'] hoặc ['items', '0', 'title']
1490
+ * - Chuỗi rỗng '': trả về chính đối tượng gốc
1491
+ * @param defaultValue Giá trị mặc định trả về khi không tìm thấy thuộc tính (mặc định: undefined)
1492
+ * @param keepLastValueIfSignal Có giữ nguyên signal cuối cùng hay không (mặc định: false - sẽ gọi signal())
1493
+ * @returns Giá trị tìm được hoặc giá trị mặc định
1494
+ *
1495
+ * @example
1496
+ * // Ví dụ cơ bản
1497
+ * const user = { name: 'John', age: 30 };
1498
+ * get(user, 'name'); // 'John'
1499
+ * get(user, 'email'); // undefined
1500
+ * get(user, 'email', 'no-email'); // 'no-email'
1501
+ *
1502
+ * @example
1503
+ * // Truyền path rỗng - trả về chính đối tượng gốc
1504
+ * const data = { name: 'Alice', age: 25 };
1505
+ * get(data, ''); // { name: 'Alice', age: 25 } (chính đối tượng data)
1506
+ * get(data, '', 'default'); // { name: 'Alice', age: 25 } (bỏ qua defaultValue)
1507
+ *
1508
+ * @example
1509
+ * // Truy cập thuộc tính sâu
1510
+ * const data = {
1511
+ * user: {
1512
+ * profile: {
1513
+ * name: 'Alice',
1514
+ * settings: { theme: 'dark' }
1515
+ * }
1516
+ * }
1517
+ * };
1518
+ * get(data, 'user.profile.name'); // 'Alice'
1519
+ * get(data, 'user.profile.settings.theme'); // 'dark'
1520
+ * get(data, 'user.profile.avatar', 'default.jpg'); // 'default.jpg'
1521
+ *
1522
+ * @example
1523
+ * // Truy cập mảng
1524
+ * const items = [
1525
+ * { name: 'Item 1', price: 100 },
1526
+ * { name: 'Item 2', price: 200 }
1527
+ * ];
1528
+ * get(items, '[0].name'); // 'Item 1'
1529
+ * get(items, '[1].name'); // 'Item 2'
1530
+ * get(items, '[2].name', 'Not found'); // 'Not found'
1531
+ *
1532
+ * @example
1533
+ * // Sử dụng với mảng path
1534
+ * const nested = { a: { b: { c: 'deep value' } } };
1535
+ * get(nested, ['a', 'b', 'c']); // 'deep value'
1536
+ * get(nested, ['a', 'b', 'd'], 'default'); // 'default'
1537
+ *
1538
+ * @example
1539
+ * // Trường hợp đặc biệt
1540
+ * get(null, 'any.path'); // undefined
1541
+ * get(undefined, 'any.path', 'fallback'); // 'fallback'
1542
+ */
1543
+ const get = (obj, path, defaultValue = undefined, keepLastValueIfSignal) => {
1544
+ if (isNil(obj)) {
1545
+ return defaultValue;
1452
1546
  }
1453
- }
1454
-
1455
- /* eslint-disable @typescript-eslint/no-explicit-any */
1456
- const getKeyCacheByArrayObject = (keyCache, argumentsValue) => {
1457
- if (!keyCache || !argumentsValue) {
1458
- return '';
1547
+ while (isSignal(obj)) {
1548
+ obj = obj();
1459
1549
  }
1460
- let keyBuild = `${JSON.stringify(argumentsValue.slice(1))}-key-cache-plus`;
1461
- argumentsValue.forEach((item) => {
1462
- if (typeof item === 'object') {
1463
- if (Array.isArray(item)) {
1464
- keyBuild = `${keyBuild}${JSON.stringify(item)}`;
1465
- return;
1550
+ if (path === '') {
1551
+ return obj;
1552
+ }
1553
+ if (obj instanceof HttpParams) {
1554
+ return obj.get(`${path}`);
1555
+ }
1556
+ if (obj instanceof DOMRect) {
1557
+ return obj[path];
1558
+ }
1559
+ const paths = Array.isArray(path) ? path : path.replace(/\[(\d+)]/g, '.$1').split('.').filter(key => key);
1560
+ for (const index in paths) {
1561
+ const key = paths[index];
1562
+ if (obj instanceof CSSStyleDeclaration) {
1563
+ obj = obj[key];
1564
+ continue;
1565
+ }
1566
+ if (obj instanceof HTMLElement) {
1567
+ obj = obj[key];
1568
+ continue;
1569
+ }
1570
+ if (isNil(obj) || !Object.prototype.hasOwnProperty.call(obj, key)) {
1571
+ return defaultValue;
1572
+ }
1573
+ const val = isSignal(obj[key]) && !keepLastValueIfSignal ? obj[key]() : obj[key];
1574
+ obj = val;
1575
+ }
1576
+ return obj;
1577
+ };
1578
+ /**
1579
+ * Thiết lập giá trị cho một thuộc tính trong đối tượng theo đường dẫn chỉ định
1580
+ * @param obj Đối tượng cần thiết lập giá trị
1581
+ * @param path Đường dẫn đến thuộc tính, có thể là chuỗi (vd: 'a.b.c') hoặc mảng (vd: ['a', 'b', 'c'])
1582
+ * @param value Giá trị cần thiết lập
1583
+ * @returns Đối tượng sau khi đã thiết lập giá trị
1584
+ * @throws Error nếu tham số đầu tiên không phải là đối tượng
1585
+ * @example
1586
+ * const obj = { a: { b: 1 } };
1587
+ * set(obj, 'a.b', 2); // { a: { b: 2 } }
1588
+ */
1589
+ const set = (obj, path, value) => {
1590
+ if (!obj || (typeof obj !== "object" && !isSignal(obj)) || (isSignal(obj) && typeof obj() !== "object")) {
1591
+ throw new Error("The first argument must be an object");
1592
+ }
1593
+ if (obj instanceof HttpParams) {
1594
+ return obj.set(`${path}`, value);
1595
+ }
1596
+ const pathArray = Array.isArray(path) ? path : path.replace(/\[(\d+)]/g, '.[$1]').split('.').filter(key => key);
1597
+ let currentObjectByKey = isSignal(obj) ? obj() : obj;
1598
+ let preObjectByKey = obj;
1599
+ pathArray.forEach((key, index) => {
1600
+ if (index < pathArray.length - 1) {
1601
+ if (!(key in currentObjectByKey) || (typeof currentObjectByKey[key] !== "object" && !isSignal(currentObjectByKey[key]))) {
1602
+ const nextKey = pathArray[index + 1];
1603
+ key = key.replace(/\[(\d+)]/g, '$1');
1604
+ currentObjectByKey[key] = /\[(\d+)]/g.test(nextKey) ? [] : {};
1466
1605
  }
1467
- const keys = (item instanceof HttpParams ? item.keys() : Object.keys(item)).sort(((str1, str2) => str1.localeCompare(str2)));
1468
- keys.forEach(key => {
1469
- keyBuild = `${keyBuild}${JSON.stringify(get(item, key))}`;
1470
- });
1606
+ currentObjectByKey = key ? currentObjectByKey[key] : currentObjectByKey;
1607
+ preObjectByKey = currentObjectByKey;
1608
+ currentObjectByKey = isSignal(currentObjectByKey) ? currentObjectByKey() : currentObjectByKey;
1471
1609
  return;
1472
1610
  }
1473
- keyBuild = `${keyBuild}${item}`;
1474
- });
1475
- const keyCachePlus = md5(keyBuild);
1476
- const keyCacheMD5 = md5(keyCache);
1477
- return `${keyCacheMD5}-${md5(`${keyCacheMD5}-${keyCachePlus}`)}`;
1478
- };
1479
-
1480
- let functionCheck = () => {
1481
- return window.parent !== window.top;
1482
- };
1483
- const updateFunctionCheckEmbedFrame = (functionCustom) => {
1484
- functionCheck = functionCustom;
1485
- };
1486
- const isEmbedFrame = () => {
1487
- return functionCheck();
1488
- };
1489
-
1490
- const ERROR_MESSAGE_EMPTY_VALID = 'i18n_valid_empty_message';
1491
- const ERROR_MESSAGE_PATTEN_VALID = 'i18n_valid_pattern_message';
1492
- const ERROR_MESSAGE_MIN_VALID = 'i18n_message_error_input_min_value';
1493
- const ERROR_MESSAGE_MAX_VALID = 'i18n_message_error_input_max_value';
1494
- const ERROR_MESSAGE_MIN_LENGTH = 'i18n_message_error_input_min_length';
1495
- const ERROR_MESSAGE_MAX_LENGTH = 'i18n_message_error_input_max_length';
1496
- const CHARACTER_DATA_EMPTY = '__';
1497
- const DEFAULT_START_PAGE_0 = 'defaultStartPage0';
1498
- const COMMUNICATE_MICRO_PREFIX_TYPE = '3RD_INTEGRATE_MICRO_SITE_';
1499
- const COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE = 'MICRO_SITES_ALL_MESSAGE';
1500
-
1501
- /* eslint-disable @typescript-eslint/no-explicit-any */
1502
- class UtilsCommunicateMicroKeyGlobal {
1503
- static KEY_MESSAGE_MODAL = 'LIBS_UI_MODEL_EVENT';
1504
- }
1505
- class UtilsCommunicateMicro {
1506
- static initdEvent;
1507
- static subs = new Map();
1508
- static allMessageSub = new Subject();
1509
- static initEvent(currentWindow, onDestroy) {
1510
- if (this.initdEvent) {
1611
+ if (typeof currentObjectByKey !== "object") {
1511
1612
  return;
1512
1613
  }
1513
- this.initdEvent = true;
1514
- if (!this.subs.get(COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE)) {
1515
- this.subs.set(COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE, UtilsCommunicateMicro.allMessageSub);
1614
+ // Gán giá trị ở cuối đường dẫn
1615
+ key = key.replace(/\[(\d+)]/g, '$1');
1616
+ const valueOfKey = currentObjectByKey[key];
1617
+ const valueOfKeyIsSignal = isSignal(valueOfKey);
1618
+ if (valueOfKeyIsSignal && (typeof valueOfKey() !== 'object' || valueOfKey() === null)) {
1619
+ valueOfKey.set(isSignal(value) ? value() : value);
1620
+ return;
1516
1621
  }
1517
- fromEvent(currentWindow, 'message').pipe(takeUntil(onDestroy)).subscribe(e => {
1518
- const event = { data: { ...e.data } };
1519
- const data = event.data;
1520
- const type = data.type;
1521
- if (!type) {
1522
- return this.allMessageSub.next(event);
1523
- }
1524
- const sub = UtilsCommunicateMicro.GetMessage(type);
1525
- if (!type || !data.response) {
1526
- return;
1527
- }
1528
- try {
1529
- if (type.includes(COMMUNICATE_MICRO_PREFIX_TYPE)) {
1530
- data.response = JSON.parse(decrypt3rd(data.response));
1531
- sub.next(event);
1532
- this.allMessageSub.next(event);
1533
- return;
1622
+ if (isSignal(preObjectByKey)) {
1623
+ preObjectByKey.update((data) => {
1624
+ const dataOfKey = data[key];
1625
+ if (isNil(dataOfKey) || !valueOfKeyIsSignal) {
1626
+ data[key] = value;
1534
1627
  }
1535
- data.response = JSON.parse(decrypt(data.response));
1536
- sub.next(event);
1537
- this.allMessageSub.next(event);
1538
- }
1539
- catch (error) {
1540
- console.log(error);
1541
- sub.next(event);
1542
- this.allMessageSub.next(event);
1543
- }
1544
- });
1545
- UtilsCommunicateMicro.GetMessage(UtilsCache.typeKeyClearLocalStorage).pipe(filter(e => e.data.response !== UtilsCache.idService), takeUntil(onDestroy)).subscribe(() => {
1546
- console.log('clear all cache by event');
1547
- UtilsCache.ClearAll();
1548
- });
1549
- }
1550
- static PostMessageToParent(data) {
1551
- data = this.convertData(data);
1552
- try {
1553
- if (isEmbedFrame()) {
1554
- window?.parent?.postMessage(data, "*");
1555
- return;
1556
- }
1557
- window?.top?.postMessage(data, "*");
1628
+ if (valueOfKeyIsSignal) {
1629
+ valueOfKey.set(isSignal(value) ? value() : value);
1630
+ }
1631
+ if (Array.isArray(data)) {
1632
+ return [...data];
1633
+ }
1634
+ return { ...data };
1635
+ });
1636
+ return;
1558
1637
  }
1559
- catch (error) {
1560
- console.log(error);
1638
+ if (valueOfKeyIsSignal) {
1639
+ valueOfKey.set(isSignal(value) ? value() : value);
1640
+ return;
1561
1641
  }
1642
+ currentObjectByKey[key] = value;
1643
+ });
1644
+ return obj;
1645
+ };
1646
+ /**
1647
+ * Tạo một bản sao sâu của một đối tượng hoặc giá trị bất kỳ
1648
+ * @param data Dữ liệu cần sao chép
1649
+ * @param options Tùy chọn cấu hình
1650
+ * @param options.ignoreSignal Nếu true, sẽ không sao chép các signal mà trả về signal gốc
1651
+ * @param seen WeakMap để theo dõi các tham chiếu đã được sao chép, tránh lặp vô hạn với các tham chiếu vòng
1652
+ * @returns Bản sao sâu của dữ liệu đầu vào
1653
+ * @example
1654
+ * const obj = {
1655
+ * a: 1,
1656
+ * b: { c: 2 },
1657
+ * d: [1, 2, 3]
1658
+ * };
1659
+ * const clone = cloneDeep(obj);
1660
+ * // clone là một bản sao hoàn toàn độc lập của obj
1661
+ */
1662
+ const cloneDeep = (data, options = { ignoreSignal: false }, seen = new WeakMap()) => {
1663
+ if (data === null || (typeof data !== 'object' && !isSignal(data))) {
1664
+ return data;
1562
1665
  }
1563
- static PostMessageToChildren(data) {
1564
- data = this.convertData(data);
1565
- const iframes = document.querySelectorAll("iframe");
1566
- Array.from(iframes).forEach(iframe => {
1567
- iframe?.contentWindow?.postMessage(data, '*');
1666
+ if (seen.has(data)) {
1667
+ return seen.get(data);
1668
+ }
1669
+ if (data instanceof HttpParams) {
1670
+ return new UtilsHttpParamsRequest(undefined, data);
1671
+ }
1672
+ if (data instanceof Date) {
1673
+ return new Date(data.getTime());
1674
+ }
1675
+ if (dayjs.isDayjs(data)) {
1676
+ return getDayjs({ date: data.valueOf() });
1677
+ }
1678
+ if (data instanceof RegExp) {
1679
+ return new RegExp(data.source, data.flags);
1680
+ }
1681
+ if (data instanceof Map) {
1682
+ const mapCopy = new Map();
1683
+ seen.set(data, mapCopy);
1684
+ data.forEach((val, key) => {
1685
+ mapCopy.set(cloneDeep(key, options, seen), cloneDeep(val, options, seen));
1568
1686
  });
1687
+ return mapCopy;
1569
1688
  }
1570
- static PostMessageToOpener(data) {
1571
- if (!window.opener) {
1572
- return;
1573
- }
1574
- data = this.convertData(data);
1575
- window.opener.postMessage(data, '*');
1689
+ if (data instanceof Set) {
1690
+ const setCopy = new Set();
1691
+ seen.set(data, setCopy);
1692
+ data.forEach(val => {
1693
+ setCopy.add(cloneDeep(val, options, seen));
1694
+ });
1695
+ return setCopy;
1696
+ }
1697
+ if (Array.isArray(data)) {
1698
+ seen.set(data, data.map(item => cloneDeep(item, options, seen)));
1699
+ return seen.get(data);
1700
+ }
1701
+ if (data instanceof File || data instanceof Blob || Object.prototype.toString.call(data) === '[object File]') {
1702
+ return data;
1576
1703
  }
1577
- static convertData(data) {
1578
- if (!data || !data.type || !data.response) {
1579
- return data;
1580
- }
1581
- data = { ...data };
1582
- const type = data.type;
1583
- if (type.includes(COMMUNICATE_MICRO_PREFIX_TYPE)) {
1584
- try {
1585
- JSON.parse(decrypt3rd(data.response));
1586
- return data;
1587
- }
1588
- catch (_) {
1589
- data.response = encrypt3rd(JSON.stringify(data.response));
1590
- return data;
1591
- }
1592
- }
1593
- try {
1594
- JSON.parse(decrypt(data.response));
1595
- return data;
1596
- }
1597
- catch (_) {
1598
- data.response = encrypt(JSON.stringify(data.response));
1704
+ if (data instanceof TemplateRef || data instanceof ElementRef || data instanceof Element || data instanceof Promise || data instanceof Observable) {
1705
+ return data;
1706
+ }
1707
+ if (isSignal(data)) {
1708
+ if (options?.ignoreSignal) {
1599
1709
  return data;
1600
1710
  }
1711
+ seen.set(data, signal(cloneDeep(data(), options, seen)));
1712
+ return seen.get(data);
1601
1713
  }
1602
- static GetMessage(messageType) {
1603
- if (!this.initdEvent) {
1604
- throw Error("chưa khơi tạo hàm lắng nghe sự kiện, gọi UtilsCommunicateMicro.initEvent(window) tại root component");
1605
- }
1606
- if (typeof messageType === 'string') {
1607
- let sub = this.subs.get(messageType);
1608
- if (!sub) {
1609
- sub = new Subject();
1610
- this.subs.set(messageType, sub);
1611
- return sub;
1612
- }
1613
- return sub;
1714
+ const result = {};
1715
+ seen.set(data, result);
1716
+ for (const key in data) {
1717
+ const value = data[key];
1718
+ if (value instanceof HttpParams) {
1719
+ result[key] = new UtilsHttpParamsRequest(undefined, value);
1720
+ continue;
1614
1721
  }
1615
- if (!Array.isArray(messageType) || !messageType.length) {
1616
- throw new Error('message type empty');
1722
+ if (dayjs.isDayjs(value)) {
1723
+ result[key] = getDayjs({ date: value.valueOf() });
1724
+ continue;
1617
1725
  }
1618
- if (messageType.length === 1) {
1619
- return this.GetMessage(messageType[0]);
1726
+ if (value instanceof TemplateRef || value instanceof ElementRef || value instanceof Element || value instanceof Promise || value instanceof Observable) {
1727
+ result[key] = value;
1728
+ continue;
1620
1729
  }
1621
- const types = messageType.sort().join(';');
1622
- let sub = this.subs.get(types);
1623
- if (sub) {
1624
- return sub;
1730
+ if (Object.prototype.hasOwnProperty.call(data, key)) {
1731
+ result[key] = cloneDeep(value, options, seen);
1625
1732
  }
1626
- sub = new Subject();
1627
- this.subs.set(types, sub);
1628
- this.initSubject(sub, messageType);
1629
- return sub;
1630
1733
  }
1631
- static initSubject(subRoot, messageType) {
1632
- messageType.forEach(key => {
1633
- this.GetMessage(key).subscribe(e => {
1634
- subRoot.next(e);
1635
- });
1636
- });
1734
+ return result;
1735
+ };
1736
+ /**
1737
+ * Chuyển đổi một mảng các đối tượng thành một đối tượng với khóa được chỉ định
1738
+ * @param data Mảng các đối tượng cần chuyển đổi
1739
+ * @param key Tên thuộc tính được sử dụng làm khóa trong đối tượng kết quả
1740
+ * @returns Đối tượng với các giá trị từ mảng được đánh key theo thuộc tính đã chỉ định
1741
+ * @example
1742
+ * const data = [
1743
+ * { id: 1, name: 'John' },
1744
+ * { id: 2, name: 'Jane' }
1745
+ * ];
1746
+ * keyBy(data, 'id');
1747
+ * // Kết quả: {
1748
+ * // '1': { id: 1, name: 'John' },
1749
+ * // '2': { id: 2, name: 'Jane' }
1750
+ * // }
1751
+ */
1752
+ const keyBy = (data, key) => {
1753
+ if (!data || !data.length || !key) {
1754
+ return {};
1637
1755
  }
1638
- }
1639
-
1640
- /* eslint-disable no-async-promise-executor */
1641
- /* eslint-disable @typescript-eslint/no-explicit-any */
1642
- class UtilsCache {
1643
- static CACHE_EXPIRE_TIME_DEFAULT = 5 * 60;
1644
- static CACHE_EXPIRE_NONE = -1;
1645
- static idService = uuid();
1646
- static typeKeyClearLocalStorage = 'LIBS_UI_CLEAR_LOCAL_STORAGE_EVENT';
1647
- static languageKeyCache = 'SR3xQKxHgffiCevPQRralg';
1648
- static listKeyKeepWhenClearALll = Array();
1649
- static initdEvent;
1650
- static storage;
1651
- static dbName = 'libs-ui-cache';
1652
- static itemIndexByKey = 'key';
1653
- static dbVersion = 1;
1654
- static db = null;
1655
- static init(config) {
1656
- if (this.initdEvent) {
1657
- return;
1658
- }
1659
- this.initdEvent = true;
1660
- if (config.indexedDBName) {
1661
- this.dbName = config.indexedDBName;
1756
+ return data.reduce((dir, nextItem) => {
1757
+ const valueOfKey = get(nextItem, key);
1758
+ if (typeof valueOfKey !== 'string' && typeof valueOfKey !== 'number' && typeof valueOfKey !== 'boolean') {
1759
+ return dir;
1662
1760
  }
1663
- if (config.typeKeyClearLocalStorage) {
1664
- this.typeKeyClearLocalStorage = config.typeKeyClearLocalStorage;
1761
+ if (!Object.keys(dir).includes(`${valueOfKey}`)) {
1762
+ dir[`${valueOfKey}`] = nextItem;
1665
1763
  }
1666
- if (config.listKeyKeepWhenClearAll) {
1667
- this.listKeyKeepWhenClearALll = config.listKeyKeepWhenClearAll;
1764
+ return dir;
1765
+ }, {});
1766
+ };
1767
+ /**
1768
+ * Nhóm các đối tượng trong một mảng thành các nhóm dựa trên một thuộc tính cụ thể
1769
+ * @param data Mảng các đối tượng cần nhóm
1770
+ * @param key Tên thuộc tính được sử dụng làm khóa nhóm
1771
+ * @returns Đối tượng với các giá trị từ mảng được nhóm theo thuộc tính đã chỉ định
1772
+ * @example
1773
+ * const data = [
1774
+ * { id: 1, name: 'John' },
1775
+ * { id: 2, name: 'Jane' },
1776
+ * { id: 1, name: 'John' }
1777
+ * ];
1778
+ * groupBy(data, 'id');
1779
+ * // Kết quả: {
1780
+ * // '1': [
1781
+ * // { id: 1, name: 'John' },
1782
+ * // { id: 1, name: 'John' }
1783
+ * // ],
1784
+ * // '2': [
1785
+ * // { id: 2, name: 'Jane' }
1786
+ * // }
1787
+ */
1788
+ const groupBy = (data, key) => {
1789
+ if (!data || !data.length || !Object.keys(get(data, '0')).length || !key) {
1790
+ return {};
1791
+ }
1792
+ return data.reduce((dir, nextItem) => {
1793
+ const valueOfKey = get(nextItem, key);
1794
+ if (typeof valueOfKey !== 'string' && typeof valueOfKey !== 'number' && typeof valueOfKey !== 'boolean') {
1795
+ return dir;
1668
1796
  }
1669
- if (config.languageKeyCache) {
1670
- this.languageKeyCache = config.languageKeyCache;
1797
+ if (!Object.keys(dir).includes(`${valueOfKey}`)) {
1798
+ dir[`${valueOfKey}`] = [];
1671
1799
  }
1800
+ dir[`${valueOfKey}`].push(nextItem);
1801
+ return dir;
1802
+ }, {});
1803
+ };
1804
+ /**
1805
+ * Tạo một mảng các số từ giá trị bắt đầu đến giá trị kết thúc với bước nhảy tùy chọn
1806
+ * @param start Giá trị bắt đầu của dãy số. Nếu chỉ có một tham số, đây sẽ là giá trị kết thúc và giá trị bắt đầu sẽ là 0
1807
+ * @param end Giá trị kết thúc của dãy số (tùy chọn)
1808
+ * @param step Bước nhảy giữa các số trong dãy (tùy chọn). Mặc định là 1 nếu end > start, -1 nếu end < start
1809
+ * @returns Mảng các số từ start đến end với bước nhảy step
1810
+ * @example
1811
+ * range(4); // [0, 1, 2, 3]
1812
+ * range(1, 5); // [1, 2, 3, 4]
1813
+ * range(0, 20, 5); // [0, 5, 10, 15]
1814
+ * range(5, 2); // [5, 4, 3]
1815
+ */
1816
+ const range = (start, end, step) => {
1817
+ if (end === undefined || end === null) {
1818
+ end = start;
1819
+ start = 0;
1672
1820
  }
1673
- static setLang(lang) {
1674
- if (!UtilsLanguageConstants.isSupported(lang)) {
1675
- throw Error(`Language not support ${lang}`);
1821
+ if (!step) {
1822
+ step = end < 0 ? -1 : 1;
1823
+ }
1824
+ if (end < start && step > 0) {
1825
+ step *= -1;
1826
+ }
1827
+ const valueStartByStep = step + start;
1828
+ const direction = start < end ? 'asc' : 'desc';
1829
+ if ((direction === 'asc' && (valueStartByStep < start || valueStartByStep > end)) || (direction === 'desc' && (valueStartByStep > start || valueStartByStep < end))) {
1830
+ return [start];
1831
+ }
1832
+ const arr = new Array();
1833
+ for (let index = 0; index < Math.abs(end - start); index++) {
1834
+ let value = start + index * step;
1835
+ if (index === 0) {
1836
+ value = start;
1676
1837
  }
1677
- this.Set(this.languageKeyCache, lang, this.CACHE_EXPIRE_NONE);
1838
+ if ((direction === 'asc' && (value < start || value > end)) || (direction === 'desc' && (value > start || value < end))) {
1839
+ return arr;
1840
+ }
1841
+ arr.push(value);
1678
1842
  }
1679
- static getLang() {
1680
- return this.Get(this.languageKeyCache, UtilsLanguageConstants.defaultLang);
1843
+ return arr;
1844
+ };
1845
+ /**
1846
+ * So sánh hai giá trị bất kỳ có bằng nhau hay không
1847
+ * @param value1 Giá trị thứ nhất cần so sánh
1848
+ * @param value2 Giá trị thứ hai cần so sánh
1849
+ * @param exactlyPosition Có so sánh chính xác vị trí các phần tử trong mảng hay không
1850
+ * @returns true nếu hai giá trị bằng nhau, false nếu không bằng nhau
1851
+ * @example
1852
+ * isEqual([1,2,3], [1,2,3]); // true
1853
+ * isEqual([1,2,3], [3,2,1]); // true khi exactlyPosition = false
1854
+ * isEqual([1,2,3], [3,2,1]); // false khi exactlyPosition = true
1855
+ * isEqual({a:1}, {a:1}); // true
1856
+ */
1857
+ const isEqual = (value1, value2, options) => {
1858
+ const { exactlyPosition = false, ignoreExactlyDataType = false } = options || {};
1859
+ if (value1 === value2 || (value1 === null && value2 === null) || (value1 === undefined && value2 === undefined)) {
1860
+ return true;
1681
1861
  }
1682
- static openDB() {
1683
- return new Promise(resolve => {
1684
- const request = indexedDB.open(this.dbName, this.dbVersion);
1685
- request.onupgradeneeded = (event) => {
1686
- const db = event.target.result;
1687
- if (!db.objectStoreNames.contains(this.dbName)) {
1688
- const objectStore = db.createObjectStore(this.dbName, { keyPath: this.itemIndexByKey });
1689
- objectStore.createIndex(this.itemIndexByKey, this.itemIndexByKey, { unique: true });
1690
- }
1691
- };
1692
- request.onsuccess = () => {
1693
- this.db = request.result;
1694
- resolve(true);
1695
- };
1696
- request.onerror = (event) => {
1697
- console.trace('Error opening IndexedDB:', event);
1698
- resolve(false);
1699
- };
1700
- });
1862
+ if (ignoreExactlyDataType) {
1863
+ return isEqual(isNil(value1) ? undefined : `${value1}`, isNil(value2) ? undefined : `${value2}`);
1864
+ }
1865
+ // Handle signals
1866
+ while (isSignal(value1)) {
1867
+ value1 = value1();
1868
+ }
1869
+ while (isSignal(value2)) {
1870
+ value2 = value2();
1701
1871
  }
1702
- static async getObjectStore() {
1703
- if (!this.db) {
1704
- await this.openDB();
1872
+ if (typeof value1 !== 'object' || typeof value2 !== 'object' || (Array.isArray(value1) && !Array.isArray(value2)) || (!Array.isArray(value1) && Array.isArray(value2))) {
1873
+ return false;
1874
+ }
1875
+ if (Array.isArray(value1)) {
1876
+ if (value1.length !== value2.length) {
1877
+ return false;
1705
1878
  }
1706
- const transaction = this.db?.transaction([this.dbName], 'readwrite');
1707
- if (!transaction) {
1708
- return null;
1879
+ if (!exactlyPosition) {
1880
+ return !value1.some(item => !value2.includes(item));
1709
1881
  }
1710
- return transaction.objectStore(this.dbName);
1882
+ return !value1.some((item, index) => !isEqual(item, value2[index], options));
1711
1883
  }
1712
- static get LocalStorage() {
1713
- try {
1714
- if (typeof window.localStorage !== 'undefined') {
1715
- const OS = getPlatFromBrowser();
1716
- if (OS.includes('Safari') && parseFloat(OS.split(' ').pop() || '0') >= 16) {
1717
- return this.getLocalStorageFakeOnSafari();
1884
+ if (Object.keys(value1).length !== Object.keys(value2).length) {
1885
+ return false;
1886
+ }
1887
+ return !Object.keys(value1).some((key) => !isEqual(value1[key], value2[key], options));
1888
+ };
1889
+ /**
1890
+ * Loại bỏ các phần tử trùng lặp trong mảng dựa trên một thuộc tính chỉ định
1891
+ * @param data Mảng dữ liệu cần xử lý
1892
+ * @param key Tên thuộc tính dùng để so sánh trùng lặp. Nếu không có key thì so sánh trực tiếp giá trị
1893
+ * @returns Mảng mới chứa các phần tử không trùng lặp
1894
+ * @example
1895
+ * const arr = [
1896
+ * { id: 1, name: 'A' },
1897
+ * { id: 2, name: 'B' },
1898
+ * { id: 1, name: 'C' }
1899
+ * ];
1900
+ * uniqBy(arr, 'id'); // [{ id: 1, name: 'A' }, { id: 2, name: 'B' }]
1901
+ *
1902
+ * const numbers = [1, 2, 2, 3, 3];
1903
+ * uniqBy(numbers); // [1, 2, 3]
1904
+ *
1905
+ * const numbersSignal = [signal(1), signal(2), signal(3), signal(2), signal(5), signal(4), signal(1), signal(6), signal(7), signal(6)];
1906
+ * uniqBy(numbersSignal); // [signal(1), signal(2), signal(3), signal(5), signal(4), signal(6), signal(7)]
1907
+ */
1908
+ const uniqBy = (data, key) => {
1909
+ if (!key || !data?.length || typeof get(data, '0') !== 'object') {
1910
+ // Xử lý mảng chứa signal values
1911
+ if (data[0] && isSignal(data[0])) {
1912
+ const seen = new Set();
1913
+ return data.filter(item => {
1914
+ const value = `${get(item, '')}`;
1915
+ if (seen.has(value)) {
1916
+ return false;
1718
1917
  }
1719
- return localStorage;
1720
- }
1721
- return this.getLocalStorageFake();
1722
- }
1723
- catch (error) {
1724
- console.trace(`LocalStorage `, error);
1725
- return this.getLocalStorageFake();
1918
+ seen.add(value);
1919
+ return true;
1920
+ });
1726
1921
  }
1922
+ // Xử lý mảng primitive values
1923
+ return Array.from(new Set(data));
1727
1924
  }
1728
- static getLocalStorageFakeOnSafari() {
1729
- if (typeof window.localStorage !== 'undefined' && !this.storage && Object.keys(localStorage).length) {
1730
- this.storage = { ...localStorage };
1925
+ const dataUnique = keyBy(data, key);
1926
+ return Object.keys(dataUnique).map(key => dataUnique[key]);
1927
+ };
1928
+ const generateInterface = (obj, interfaceName) => {
1929
+ const generateType = (value) => {
1930
+ if (value === null) {
1931
+ return 'null';
1731
1932
  }
1732
- return {
1733
- setItem: (key, value) => {
1734
- localStorage.setItem(key, value);
1735
- this.storage[key] = value;
1736
- },
1737
- getItem: (key) => {
1738
- const value = localStorage.getItem(key);
1739
- if (value) {
1740
- return value;
1741
- }
1742
- if (!this.storage || isNil(this.storage[key])) {
1743
- return null;
1744
- }
1745
- localStorage.setItem(key, this.storage[key]);
1746
- return this.storage?.[key];
1747
- },
1748
- removeItem: (key) => {
1749
- delete this.storage?.[key];
1750
- localStorage.removeItem(key);
1751
- },
1752
- clear: () => {
1753
- this.storage = {};
1754
- localStorage.clear();
1755
- }
1756
- };
1757
- }
1758
- static getLocalStorageFake() {
1759
- if (!this.storage) {
1760
- this.storage = {};
1933
+ const type = typeof value;
1934
+ if (type === 'string') {
1935
+ return 'string';
1761
1936
  }
1762
- return {
1763
- setItem: (key, value) => {
1764
- this.storage[key] = value;
1765
- },
1766
- getItem: (key) => {
1767
- return this.storage?.[key];
1768
- },
1769
- removeItem: (key) => {
1770
- delete this.storage?.[key];
1771
- },
1772
- clear: () => {
1773
- this.storage = {};
1774
- }
1775
- };
1776
- }
1777
- static async GetAsync(key, default_value, isKeyMD5 = false) {
1778
- key = isKeyMD5 ? key : md5(key);
1779
- return new Promise(async (resolve) => {
1780
- const objectStore = await this.getObjectStore();
1781
- if (!objectStore) {
1782
- return resolve(default_value);
1783
- }
1784
- const request = objectStore.get(key);
1785
- request.onsuccess = () => {
1786
- if (!request.result) {
1787
- return resolve(default_value);
1788
- }
1789
- const data = JSON.parse(decrypt(request.result.value));
1790
- if (data.expire === this.CACHE_EXPIRE_NONE) {
1791
- return resolve(data.json);
1792
- }
1793
- const currentMillisecond = (new Date().valueOf() / 1000);
1794
- if (data.expire < currentMillisecond) {
1795
- return resolve(default_value);
1796
- }
1797
- return resolve(data.json);
1798
- };
1799
- request.onerror = () => {
1800
- console.trace(`Get key ${key} Error, return default value: ${default_value}`);
1801
- return resolve(default_value);
1802
- };
1803
- });
1804
- }
1805
- static Get(key, default_value) {
1806
- if (!key) {
1807
- return this.GetDefaultValueBySpecificKey(key, default_value);
1937
+ if (type === 'number') {
1938
+ return 'number';
1808
1939
  }
1809
- const cachedData = this.LocalStorage.getItem(key);
1810
- if (!cachedData) {
1811
- return this.GetDefaultValueBySpecificKey(key, default_value);
1940
+ if (type === 'boolean') {
1941
+ return 'boolean';
1812
1942
  }
1813
- try {
1814
- const data = JSON.parse(decrypt(cachedData));
1815
- if (data.expire === this.CACHE_EXPIRE_NONE) {
1816
- return data.value ?? default_value;
1817
- }
1818
- const currentMillisecond = (new Date().valueOf() / 1000);
1819
- if (data.expire < currentMillisecond) {
1820
- return this.GetDefaultValueBySpecificKey(key, default_value);
1821
- }
1822
- return data.value;
1943
+ if (type === 'undefined') {
1944
+ return 'any';
1823
1945
  }
1824
- catch (error) {
1825
- console.trace(error);
1826
- console.trace(`Get key ${key} Error, return default value: ${default_value}`, error);
1827
- return this.GetDefaultValueBySpecificKey(key, default_value);
1946
+ if (value instanceof Date) {
1947
+ return 'Date';
1828
1948
  }
1829
- }
1830
- static GetDefaultValueBySpecificKey(key, default_value) {
1831
- return default_value;
1832
- }
1833
- static async SetAsync(key, value, expireTimeBySecond = this.CACHE_EXPIRE_TIME_DEFAULT, isKeyMD5 = false) {
1834
- return new Promise(async (resolve) => {
1835
- const objectStore = await this.getObjectStore();
1836
- key = isKeyMD5 ? key : md5(key);
1837
- try {
1838
- const currentMillisecond = expireTimeBySecond === this.CACHE_EXPIRE_NONE ? this.CACHE_EXPIRE_NONE : (new Date().valueOf() / 1000) + expireTimeBySecond;
1839
- const data = {
1840
- value: encrypt(JSON.stringify({ json: value, expire: currentMillisecond })),
1841
- };
1842
- data[this.itemIndexByKey] = key;
1843
- if (!objectStore) {
1844
- console.trace(`Can not open object store`);
1845
- return resolve({ key, messageError: 'Can not open object store' });
1846
- }
1847
- const request = objectStore?.put(data);
1848
- request.onsuccess = () => {
1849
- console.log(`Set key ${key} Success`);
1850
- resolve(request.result);
1851
- };
1852
- request.onerror = (error) => {
1853
- console.trace(`Set key ${key} Error`);
1854
- resolve({ key, messageError: get(error, 'message') });
1855
- };
1949
+ if (value instanceof RegExp) {
1950
+ return 'RegExp';
1951
+ }
1952
+ if (Array.isArray(value)) {
1953
+ if (value.length === 0) {
1954
+ return 'Array<any>';
1856
1955
  }
1857
- catch (error) {
1858
- console.trace(`Set key ${key} Error`);
1859
- resolve({ key, messageError: get(error, 'message') });
1956
+ return `Array<${generateType(value[0])}>`;
1957
+ }
1958
+ if (type === 'object') {
1959
+ let interfaceStr = '{\n';
1960
+ for (const key in value) {
1961
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
1962
+ const valueType = generateType(value[key]);
1963
+ interfaceStr += ` ${key}: ${valueType};\n`;
1964
+ }
1860
1965
  }
1861
- });
1862
- }
1863
- static Set(key, value, expireTimeBySecond = this.CACHE_EXPIRE_TIME_DEFAULT) {
1864
- const currentMillisecond = expireTimeBySecond === this.CACHE_EXPIRE_NONE ? this.CACHE_EXPIRE_NONE : (new Date().valueOf() / 1000) + expireTimeBySecond;
1865
- const data = {
1866
- value: value,
1867
- expire: currentMillisecond
1868
- };
1869
- try {
1870
- this.LocalStorage.setItem(key, encrypt(JSON.stringify(data)));
1871
- return true;
1966
+ interfaceStr += '}';
1967
+ return interfaceStr;
1968
+ }
1969
+ return 'any';
1970
+ };
1971
+ const interfaceStr = `interface ${interfaceName} ${generateType(obj)}`;
1972
+ return interfaceStr;
1973
+ };
1974
+
1975
+ ;
1976
+ const step = 20;
1977
+ const percent = 0.05;
1978
+ const colorStepContrastFromOrigin = (color, stepNumber) => {
1979
+ return colorContrastFromOrigin(color).find(item => item.step === stepNumber);
1980
+ };
1981
+ const colorContrastFromOrigin = (color) => {
1982
+ const parsedColorsArray = parseColorValues(color);
1983
+ const colors = [];
1984
+ let calculatedShades = [];
1985
+ let calculatedTints = [];
1986
+ if (parsedColorsArray !== null) {
1987
+ for (let i = 0; i < parsedColorsArray.length; i++) {
1988
+ calculatedShades = calculateShades(parsedColorsArray[i]);
1989
+ calculatedTints = calculateTints(parsedColorsArray[i]);
1872
1990
  }
1873
- catch (error) {
1874
- console.trace(`Set key ${key} Error`, error);
1875
- return false;
1991
+ for (let i = 0; i <= step; i++) {
1992
+ colors.push({ step: i * 5, label: `${i * 5}%`, dark: `#${calculatedShades[i]}`, light: `#${calculatedTints[i]}` });
1876
1993
  }
1877
1994
  }
1878
- static async ClearAsync(key, isMD5 = false) {
1879
- return new Promise(async (resolve) => {
1880
- const objectStore = await this.getObjectStore();
1881
- if (!objectStore) {
1882
- return resolve();
1995
+ return colors;
1996
+ };
1997
+ const parseColorValues = (colorValues) => {
1998
+ let colorValuesArray = colorValues.match(/\b[0-9A-Fa-f]{3}\b|[0-9A-Fa-f]{6}\b/g);
1999
+ if (colorValuesArray) {
2000
+ colorValuesArray = colorValuesArray.map((item) => {
2001
+ if (item.length === 3) {
2002
+ let newItem = item.toString().split('');
2003
+ newItem = newItem.reduce((acc, it) => {
2004
+ return acc + it + it;
2005
+ }, '');
2006
+ return newItem;
1883
2007
  }
1884
- const request = objectStore.delete(isMD5 ? key : md5(key));
1885
- request.onsuccess = () => {
1886
- resolve({ clearSuccess: true });
1887
- };
1888
- request.onerror = (event) => {
1889
- console.trace('Error deleting Key:', get(event.target.error, 'message'));
1890
- resolve({ messageError: get(event.target.error, 'message'), clearSuccess: false });
1891
- };
2008
+ return item;
1892
2009
  });
1893
2010
  }
1894
- static Clear(key) {
1895
- if (key.includes('kc-callback-')) {
1896
- return;
1897
- }
1898
- this.LocalStorage.removeItem(key);
2011
+ return colorValuesArray;
2012
+ };
2013
+ const calculateShades = (colorValue) => {
2014
+ return calculate(colorValue, rgbShade).concat("000000");
2015
+ };
2016
+ const calculateTints = (colorValue) => {
2017
+ return calculate(colorValue, rgbTint).concat("ffffff");
2018
+ };
2019
+ const calculate = (colorValue, shadeOrTint) => {
2020
+ const color = hexToRGB(colorValue);
2021
+ const shadeValues = [];
2022
+ for (let i = 0; i < step; i++) {
2023
+ shadeValues[i] = rgbToHex(shadeOrTint(color, i));
1899
2024
  }
1900
- static ClearAllAsync() {
1901
- return new Promise(async (resolve) => {
1902
- const objectStore = await this.getObjectStore();
1903
- if (!objectStore) {
1904
- return resolve();
1905
- }
1906
- const request = objectStore.clear();
1907
- request.onsuccess = () => {
1908
- console.log('clear all successfully');
1909
- resolve({ clearSuccess: true });
1910
- };
1911
- request.onerror = (event) => {
1912
- console.trace('Error deleting key:', get(event.target.error, 'message'));
1913
- resolve({ messageError: get(event.target.error, 'message'), clearSuccess: false });
1914
- };
1915
- });
2025
+ return shadeValues;
2026
+ };
2027
+ const rgbShade = (rgb, i) => { return { red: rgb.red * (1 - percent * i), green: rgb.green * (1 - percent * i), blue: rgb.blue * (1 - percent * i) }; };
2028
+ const rgbTint = (rgb, i) => { return { red: rgb.red + (255 - rgb.red) * i * percent, green: rgb.green + (255 - rgb.green) * i * percent, blue: rgb.blue + (255 - rgb.blue) * i * percent }; };
2029
+ const rgbToHex = (rgb) => { return intToHex(rgb.red) + intToHex(rgb.green) + intToHex(rgb.blue); };
2030
+ const hexToRGB = (colorValue) => { return { red: parseInt(colorValue.substr(0, 2), 16), green: parseInt(colorValue.substr(2, 2), 16), blue: parseInt(colorValue.substr(4, 2), 16) }; };
2031
+ const intToHex = (rgbint) => { return pad(Math.min(Math.max(Math.round(rgbint), 0), 255).toString(16), 2); };
2032
+ const pad = (number, length) => {
2033
+ let str = '' + number;
2034
+ while (str.length < length) {
2035
+ str = '0' + str;
1916
2036
  }
1917
- static ClearAll() {
1918
- if (isEmbedFrame()) {
1919
- const data = {
1920
- type: this.typeKeyClearLocalStorage,
1921
- response: {
1922
- idEvent: this.idService
1923
- }
1924
- };
1925
- UtilsCommunicateMicro.PostMessageToParent(data);
1926
- }
1927
- const keys = [...this.listKeyKeepWhenClearALll];
1928
- Object.keys(this.LocalStorage).forEach(key => {
1929
- if (key.includes('kc-callback-')) {
1930
- keys.push(key);
1931
- }
1932
- });
1933
- const stores = this.GetDataByKeys(Array.from(keys));
1934
- this.LocalStorage.clear();
1935
- this.SetDataByKey(stores);
2037
+ return str;
2038
+ };
2039
+ const listColorDefine = ['#E62222', '#B81B1B', '#EB4E4E', '#F97316', '#C75C12', '#FA8F45', '#FFB700', '#CC9200', '#FFC533', '#84CC16', '#6AA312', '#9dd645', '#00BC62', '#00A757', '#33DA8A', '#06B6D4', '#1B59C4', '#4E8CF7', '#0EA5E9',
2040
+ '#1B59C4', '#4E8CF7', '#226FF5', '#1B59C4', '#4E8CF7', '#6366F1', '#4F52C1', '#8285F4', '#5B04B3', '#49038F', '#7C36C2', '#D946EF', '#AE38BF', '#E16BF2', '#EC4899', '#BD3A7A', '#F06DAD', '#F43F5E', '#C3324B', '#F6657E', '#757380', '#5E5C66', '#918F99',
2041
+ '#202020', '#1A1A1A', '#4D4D4D'
2042
+ ];
2043
+ const getColorById = (str) => {
2044
+ let hashString = 0;
2045
+ if (!str) {
2046
+ return '';
1936
2047
  }
1937
- static GetDataByKeys(keys) {
1938
- const stores = new Map();
1939
- keys.forEach((key) => {
1940
- if (key.includes('kc-callback-')) {
1941
- stores.set(key, this.LocalStorage.getItem(key));
1942
- return;
1943
- }
1944
- stores.set(key, this.Get(key));
1945
- });
1946
- return stores;
2048
+ for (let i = 0; i < str.length; i++) {
2049
+ const char = str.charCodeAt(i);
2050
+ hashString = ((hashString << 5) - hashString) + char;
2051
+ hashString = hashString & hashString;
1947
2052
  }
1948
- static SetDataByKey(stores) {
1949
- stores.forEach((value, key) => {
1950
- if (key.includes('kc-callback-')) {
1951
- this.LocalStorage.setItem(key, value);
1952
- return;
1953
- }
1954
- if (key === this.languageKeyCache) {
1955
- return this.setLang(value);
1956
- }
1957
- this.Set(key, value, this.CACHE_EXPIRE_NONE);
1958
- });
2053
+ return listColorDefine[Math.abs(hashString) % listColorDefine.length];
2054
+ };
2055
+
2056
+ class UtilsKeyCodeConstant {
2057
+ static MAC_ENTER = 3;
2058
+ static BACKSPACE = 8;
2059
+ static TAB = 9;
2060
+ static NUM_CENTER = 12;
2061
+ static ENTER = 13;
2062
+ static SHIFT = 16;
2063
+ static CONTROL = 17;
2064
+ static ALT = 18;
2065
+ static PAUSE = 19;
2066
+ static CAPS_LOCK = 20;
2067
+ static ESCAPE = 27;
2068
+ static SPACE = 32;
2069
+ static PAGE_UP = 33;
2070
+ static PAGE_DOWN = 34;
2071
+ static END = 35;
2072
+ static HOME = 36;
2073
+ static LEFT_ARROW = 37;
2074
+ static UP_ARROW = 38;
2075
+ static RIGHT_ARROW = 39;
2076
+ static DOWN_ARROW = 40;
2077
+ static PLUS_SIGN = 43;
2078
+ static PRINT_SCREEN = 44;
2079
+ static INSERT = 45;
2080
+ static DELETE = 46;
2081
+ static ZERO = 48;
2082
+ static ONE = 49;
2083
+ static TWO = 50;
2084
+ static THREE = 51;
2085
+ static FOUR = 52;
2086
+ static FIVE = 53;
2087
+ static SIX = 54;
2088
+ static SEVEN = 55;
2089
+ static EIGHT = 56;
2090
+ static NINE = 57;
2091
+ static FF_SEMICOLON = 59; // Firefox (Gecko) fires this for semicolon instead of 186
2092
+ static FF_EQUALS = 61; // Firefox (Gecko) fires this for equals instead of 187
2093
+ static QUESTION_MARK = 63;
2094
+ static AT_SIGN = 64;
2095
+ static A = 65;
2096
+ static B = 66;
2097
+ static C = 67;
2098
+ static D = 68;
2099
+ static E = 69;
2100
+ static F = 70;
2101
+ static G = 71;
2102
+ static H = 72;
2103
+ static I = 73;
2104
+ static J = 74;
2105
+ static K = 75;
2106
+ static L = 76;
2107
+ static M = 77;
2108
+ static N = 78;
2109
+ static O = 79;
2110
+ static P = 80;
2111
+ static Q = 81;
2112
+ static R = 82;
2113
+ static S = 83;
2114
+ static T = 84;
2115
+ static U = 85;
2116
+ static V = 86;
2117
+ static W = 87;
2118
+ static X = 88;
2119
+ static Y = 89;
2120
+ static Z = 90;
2121
+ static META = 91; // WIN_KEY_LEFT
2122
+ static MAC_WK_CMD_LEFT = 91;
2123
+ static MAC_WK_CMD_RIGHT = 93;
2124
+ static CONTEXT_MENU = 93;
2125
+ static NUMPAD_ZERO = 96;
2126
+ static NUMPAD_ONE = 97;
2127
+ static NUMPAD_TWO = 98;
2128
+ static NUMPAD_THREE = 99;
2129
+ static NUMPAD_FOUR = 100;
2130
+ static NUMPAD_FIVE = 101;
2131
+ static NUMPAD_SIX = 102;
2132
+ static NUMPAD_SEVEN = 103;
2133
+ static NUMPAD_EIGHT = 104;
2134
+ static NUMPAD_NINE = 105;
2135
+ static NUMPAD_MULTIPLY = 106;
2136
+ static NUMPAD_PLUS = 107;
2137
+ static NUMPAD_MINUS = 109;
2138
+ static NUMPAD_PERIOD = 110;
2139
+ static NUMPAD_DIVIDE = 111;
2140
+ static F1 = 112;
2141
+ static F2 = 113;
2142
+ static F3 = 114;
2143
+ static F4 = 115;
2144
+ static F5 = 116;
2145
+ static F6 = 117;
2146
+ static F7 = 118;
2147
+ static F8 = 119;
2148
+ static F9 = 120;
2149
+ static F10 = 121;
2150
+ static F11 = 122;
2151
+ static F12 = 123;
2152
+ static NUM_LOCK = 144;
2153
+ static SCROLL_LOCK = 145;
2154
+ static FIRST_MEDIA = 166;
2155
+ static FF_MINUS = 173;
2156
+ static MUTE = 173; // Firefox (Gecko) fires 181 for MUTE
2157
+ static VOLUME_DOWN = 174; // Firefox (Gecko) fires 182 for VOLUME_DOWN
2158
+ static VOLUME_UP = 175; // Firefox (Gecko) fires 183 for VOLUME_UP
2159
+ static FF_MUTE = 181;
2160
+ static FF_VOLUME_DOWN = 182;
2161
+ static LAST_MEDIA = 183;
2162
+ static FF_VOLUME_UP = 183;
2163
+ static SEMICOLON = 186; // Firefox (Gecko) fires 59 for SEMICOLON
2164
+ static EQUALS = 187; // Firefox (Gecko) fires 61 for EQUALS
2165
+ static COMMA = 188;
2166
+ static DASH = 189; // Firefox (Gecko) fires 173 for DASH/MINUS
2167
+ static PERIOD = 190;
2168
+ static SLASH = 191;
2169
+ static APOSTROPHE = 192;
2170
+ static TILDE = 192;
2171
+ static OPEN_SQUARE_BRACKET = 219;
2172
+ static BACKSLASH = 220;
2173
+ static CLOSE_SQUARE_BRACKET = 221;
2174
+ static SINGLE_QUOTE = 222;
2175
+ static MAC_META = 224;
2176
+ }
2177
+
2178
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2179
+ const getKeyCacheByArrayObject = (keyCache, argumentsValue) => {
2180
+ if (!keyCache || !argumentsValue) {
2181
+ return '';
1959
2182
  }
1960
- static DeleteKeyStartWithAsync(keyCacheStartWith, isKeyMD5 = false) {
1961
- return new Promise(async (resolve) => {
1962
- const objectStore = await this.getObjectStore();
1963
- if (!objectStore) {
1964
- return resolve({});
2183
+ let keyBuild = `${JSON.stringify(argumentsValue.slice(1))}-key-cache-plus`;
2184
+ argumentsValue.forEach((item) => {
2185
+ if (typeof item === 'object') {
2186
+ if (Array.isArray(item)) {
2187
+ keyBuild = `${keyBuild}${JSON.stringify(item)}`;
2188
+ return;
1965
2189
  }
1966
- // Lấy tất cả các keys từ index
1967
- const request = objectStore.getAll();
1968
- keyCacheStartWith = isKeyMD5 ? keyCacheStartWith : md5(keyCacheStartWith);
1969
- request.onsuccess = (e) => {
1970
- const data = e.target.result;
1971
- if (!Array.isArray(data)) {
1972
- return resolve({});
1973
- }
1974
- data.forEach(obj => {
1975
- if (obj[this.itemIndexByKey].startsWith(keyCacheStartWith)) {
1976
- this.ClearAsync(obj[this.itemIndexByKey], true);
1977
- }
1978
- });
1979
- return resolve({});
1980
- };
1981
- request.onerror = () => {
1982
- return resolve({});
1983
- };
1984
- });
1985
- }
1986
- static DeleteKeyStartWith(keyCache, isMD5 = false) {
1987
- keyCache = isMD5 ? md5(keyCache) : keyCache;
1988
- const keys = Object.keys(this.LocalStorage);
1989
- if (!keys || !keys.length) {
2190
+ const keys = (item instanceof HttpParams ? item.keys() : Object.keys(item)).sort(((str1, str2) => str1.localeCompare(str2)));
2191
+ keys.forEach(key => {
2192
+ keyBuild = `${keyBuild}${JSON.stringify(get(item, key))}`;
2193
+ });
1990
2194
  return;
1991
2195
  }
1992
- keys.forEach(key => {
1993
- if (key.startsWith(keyCache)) {
1994
- this.Clear(key);
1995
- }
1996
- });
1997
- }
1998
- static DeleteDatabaseIndexDB(dbName) {
1999
- return new Promise((resolve) => {
2000
- dbName = (dbName || this.dbName);
2001
- const request = indexedDB.deleteDatabase(dbName);
2002
- request.onsuccess = () => {
2003
- console.trace('Database deleted successfully');
2004
- resolve({ deleteSuccess: true });
2005
- };
2006
- request.onerror = (event) => {
2007
- console.trace('Error deleting database:', event.target.error);
2008
- resolve({ messageError: get(event.target.error, 'message'), deleteSuccess: false });
2009
- };
2010
- request.onblocked = () => {
2011
- console.trace('Delete request is blocked');
2012
- resolve({ messageError: 'Delete request is blocked', deleteSuccess: false });
2013
- };
2014
- });
2015
- }
2016
- }
2196
+ keyBuild = `${keyBuild}${item}`;
2197
+ });
2198
+ const keyCachePlus = md5(keyBuild);
2199
+ const keyCacheMD5 = md5(keyCache);
2200
+ return `${keyCacheMD5}-${md5(`${keyCacheMD5}-${keyCachePlus}`)}`;
2201
+ };
2017
2202
 
2018
2203
  const formatNumber = (value) => {
2019
2204
  const lang = UtilsCache.getLang();
@@ -2194,185 +2379,6 @@ const convertSignalToObject = (data, isCloneDeep = true, seen = new WeakMap()) =
2194
2379
  return result;
2195
2380
  };
2196
2381
 
2197
- dayjs.extend(localeData);
2198
- dayjs.extend(updateLocale);
2199
- dayjs.extend(utc);
2200
- dayjs.extend(timezone);
2201
- dayjs.extend(customParseFormat);
2202
- let timeZoneSetup = "Asia/Ho_Chi_Minh";
2203
- const setDefaultTimeZone = (localeZone = timeZoneSetup) => {
2204
- timeZoneSetup = localeZone;
2205
- dayjs.tz.setDefault(localeZone);
2206
- };
2207
- let functionFormatDate = undefined;
2208
- const updateFunctionFormatDate = (functionCustom) => {
2209
- functionFormatDate = functionCustom;
2210
- };
2211
- /**
2212
- * @description Lấy đối tượng dayjs theo config
2213
- * @param config nếu không có config sẽ trả về đối tượng dayjs là thời gian hiện tại
2214
- * @param config.date thời gian cần lấy
2215
- * @param config.returnDayjsIfConfigDateNotExist true nếu muốn trả về đối tượng dayjs nếu config.date không có
2216
- * @param config.utc true nếu muốn lấy thời gian UTC
2217
- * @param config.formatOfDate định dạng thời gian đang được truyền vào
2218
- */
2219
- const getDayjs = (config) => {
2220
- if (!config) {
2221
- return dayjs().tz();
2222
- }
2223
- config.date = !config.date && config.returnDayjsIfConfigDateNotExist ? dayjs().tz() : config.date;
2224
- if (typeof config.date === 'number') {
2225
- config.date = dayjs.unix(config.date);
2226
- }
2227
- if (!config.date) {
2228
- return undefined;
2229
- }
2230
- let { date, utc, formatOfDate } = config;
2231
- while (isSignal(date)) {
2232
- date = date();
2233
- }
2234
- while (isSignal(utc)) {
2235
- utc = utc();
2236
- }
2237
- while (isSignal(formatOfDate)) {
2238
- formatOfDate = formatOfDate();
2239
- }
2240
- if (utc) {
2241
- if (formatOfDate) {
2242
- return dayjs(date, formatOfDate).utc();
2243
- }
2244
- const dateInputIsUTC = (typeof date === 'object' && date.isUTC()) || (typeof date === 'string' && date.includes('Z'));
2245
- if (dateInputIsUTC) {
2246
- return dayjs(date);
2247
- }
2248
- return dayjs(date).utc();
2249
- }
2250
- if (typeof date === 'string' && !date.includes('Z') && !date.includes('+')) {
2251
- return (formatOfDate ? dayjs.tz(date, formatOfDate, config.localeZone || timeZoneSetup) : dayjs.tz(date, config.localeZone || timeZoneSetup));
2252
- }
2253
- return (formatOfDate ? dayjs(date, formatOfDate) : dayjs(date)).tz();
2254
- };
2255
- /**
2256
- * @description Kiểm tra xem hai ngày có khác nhau không (khác ngày trong tuần)
2257
- * @param date1 ngày thứ nhất cần so sánh
2258
- * @param date2 ngày thứ hai cần so sánh
2259
- * @returns true nếu hai ngày khác nhau, false nếu giống nhau hoặc không thể so sánh
2260
- */
2261
- const isDifferenceDay = (date1, date2) => {
2262
- if (isDifferenceMonth(date1, date2)) {
2263
- return true;
2264
- }
2265
- const day1 = getDayjs({ date: date1 })?.day();
2266
- const day2 = getDayjs({ date: date2 })?.day();
2267
- return day1 !== day2;
2268
- };
2269
- /**
2270
- * @description Kiểm tra xem hai ngày có khác tháng không
2271
- * @param date1 ngày thứ nhất cần so sánh
2272
- * @param date2 ngày thứ hai cần so sánh
2273
- * @returns true nếu hai ngày khác tháng, false nếu cùng tháng hoặc không thể so sánh
2274
- */
2275
- const isDifferenceMonth = (date1, date2) => {
2276
- if (isDifferenceYear(date1, date2)) {
2277
- return true;
2278
- }
2279
- const month1 = getDayjs({ date: date1 })?.month();
2280
- const month2 = getDayjs({ date: date2 })?.month();
2281
- return month1 !== month2;
2282
- };
2283
- /**
2284
- * @description Kiểm tra xem hai ngày có khác năm không
2285
- * @param date1 ngày thứ nhất cần so sánh
2286
- * @param date2 ngày thứ hai cần so sánh
2287
- * @returns true nếu hai ngày khác năm, false nếu cùng năm hoặc không thể so sánh
2288
- */
2289
- const isDifferenceYear = (date1, date2) => {
2290
- const year1 = getDayjs({ date: date1 })?.year();
2291
- const year2 = getDayjs({ date: date2 })?.year();
2292
- return year1 !== year2;
2293
- };
2294
- /**
2295
- * @description Lấy ra chuỗi thời gian hiển thị theo định dạng và ngôn ngữ
2296
- * @param date thời gian cần định dạng
2297
- * @param format định dạng thời gian muốn lấy ra
2298
- * @param lang lấy theo ngôn ngữ
2299
- */
2300
- const formatDate = (date, formatOutput = 'YYYY/MM/DD HH:mm', lang, formatInput) => {
2301
- if (!date) {
2302
- return '';
2303
- }
2304
- lang = lang || UtilsCache.getLang();
2305
- if (functionFormatDate) {
2306
- return functionFormatDate(date, formatOutput, lang);
2307
- }
2308
- date = getDayjs({ date, formatOfDate: formatInput }).locale(lang);
2309
- if (lang === 'vi') {
2310
- dayjs.updateLocale('vi', {
2311
- monthsShort: 'Thg 1_Thg 2_Thg 3_Thg 4_Thg 5_Thg 6_Thg 7_Thg 8_Thg 9_Thg 10_Thg 11_Thg 12'.split('_')
2312
- });
2313
- }
2314
- return date.format(getFormatData(getTypeByFormat(formatOutput), lang)) || '';
2315
- };
2316
- const getTypeByFormat = (format) => {
2317
- if (format === 'dm' || format === 'dmy' || format === 'dmy hm' || format === 'my') {
2318
- return format;
2319
- }
2320
- if (format === 'DD/MM/YYYY' || format === 'YYYY-MM-DD' || format === 'dd/MM/yyyy' || format === 'dd-MM-yyyy' || format === 'dd/mm/yyyy') {
2321
- return 'dmy';
2322
- }
2323
- if (format === 'MM-DD' || format === 'dd/MM' || format === 'dd/mm') {
2324
- return 'dm';
2325
- }
2326
- if (format === 'M/YYYY' || format === 'YYYY-MM' || format === 'MM/yyyy') {
2327
- return 'my';
2328
- }
2329
- if (format === 'YYYY/MM/DD hh:mm:ss' || format === 'dmy hms' || format === 'dd/mm/yyyy hh:mm:ss') {
2330
- return 'dmy hms';
2331
- }
2332
- if (format === 'hh:mm' || format === 'HH:mm') {
2333
- return 'hh:mm';
2334
- }
2335
- if (format === 'hh:mm A' || format === 'HH:mm A') {
2336
- return 'hh:mm A';
2337
- }
2338
- return 'dmy hm';
2339
- };
2340
- const getFormatData = (type, lang) => {
2341
- switch (type) {
2342
- case 'dm':
2343
- if (lang === 'vi') {
2344
- return 'D MMM';
2345
- }
2346
- return 'MMM D';
2347
- case 'dmy':
2348
- if (lang === 'vi') {
2349
- return 'D MMM, YYYY';
2350
- }
2351
- return 'MMM D, YYYY';
2352
- case 'dmy hm':
2353
- if (lang === 'vi') {
2354
- return 'D MMM, YYYY HH:mm';
2355
- }
2356
- return 'MMM D, YYYY HH:mm';
2357
- case 'my':
2358
- if (lang === 'vi') {
2359
- return 'MMM, YYYY';
2360
- }
2361
- return 'MMM YYYY ';
2362
- case 'dmy hms':
2363
- if (lang === 'vi') {
2364
- return 'D MMM, YYYY HH:mm:ss';
2365
- }
2366
- return 'MMM D, YYYY HH:mm:ss';
2367
- case 'hh:mm':
2368
- case 'HH:mm':
2369
- return 'HH:mm';
2370
- case 'hh:mm A':
2371
- case 'HH:mm A':
2372
- return 'HH:mm A';
2373
- }
2374
- };
2375
-
2376
2382
  const ENCODE_URI_PATTERN = /%([0-9A-F]{2})/g;
2377
2383
  const decodeURI = (value) => {
2378
2384
  return decodeURIComponent(value.split('').map((c) => {
@@ -2531,7 +2537,7 @@ const convertBlobToFile = (blob, fileName) => {
2531
2537
  return new File([blob], name, { type: blob.type, lastModified: Date.now() });
2532
2538
  };
2533
2539
  const convertUrlToFile = (url, fileName) => {
2534
- return new Promise((resolve) => {
2540
+ return new Promise((resolve, reject) => {
2535
2541
  const xhr = new XMLHttpRequest();
2536
2542
  xhr.onload = function () {
2537
2543
  const reader = new FileReader();
@@ -2541,6 +2547,9 @@ const convertUrlToFile = (url, fileName) => {
2541
2547
  };
2542
2548
  reader.readAsDataURL(xhr.response);
2543
2549
  };
2550
+ xhr.onerror = (err) => {
2551
+ reject(err);
2552
+ };
2544
2553
  xhr.open('GET', url);
2545
2554
  xhr.responseType = 'blob';
2546
2555
  xhr.send();