@libs-ui/utils 0.2.282 → 0.2.284
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.
- package/esm2022/get-smart-axis-scale.mjs +130 -52
- package/esm2022/helpers.mjs +65 -1
- package/fesm2022/libs-ui-utils.mjs +193 -51
- package/fesm2022/libs-ui-utils.mjs.map +1 -1
- package/get-smart-axis-scale.d.ts +25 -14
- package/helpers.d.ts +64 -0
- package/package.json +2 -2
|
@@ -645,6 +645,70 @@ const omitBy = (objData, predicate) => {
|
|
|
645
645
|
});
|
|
646
646
|
return newObj;
|
|
647
647
|
};
|
|
648
|
+
/**
|
|
649
|
+
* Lấy giá trị từ đối tượng theo đường dẫn chỉ định
|
|
650
|
+
*
|
|
651
|
+
* 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,
|
|
652
|
+
* tránh lỗi khi thuộc tính không tồn tại.
|
|
653
|
+
*
|
|
654
|
+
* @param obj Đối tượng nguồn cần lấy giá trị
|
|
655
|
+
* @param path Đường dẫn đến thuộc tính cần lấy. Có thể là:
|
|
656
|
+
* - Chuỗi: 'user.profile.name' hoặc 'items[0].title'
|
|
657
|
+
* - Mảng: ['user', 'profile', 'name'] hoặc ['items', '0', 'title']
|
|
658
|
+
* - Chuỗi rỗng '': trả về chính đối tượng gốc
|
|
659
|
+
* @param defaultValue Giá trị mặc định trả về khi không tìm thấy thuộc tính (mặc định: undefined)
|
|
660
|
+
* @param keepLastValueIfSignal Có giữ nguyên signal cuối cùng hay không (mặc định: false - sẽ gọi signal())
|
|
661
|
+
* @returns Giá trị tìm được hoặc giá trị mặc định
|
|
662
|
+
*
|
|
663
|
+
* @example
|
|
664
|
+
* // Ví dụ cơ bản
|
|
665
|
+
* const user = { name: 'John', age: 30 };
|
|
666
|
+
* get(user, 'name'); // 'John'
|
|
667
|
+
* get(user, 'email'); // undefined
|
|
668
|
+
* get(user, 'email', 'no-email'); // 'no-email'
|
|
669
|
+
*
|
|
670
|
+
* @example
|
|
671
|
+
* // Truyền path rỗng - trả về chính đối tượng gốc
|
|
672
|
+
* const data = { name: 'Alice', age: 25 };
|
|
673
|
+
* get(data, ''); // { name: 'Alice', age: 25 } (chính đối tượng data)
|
|
674
|
+
* get(data, '', 'default'); // { name: 'Alice', age: 25 } (bỏ qua defaultValue)
|
|
675
|
+
*
|
|
676
|
+
* @example
|
|
677
|
+
* // Truy cập thuộc tính sâu
|
|
678
|
+
* const data = {
|
|
679
|
+
* user: {
|
|
680
|
+
* profile: {
|
|
681
|
+
* name: 'Alice',
|
|
682
|
+
* settings: { theme: 'dark' }
|
|
683
|
+
* }
|
|
684
|
+
* }
|
|
685
|
+
* };
|
|
686
|
+
* get(data, 'user.profile.name'); // 'Alice'
|
|
687
|
+
* get(data, 'user.profile.settings.theme'); // 'dark'
|
|
688
|
+
* get(data, 'user.profile.avatar', 'default.jpg'); // 'default.jpg'
|
|
689
|
+
*
|
|
690
|
+
* @example
|
|
691
|
+
* // Truy cập mảng
|
|
692
|
+
* const items = [
|
|
693
|
+
* { name: 'Item 1', price: 100 },
|
|
694
|
+
* { name: 'Item 2', price: 200 }
|
|
695
|
+
* ];
|
|
696
|
+
* get(items, '[0].name'); // 'Item 1'
|
|
697
|
+
* get(items, '0.price'); // 100
|
|
698
|
+
* get(items, '[1].name'); // 'Item 2'
|
|
699
|
+
* get(items, '[2].name', 'Not found'); // 'Not found'
|
|
700
|
+
*
|
|
701
|
+
* @example
|
|
702
|
+
* // Sử dụng với mảng path
|
|
703
|
+
* const nested = { a: { b: { c: 'deep value' } } };
|
|
704
|
+
* get(nested, ['a', 'b', 'c']); // 'deep value'
|
|
705
|
+
* get(nested, ['a', 'b', 'd'], 'default'); // 'default'
|
|
706
|
+
*
|
|
707
|
+
* @example
|
|
708
|
+
* // Trường hợp đặc biệt
|
|
709
|
+
* get(null, 'any.path'); // undefined
|
|
710
|
+
* get(undefined, 'any.path', 'fallback'); // 'fallback'
|
|
711
|
+
*/
|
|
648
712
|
const get = (obj, path, defaultValue = undefined, keepLastValueIfSignal) => {
|
|
649
713
|
if (isNil(obj)) {
|
|
650
714
|
return defaultValue;
|
|
@@ -2447,66 +2511,60 @@ const convertUrlToFile = (url, fileName) => {
|
|
|
2447
2511
|
});
|
|
2448
2512
|
};
|
|
2449
2513
|
|
|
2514
|
+
const DEFAULT_MIN_TICK_COUNT = 5;
|
|
2515
|
+
const DEFAULT_MAX_TICK_COUNT = 10;
|
|
2516
|
+
const MIN_DISTANCE_TO_ZERO = 3;
|
|
2517
|
+
const NEGATIVE_THRESHOLD = -5;
|
|
2518
|
+
const FALLBACK_NEGATIVE_VALUE = -4;
|
|
2519
|
+
const MAX_POW_NUMBER = 14;
|
|
2520
|
+
const MAX_TEMPLATE_NUMBER = 10;
|
|
2450
2521
|
/**
|
|
2451
|
-
*
|
|
2452
|
-
*
|
|
2453
|
-
* -
|
|
2454
|
-
*
|
|
2455
|
-
*
|
|
2456
|
-
*
|
|
2457
|
-
* @
|
|
2458
|
-
* @
|
|
2522
|
+
* Tính toán smart axis scale cho biểu đồ
|
|
2523
|
+
*
|
|
2524
|
+
* @param maxData - Giá trị tối đa của dữ liệu
|
|
2525
|
+
* @param options - Các tùy chọn cấu hình axis scale
|
|
2526
|
+
* @returns Cấu hình axis scale bao gồm stepSize, max, min, tickAmount
|
|
2527
|
+
*
|
|
2528
|
+
* @throws {Error} INVALID_NEGATIVE_DATA - khi maxData < 0 mà acceptNegative = false
|
|
2529
|
+
* @throws {Error} MISSING_MIN_NEGATIVE - khi acceptNegative = true nhưng thiếu minNegative
|
|
2530
|
+
* @throws {Error} INVALID_RANGE - khi maxData < minNegative
|
|
2531
|
+
* @throws {Error} INVALID_MIN_NEGATIVE - khi minNegative >= 0
|
|
2532
|
+
*
|
|
2533
|
+
* @example
|
|
2534
|
+
* ```typescript
|
|
2535
|
+
* const result = getSmartAxisScale(100, { minTickCount: 5, maxTickCount: 10 });
|
|
2536
|
+
* // returns { stepSize: 20, max: 120, min: 0, tickAmount: 6 }
|
|
2537
|
+
* ```
|
|
2459
2538
|
*/
|
|
2460
|
-
const getSmartAxisScale = (
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
const
|
|
2464
|
-
const
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
stepCandidatesDefault.push(Math.pow(10, index) * (((templateNumber - 1) * 2 + 1) / 2));
|
|
2469
|
-
}
|
|
2470
|
-
stepCandidatesDefault.push(Math.pow(10, index) * templateNumber);
|
|
2471
|
-
}
|
|
2472
|
-
});
|
|
2473
|
-
const stepCandidates = options?.stepCandidates || stepCandidatesDefault;
|
|
2474
|
-
let distanceToZero = maxData;
|
|
2475
|
-
let reverse = 1;
|
|
2476
|
-
if (maxData < 0 && !options?.acceptNegative) {
|
|
2477
|
-
throw new Error("maxData is less than 0 and acceptNegative is false");
|
|
2478
|
-
}
|
|
2479
|
-
if (options?.acceptNegative) {
|
|
2480
|
-
if (isNil(options.minNegative)) {
|
|
2481
|
-
throw new Error("minNegative is required when acceptNegative is true");
|
|
2482
|
-
}
|
|
2483
|
-
if (maxData < options.minNegative) {
|
|
2484
|
-
throw new Error("maxData is less than minNegative");
|
|
2485
|
-
}
|
|
2486
|
-
if (options.minNegative >= 0) {
|
|
2487
|
-
throw new Error("minNegative must be negative");
|
|
2488
|
-
}
|
|
2539
|
+
const getSmartAxisScale = (originalMaxData, options) => {
|
|
2540
|
+
let maxData = originalMaxData;
|
|
2541
|
+
validateInputs(maxData, options);
|
|
2542
|
+
const minTickCount = options?.minTickCount || DEFAULT_MIN_TICK_COUNT;
|
|
2543
|
+
const maxTickCount = options?.maxTickCount || DEFAULT_MAX_TICK_COUNT;
|
|
2544
|
+
let scaleDirection = 1;
|
|
2545
|
+
let rangeDistance = maxData;
|
|
2546
|
+
if (options?.acceptNegative && !isNil(options.minNegative)) {
|
|
2489
2547
|
if (maxData === 0) {
|
|
2490
2548
|
maxData = -1;
|
|
2491
2549
|
}
|
|
2492
|
-
stepCandidates.unshift(...stepCandidates.map(item => item * -1));
|
|
2493
2550
|
if (maxData <= 0) {
|
|
2494
|
-
|
|
2551
|
+
rangeDistance = options.minNegative;
|
|
2495
2552
|
}
|
|
2496
|
-
if (
|
|
2497
|
-
|
|
2553
|
+
if (rangeDistance > NEGATIVE_THRESHOLD) {
|
|
2554
|
+
rangeDistance = FALLBACK_NEGATIVE_VALUE;
|
|
2498
2555
|
}
|
|
2499
2556
|
if (maxData > 0) {
|
|
2500
|
-
|
|
2501
|
-
|
|
2557
|
+
rangeDistance = maxData + Math.abs(options.minNegative);
|
|
2558
|
+
scaleDirection = -1;
|
|
2502
2559
|
}
|
|
2503
2560
|
}
|
|
2504
|
-
if (Math.abs(
|
|
2505
|
-
|
|
2561
|
+
if (Math.abs(rangeDistance) < MIN_DISTANCE_TO_ZERO) {
|
|
2562
|
+
rangeDistance = MIN_DISTANCE_TO_ZERO;
|
|
2506
2563
|
}
|
|
2564
|
+
const stepCandidates = getStepCandidates(maxData, minTickCount, options?.minNegative || 0, options?.acceptStepIsTypeFloat, options?.stepCandidates, options?.acceptNegative);
|
|
2507
2565
|
for (const step of stepCandidates) {
|
|
2508
|
-
let tickCount = Math.abs(Math.ceil(
|
|
2509
|
-
let maxValue = maxData <= 0 ? 0 : step * tickCount *
|
|
2566
|
+
let tickCount = Math.abs(Math.ceil(rangeDistance / step)) + (scaleDirection === -1 ? 2 : 1);
|
|
2567
|
+
let maxValue = maxData <= 0 ? 0 : step * tickCount * scaleDirection;
|
|
2510
2568
|
let minValue = 0;
|
|
2511
2569
|
if (tickCount >= minTickCount && tickCount <= maxTickCount) {
|
|
2512
2570
|
if (maxData < maxValue) {
|
|
@@ -2517,8 +2575,9 @@ const getSmartAxisScale = (maxData, options) => {
|
|
|
2517
2575
|
tick++;
|
|
2518
2576
|
}
|
|
2519
2577
|
}
|
|
2520
|
-
|
|
2521
|
-
|
|
2578
|
+
const maxValuePositive = maxValue - Math.abs(minValue);
|
|
2579
|
+
tickCount = maxValuePositive - originalMaxData > Math.abs(step) && tickCount - 1 >= minTickCount ? tickCount - 1 : tickCount;
|
|
2580
|
+
maxValue = (step * tickCount * scaleDirection) - (minValue * scaleDirection);
|
|
2522
2581
|
return {
|
|
2523
2582
|
stepSize: Math.abs(step),
|
|
2524
2583
|
max: maxValue,
|
|
@@ -2528,9 +2587,9 @@ const getSmartAxisScale = (maxData, options) => {
|
|
|
2528
2587
|
}
|
|
2529
2588
|
}
|
|
2530
2589
|
}
|
|
2531
|
-
let step = Math.ceil(
|
|
2590
|
+
let step = Math.ceil(rangeDistance / minTickCount) || 1;
|
|
2532
2591
|
let maxValue = step * minTickCount;
|
|
2533
|
-
if (
|
|
2592
|
+
if (rangeDistance === maxValue) {
|
|
2534
2593
|
step = step + Math.ceil(step / 10);
|
|
2535
2594
|
maxValue = step * minTickCount;
|
|
2536
2595
|
}
|
|
@@ -2541,6 +2600,89 @@ const getSmartAxisScale = (maxData, options) => {
|
|
|
2541
2600
|
tickAmount: minTickCount
|
|
2542
2601
|
};
|
|
2543
2602
|
};
|
|
2603
|
+
// Cache cho các giá trị lũy thừa 10 để tối ưu hiệu suất
|
|
2604
|
+
const POWER_CACHE = new Map();
|
|
2605
|
+
/**
|
|
2606
|
+
* Tạo danh sách các step candidates cho việc tính toán scale
|
|
2607
|
+
* @param maxData - Giá trị dữ liệu tối đa
|
|
2608
|
+
* @param minStep - Số bước tối thiểu
|
|
2609
|
+
* @param minNegative - Giá trị âm tối thiểu
|
|
2610
|
+
* @param acceptStepIsTypeFloat - Có chấp nhận step thập phân không
|
|
2611
|
+
* @param stepCandidatesByOptions - Step candidates do người dùng cung cấp
|
|
2612
|
+
* @param includeNegativeSteps - Có bao gồm các step âm không
|
|
2613
|
+
* @returns Danh sách các step candidates
|
|
2614
|
+
*/
|
|
2615
|
+
const getStepCandidates = (maxData, minStep, minNegative, acceptStepIsTypeFloat = false, stepCandidatesByOptions, includeNegativeSteps = false) => {
|
|
2616
|
+
// Nếu có step candidates tùy chỉnh, sử dụng chúng
|
|
2617
|
+
if (stepCandidatesByOptions && stepCandidatesByOptions.length > 0) {
|
|
2618
|
+
return stepCandidatesByOptions;
|
|
2619
|
+
}
|
|
2620
|
+
const stepCandidates = new Array();
|
|
2621
|
+
const maxValueStep = Math.abs(Math.max(maxData, Math.abs(minNegative || 0))) / minStep;
|
|
2622
|
+
// Tạo step candidates dựa trên lũy thừa của 10
|
|
2623
|
+
for (let powNumber = 0; powNumber <= MAX_POW_NUMBER; powNumber++) {
|
|
2624
|
+
// Sử dụng cache để tối ưu hiệu suất
|
|
2625
|
+
if (!POWER_CACHE.has(powNumber)) {
|
|
2626
|
+
POWER_CACHE.set(powNumber, Math.pow(10, powNumber));
|
|
2627
|
+
}
|
|
2628
|
+
const powValue = POWER_CACHE.get(powNumber);
|
|
2629
|
+
for (let templateNumber = 1; templateNumber < MAX_TEMPLATE_NUMBER; templateNumber++) {
|
|
2630
|
+
// Thêm step thập phân nếu được chấp nhận
|
|
2631
|
+
if (acceptStepIsTypeFloat) {
|
|
2632
|
+
const step = powValue * (((templateNumber - 1) * 2 + 1) / 2);
|
|
2633
|
+
stepCandidates.push(step);
|
|
2634
|
+
}
|
|
2635
|
+
const step = powValue * templateNumber;
|
|
2636
|
+
stepCandidates.push(step);
|
|
2637
|
+
// Dừng khi step đã đủ lớn
|
|
2638
|
+
if (step >= maxValueStep) {
|
|
2639
|
+
checkAndSetNegativeSteps(stepCandidates, includeNegativeSteps, minNegative);
|
|
2640
|
+
return stepCandidates;
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
checkAndSetNegativeSteps(stepCandidates, includeNegativeSteps, minNegative);
|
|
2645
|
+
return stepCandidates;
|
|
2646
|
+
};
|
|
2647
|
+
/**
|
|
2648
|
+
* Kiểm tra và thêm các step âm vào danh sách candidates nếu cần
|
|
2649
|
+
* @param stepCandidates - Danh sách step candidates hiện tại
|
|
2650
|
+
* @param acceptNegative - Có chấp nhận giá trị âm không
|
|
2651
|
+
* @param minNegative - Giá trị âm tối thiểu
|
|
2652
|
+
*/
|
|
2653
|
+
const checkAndSetNegativeSteps = (stepCandidates, acceptNegative, minNegative) => {
|
|
2654
|
+
if (acceptNegative && minNegative < 0) {
|
|
2655
|
+
// Tạo các step âm và thêm vào đầu danh sách
|
|
2656
|
+
const negativeSteps = [...stepCandidates].map(step => -step);
|
|
2657
|
+
stepCandidates.unshift(...negativeSteps);
|
|
2658
|
+
}
|
|
2659
|
+
};
|
|
2660
|
+
/**
|
|
2661
|
+
* Kiểm tra tính hợp lệ của các tham số đầu vào
|
|
2662
|
+
* @param maxData - Giá trị dữ liệu tối đa
|
|
2663
|
+
* @param options - Các tùy chọn cấu hình
|
|
2664
|
+
* @throws {Error} Khi các tham số không hợp lệ
|
|
2665
|
+
*/
|
|
2666
|
+
const validateInputs = (maxData, options) => {
|
|
2667
|
+
// Kiểm tra maxData âm khi không chấp nhận giá trị âm
|
|
2668
|
+
if (maxData < 0 && !options?.acceptNegative) {
|
|
2669
|
+
throw new Error("maxData is less than 0 and acceptNegative is false");
|
|
2670
|
+
}
|
|
2671
|
+
if (options?.acceptNegative) {
|
|
2672
|
+
// Kiểm tra minNegative có được cung cấp không
|
|
2673
|
+
if (isNil(options.minNegative)) {
|
|
2674
|
+
throw new Error("minNegative is required when acceptNegative is true");
|
|
2675
|
+
}
|
|
2676
|
+
// Kiểm tra maxData phải >= minNegative
|
|
2677
|
+
if (maxData < options.minNegative) {
|
|
2678
|
+
throw new Error("maxData is less than minNegative");
|
|
2679
|
+
}
|
|
2680
|
+
// Kiểm tra minNegative phải là số âm
|
|
2681
|
+
if (options.minNegative >= 0) {
|
|
2682
|
+
throw new Error("minNegative must be negative");
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
};
|
|
2544
2686
|
|
|
2545
2687
|
/**
|
|
2546
2688
|
* Generated bundle index. Do not edit.
|