@libs-ui/utils 0.2.283 → 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 +123 -135
- package/fesm2022/libs-ui-utils.mjs +122 -134
- package/fesm2022/libs-ui-utils.mjs.map +1 -1
- package/get-smart-axis-scale.d.ts +15 -12
- package/package.json +2 -2
|
@@ -2511,8 +2511,6 @@ const convertUrlToFile = (url, fileName) => {
|
|
|
2511
2511
|
});
|
|
2512
2512
|
};
|
|
2513
2513
|
|
|
2514
|
-
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2515
|
-
// Constants for better maintainability
|
|
2516
2514
|
const DEFAULT_MIN_TICK_COUNT = 5;
|
|
2517
2515
|
const DEFAULT_MAX_TICK_COUNT = 10;
|
|
2518
2516
|
const MIN_DISTANCE_TO_ZERO = 3;
|
|
@@ -2520,144 +2518,78 @@ const NEGATIVE_THRESHOLD = -5;
|
|
|
2520
2518
|
const FALLBACK_NEGATIVE_VALUE = -4;
|
|
2521
2519
|
const MAX_POW_NUMBER = 14;
|
|
2522
2520
|
const MAX_TEMPLATE_NUMBER = 10;
|
|
2523
|
-
// Cache for power calculations - shared across function calls for better performance
|
|
2524
|
-
const POWER_CACHE = new Map();
|
|
2525
2521
|
/**
|
|
2526
|
-
*
|
|
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
2527
|
*
|
|
2528
|
-
* @
|
|
2529
|
-
*
|
|
2530
|
-
*
|
|
2531
|
-
*
|
|
2532
|
-
* - minNegative > 0 (only when acceptNegative is true)
|
|
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
|
|
2533
2532
|
*
|
|
2534
|
-
* @
|
|
2535
|
-
*
|
|
2536
|
-
*
|
|
2537
|
-
*
|
|
2538
|
-
*
|
|
2533
|
+
* @example
|
|
2534
|
+
* ```typescript
|
|
2535
|
+
* const result = getSmartAxisScale(100, { minTickCount: 5, maxTickCount: 10 });
|
|
2536
|
+
* // returns { stepSize: 20, max: 120, min: 0, tickAmount: 6 }
|
|
2537
|
+
* ```
|
|
2539
2538
|
*/
|
|
2540
|
-
const getSmartAxisScale = (
|
|
2541
|
-
|
|
2539
|
+
const getSmartAxisScale = (originalMaxData, options) => {
|
|
2540
|
+
let maxData = originalMaxData;
|
|
2542
2541
|
validateInputs(maxData, options);
|
|
2543
|
-
|
|
2544
|
-
const
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
: generateStepCandidates(maxData, minTickCount, minNegative, acceptStepIsTypeFloat);
|
|
2549
|
-
// Remove console.log for production code
|
|
2550
|
-
// console.log(`stepCandidates: `, finalStepCandidates);
|
|
2551
|
-
// Calculate scale parameters
|
|
2552
|
-
const scaleParams = calculateScaleParams(maxData, acceptNegative, minNegative, finalStepCandidates);
|
|
2553
|
-
// Find optimal scale
|
|
2554
|
-
const optimalScale = findOptimalScale(scaleParams, finalStepCandidates, minTickCount, maxTickCount, options);
|
|
2555
|
-
if (optimalScale) {
|
|
2556
|
-
return optimalScale;
|
|
2557
|
-
}
|
|
2558
|
-
// Fallback calculation
|
|
2559
|
-
return calculateFallbackScale(scaleParams.distanceToZero, minTickCount);
|
|
2560
|
-
};
|
|
2561
|
-
/**
|
|
2562
|
-
* Validates input parameters
|
|
2563
|
-
*/
|
|
2564
|
-
function validateInputs(maxData, options) {
|
|
2565
|
-
if (maxData < 0 && !options?.acceptNegative) {
|
|
2566
|
-
throw new Error("maxData is less than 0 and acceptNegative is false");
|
|
2567
|
-
}
|
|
2568
|
-
if (options?.acceptNegative) {
|
|
2569
|
-
if (isNil(options.minNegative)) {
|
|
2570
|
-
throw new Error("minNegative is required when acceptNegative is true");
|
|
2571
|
-
}
|
|
2572
|
-
if (maxData < options.minNegative) {
|
|
2573
|
-
throw new Error("maxData is less than minNegative");
|
|
2574
|
-
}
|
|
2575
|
-
if (options.minNegative >= 0) {
|
|
2576
|
-
throw new Error("minNegative must be negative");
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
}
|
|
2580
|
-
/**
|
|
2581
|
-
* Calculates scale parameters for axis calculation
|
|
2582
|
-
*/
|
|
2583
|
-
function calculateScaleParams(maxData, acceptNegative, minNegative, stepCandidates) {
|
|
2584
|
-
let distanceToZero = maxData;
|
|
2585
|
-
let reverse = 1;
|
|
2586
|
-
let adjustedMaxData = maxData;
|
|
2587
|
-
if (acceptNegative && !isNil(minNegative)) {
|
|
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)) {
|
|
2588
2547
|
if (maxData === 0) {
|
|
2589
|
-
|
|
2590
|
-
}
|
|
2591
|
-
// Add negative step candidates
|
|
2592
|
-
if (stepCandidates) {
|
|
2593
|
-
stepCandidates.unshift(...stepCandidates.map(item => -item));
|
|
2548
|
+
maxData = -1;
|
|
2594
2549
|
}
|
|
2595
|
-
if (
|
|
2596
|
-
|
|
2550
|
+
if (maxData <= 0) {
|
|
2551
|
+
rangeDistance = options.minNegative;
|
|
2597
2552
|
}
|
|
2598
|
-
if (
|
|
2599
|
-
|
|
2553
|
+
if (rangeDistance > NEGATIVE_THRESHOLD) {
|
|
2554
|
+
rangeDistance = FALLBACK_NEGATIVE_VALUE;
|
|
2600
2555
|
}
|
|
2601
|
-
if (
|
|
2602
|
-
|
|
2603
|
-
|
|
2556
|
+
if (maxData > 0) {
|
|
2557
|
+
rangeDistance = maxData + Math.abs(options.minNegative);
|
|
2558
|
+
scaleDirection = -1;
|
|
2604
2559
|
}
|
|
2605
2560
|
}
|
|
2606
|
-
if (Math.abs(
|
|
2607
|
-
|
|
2561
|
+
if (Math.abs(rangeDistance) < MIN_DISTANCE_TO_ZERO) {
|
|
2562
|
+
rangeDistance = MIN_DISTANCE_TO_ZERO;
|
|
2608
2563
|
}
|
|
2609
|
-
|
|
2610
|
-
}
|
|
2611
|
-
/**
|
|
2612
|
-
* Finds optimal scale from step candidates
|
|
2613
|
-
*/
|
|
2614
|
-
function findOptimalScale(scaleParams, stepCandidates, minTickCount, maxTickCount, options) {
|
|
2615
|
-
const { distanceToZero, reverse, adjustedMaxData } = scaleParams;
|
|
2564
|
+
const stepCandidates = getStepCandidates(maxData, minTickCount, options?.minNegative || 0, options?.acceptStepIsTypeFloat, options?.stepCandidates, options?.acceptNegative);
|
|
2616
2565
|
for (const step of stepCandidates) {
|
|
2617
|
-
let tickCount = Math.abs(Math.ceil(
|
|
2618
|
-
let maxValue =
|
|
2566
|
+
let tickCount = Math.abs(Math.ceil(rangeDistance / step)) + (scaleDirection === -1 ? 2 : 1);
|
|
2567
|
+
let maxValue = maxData <= 0 ? 0 : step * tickCount * scaleDirection;
|
|
2619
2568
|
let minValue = 0;
|
|
2620
|
-
if (tickCount >= minTickCount && tickCount <= maxTickCount
|
|
2621
|
-
if (
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2569
|
+
if (tickCount >= minTickCount && tickCount <= maxTickCount) {
|
|
2570
|
+
if (maxData < maxValue) {
|
|
2571
|
+
if (options?.acceptNegative) {
|
|
2572
|
+
let tick = 1;
|
|
2573
|
+
while (!isNil(options.minNegative) && tick <= tickCount && minValue >= options.minNegative) {
|
|
2574
|
+
minValue = tick * step;
|
|
2575
|
+
tick++;
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
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);
|
|
2581
|
+
return {
|
|
2582
|
+
stepSize: Math.abs(step),
|
|
2583
|
+
max: maxValue,
|
|
2584
|
+
min: minValue,
|
|
2585
|
+
tickAmount: tickCount
|
|
2586
|
+
};
|
|
2628
2587
|
}
|
|
2629
|
-
maxValue = (step * tickCount * reverse) - (minValue * reverse);
|
|
2630
|
-
return {
|
|
2631
|
-
stepSize: Math.abs(step),
|
|
2632
|
-
max: maxValue,
|
|
2633
|
-
min: minValue,
|
|
2634
|
-
tickAmount: tickCount
|
|
2635
|
-
};
|
|
2636
|
-
}
|
|
2637
|
-
}
|
|
2638
|
-
return null;
|
|
2639
|
-
}
|
|
2640
|
-
/**
|
|
2641
|
-
* Calculates minimum value for negative acceptance
|
|
2642
|
-
*/
|
|
2643
|
-
function calculateMinValue(step, tickCount, minNegative) {
|
|
2644
|
-
let minValue = 0;
|
|
2645
|
-
if (!isNil(minNegative)) {
|
|
2646
|
-
let tick = 1;
|
|
2647
|
-
while (tick <= tickCount && minValue >= minNegative) {
|
|
2648
|
-
minValue = tick * step;
|
|
2649
|
-
tick++;
|
|
2650
2588
|
}
|
|
2651
2589
|
}
|
|
2652
|
-
|
|
2653
|
-
}
|
|
2654
|
-
/**
|
|
2655
|
-
* Calculates fallback scale when no optimal scale is found
|
|
2656
|
-
*/
|
|
2657
|
-
function calculateFallbackScale(distanceToZero, minTickCount) {
|
|
2658
|
-
let step = Math.ceil(distanceToZero / minTickCount) || 1;
|
|
2590
|
+
let step = Math.ceil(rangeDistance / minTickCount) || 1;
|
|
2659
2591
|
let maxValue = step * minTickCount;
|
|
2660
|
-
if (
|
|
2592
|
+
if (rangeDistance === maxValue) {
|
|
2661
2593
|
step = step + Math.ceil(step / 10);
|
|
2662
2594
|
maxValue = step * minTickCount;
|
|
2663
2595
|
}
|
|
@@ -2667,34 +2599,90 @@ function calculateFallbackScale(distanceToZero, minTickCount) {
|
|
|
2667
2599
|
min: 0,
|
|
2668
2600
|
tickAmount: minTickCount
|
|
2669
2601
|
};
|
|
2670
|
-
}
|
|
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();
|
|
2671
2605
|
/**
|
|
2672
|
-
*
|
|
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
|
|
2673
2614
|
*/
|
|
2674
|
-
|
|
2675
|
-
|
|
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();
|
|
2676
2621
|
const maxValueStep = Math.abs(Math.max(maxData, Math.abs(minNegative || 0))) / minStep;
|
|
2677
|
-
//
|
|
2678
|
-
for (let powNumber = 0; powNumber
|
|
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
|
|
2679
2625
|
if (!POWER_CACHE.has(powNumber)) {
|
|
2680
2626
|
POWER_CACHE.set(powNumber, Math.pow(10, powNumber));
|
|
2681
2627
|
}
|
|
2682
|
-
const
|
|
2628
|
+
const powValue = POWER_CACHE.get(powNumber);
|
|
2683
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
|
|
2684
2631
|
if (acceptStepIsTypeFloat) {
|
|
2685
|
-
const
|
|
2686
|
-
stepCandidates.push(
|
|
2632
|
+
const step = powValue * (((templateNumber - 1) * 2 + 1) / 2);
|
|
2633
|
+
stepCandidates.push(step);
|
|
2687
2634
|
}
|
|
2688
|
-
const step =
|
|
2635
|
+
const step = powValue * templateNumber;
|
|
2689
2636
|
stepCandidates.push(step);
|
|
2690
|
-
//
|
|
2637
|
+
// Dừng khi step đã đủ lớn
|
|
2691
2638
|
if (step >= maxValueStep) {
|
|
2639
|
+
checkAndSetNegativeSteps(stepCandidates, includeNegativeSteps, minNegative);
|
|
2692
2640
|
return stepCandidates;
|
|
2693
2641
|
}
|
|
2694
2642
|
}
|
|
2695
2643
|
}
|
|
2644
|
+
checkAndSetNegativeSteps(stepCandidates, includeNegativeSteps, minNegative);
|
|
2696
2645
|
return stepCandidates;
|
|
2697
|
-
}
|
|
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
|
+
};
|
|
2698
2686
|
|
|
2699
2687
|
/**
|
|
2700
2688
|
* Generated bundle index. Do not edit.
|