@libs-ui/utils 0.2.283 → 0.2.285

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.
@@ -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
- * Calculates smart axis scale for charts
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
- * @description This function can throw errors if:
2529
- * - maxData < 0 and acceptNegative is false
2530
- * - minNegative is undefined and acceptNegative is true
2531
- * - maxData < minNegative (only when acceptNegative is true)
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
- * @throws {Error} Validation errors for invalid input combinations
2535
- * @required Always wrap this function in try-catch and handle exceptions
2536
- * @param maxData - Maximum data value
2537
- * @param options - Configuration options for axis scaling
2538
- * @returns Axis scale configuration
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 = (maxData, options) => {
2541
- // Input validation - fail fast approach
2539
+ const getSmartAxisScale = (originalMaxData, options) => {
2540
+ let maxData = originalMaxData;
2542
2541
  validateInputs(maxData, options);
2543
- // Extract and set defaults
2544
- const { minTickCount = DEFAULT_MIN_TICK_COUNT, maxTickCount = DEFAULT_MAX_TICK_COUNT, stepCandidates = [], acceptNegative = false, minNegative, acceptStepIsTypeFloat = false } = options || {};
2545
- // Generate step candidates if not provided
2546
- const finalStepCandidates = stepCandidates.length > 0
2547
- ? [...stepCandidates]
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
- adjustedMaxData = -1;
2590
- }
2591
- // Add negative step candidates
2592
- if (stepCandidates) {
2593
- stepCandidates.unshift(...stepCandidates.map(item => -item));
2548
+ maxData = -1;
2594
2549
  }
2595
- if (adjustedMaxData <= 0) {
2596
- distanceToZero = minNegative;
2550
+ if (maxData <= 0) {
2551
+ rangeDistance = options.minNegative;
2597
2552
  }
2598
- if (distanceToZero > NEGATIVE_THRESHOLD) {
2599
- distanceToZero = FALLBACK_NEGATIVE_VALUE;
2553
+ if (rangeDistance > NEGATIVE_THRESHOLD) {
2554
+ rangeDistance = FALLBACK_NEGATIVE_VALUE;
2600
2555
  }
2601
- if (adjustedMaxData > 0) {
2602
- distanceToZero = adjustedMaxData + Math.abs(minNegative);
2603
- reverse = -1;
2556
+ if (maxData > 0) {
2557
+ rangeDistance = maxData + Math.abs(options.minNegative);
2558
+ scaleDirection = -1;
2604
2559
  }
2605
2560
  }
2606
- if (Math.abs(distanceToZero) < MIN_DISTANCE_TO_ZERO) {
2607
- distanceToZero = MIN_DISTANCE_TO_ZERO;
2561
+ if (Math.abs(rangeDistance) < MIN_DISTANCE_TO_ZERO) {
2562
+ rangeDistance = MIN_DISTANCE_TO_ZERO;
2608
2563
  }
2609
- return { distanceToZero, reverse, adjustedMaxData };
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(distanceToZero / step)) + (reverse === -1 ? 2 : 1);
2618
- let maxValue = adjustedMaxData <= 0 ? 0 : step * tickCount * reverse;
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 && adjustedMaxData < maxValue) {
2621
- if (options?.acceptNegative) {
2622
- minValue = calculateMinValue(step, tickCount, options.minNegative);
2623
- }
2624
- // Adjust tick count based on distance calculation
2625
- const distanceCheck = distanceToZero - (tickCount - 1) * step;
2626
- if (distanceCheck < -1 * (step / 4)) {
2627
- tickCount -= 1;
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
- return minValue;
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 (distanceToZero === maxValue) {
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
- * Generates step candidates with performance optimizations
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
- function generateStepCandidates(maxData, minStep, minNegative, acceptStepIsTypeFloat) {
2675
- const stepCandidates = [];
2615
+ const getStepCandidates = (maxData, minStep, minNegative, acceptStepIsTypeFloat = false, stepCandidatesByOptions, includeNegativeSteps = false) => {
2616
+ // Nếu 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
- // Pre-populate power cache for better performance
2678
- for (let powNumber = 0; powNumber < MAX_POW_NUMBER; 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 powByTemp = POWER_CACHE.get(powNumber);
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 floatStep = powByTemp * (((templateNumber - 1) * 2 + 1) / 2);
2686
- stepCandidates.push(floatStep);
2632
+ const step = powValue * (((templateNumber - 1) * 2 + 1) / 2);
2633
+ stepCandidates.push(step);
2687
2634
  }
2688
- const step = powByTemp * templateNumber;
2635
+ const step = powValue * templateNumber;
2689
2636
  stepCandidates.push(step);
2690
- // Early termination for performance
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.