@shohojdhara/atomix 0.4.5 → 0.4.7

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.
Files changed (54) hide show
  1. package/dist/atomix.css +70 -33
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +2 -2
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/charts.d.ts +93 -109
  6. package/dist/charts.js +273 -371
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.js +183 -184
  9. package/dist/core.js.map +1 -1
  10. package/dist/forms.js +183 -184
  11. package/dist/forms.js.map +1 -1
  12. package/dist/heavy.js +183 -184
  13. package/dist/heavy.js.map +1 -1
  14. package/dist/index.d.ts +7 -51
  15. package/dist/index.esm.js +281 -470
  16. package/dist/index.esm.js.map +1 -1
  17. package/dist/index.js +287 -476
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.min.js +1 -1
  20. package/dist/index.min.js.map +1 -1
  21. package/package.json +1 -1
  22. package/src/components/AtomixGlass/AtomixGlass.tsx +60 -38
  23. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +6 -35
  24. package/src/components/AtomixGlass/glass-utils.ts +27 -14
  25. package/src/components/AtomixGlass/stories/Overview.stories.tsx +19 -21
  26. package/src/components/AtomixGlass/stories/Playground.stories.tsx +1162 -515
  27. package/src/components/AtomixGlass/stories/shared-components.tsx +11 -3
  28. package/src/components/Chart/BubbleChart.tsx +6 -2
  29. package/src/components/Chart/Chart.stories.tsx +108 -96
  30. package/src/components/Chart/ChartToolbar.tsx +6 -4
  31. package/src/components/Chart/ChartTooltip.tsx +5 -4
  32. package/src/components/Chart/GaugeChart.tsx +20 -12
  33. package/src/components/Chart/HeatmapChart.tsx +53 -23
  34. package/src/components/Chart/TreemapChart.tsx +44 -15
  35. package/src/components/Chart/index.ts +0 -2
  36. package/src/components/Chart/types.ts +4 -4
  37. package/src/components/Navigation/Navbar/Navbar.tsx +13 -5
  38. package/src/components/index.ts +0 -1
  39. package/src/lib/composables/index.ts +1 -2
  40. package/src/lib/composables/useAtomixGlass.ts +246 -222
  41. package/src/lib/composables/useAtomixGlassStyles.ts +46 -23
  42. package/src/lib/constants/components.ts +3 -1
  43. package/src/styles/01-settings/_settings.chart.scss +13 -13
  44. package/src/styles/06-components/_components.atomix-glass.scss +45 -20
  45. package/src/styles/06-components/_components.chart.scss +23 -5
  46. package/src/components/Chart/AnimatedChart.tsx +0 -230
  47. package/src/lib/composables/atomix-glass/useGlassBackgroundDetection.ts +0 -329
  48. package/src/lib/composables/atomix-glass/useGlassCornerRadius.ts +0 -82
  49. package/src/lib/composables/atomix-glass/useGlassMouseTracking.ts +0 -153
  50. package/src/lib/composables/atomix-glass/useGlassOverLight.ts +0 -198
  51. package/src/lib/composables/atomix-glass/useGlassState.ts +0 -112
  52. package/src/lib/composables/atomix-glass/useGlassTransforms.ts +0 -160
  53. package/src/lib/composables/glass-styles.ts +0 -302
  54. package/src/lib/composables/useGlassContainer.ts +0 -177
@@ -75,16 +75,18 @@ export const BackgroundWrapper: React.FC<BackgroundWrapperProps> = ({
75
75
 
76
76
  const bgStyle = {
77
77
  backgroundImage: bgImage ? `url(${bgImage})` : undefined,
78
+ backgroundSize: 'cover',
79
+ backgroundPosition: 'center',
80
+ backgroundRepeat: 'no-repeat',
78
81
  height,
79
82
  width,
80
83
  borderRadius,
81
- padding,
82
84
  ...style,
83
85
  };
84
86
 
85
87
  return (
86
88
  <div
87
- className={`u-relative u-overflow-hidden ${className}`}
89
+ className={`u-relative u-overflow-hidden u-flex u-items-center u-justify-center ${className}`}
88
90
  style={bgStyle}
89
91
  aria-hidden={ariaHidden}
90
92
  >
@@ -94,10 +96,16 @@ export const BackgroundWrapper: React.FC<BackgroundWrapperProps> = ({
94
96
  style={{
95
97
  backgroundColor: overlayColor,
96
98
  opacity: overlayOpacity,
99
+ padding,
97
100
  }}
98
101
  />
99
102
  )}
100
- <div className="u-relative u-z-10">{children}</div>
103
+ <div
104
+ className="u-relative u-z-10 u-w-100 u-h-100 u-flex u-items-center u-justify-center"
105
+ style={{ padding }}
106
+ >
107
+ {children}
108
+ </div>
101
109
  </div>
102
110
  );
103
111
  };
@@ -126,8 +126,12 @@ const BubbleChart = memo(
126
126
  minBubbleSize + ((bubble.size - minSize) / sizeRange) * (maxBubbleSize - minBubbleSize);
127
127
 
128
128
  // Calculate position
129
- const x = scales.padding.left + (bubble.x / 100) * scales.innerWidth;
130
- const y = scales.padding.top + scales.innerHeight - (bubble.y / 100) * scales.innerHeight;
129
+ // Ensure bubbles don't get cut off by adding padding equal to maxBubbleSize
130
+ const effectiveWidth = scales.innerWidth - maxBubbleSize * 2;
131
+ const effectiveHeight = scales.innerHeight - maxBubbleSize * 2;
132
+
133
+ const x = scales.padding.left + maxBubbleSize + (bubble.x / 100) * effectiveWidth;
134
+ const y = scales.padding.top + maxBubbleSize + effectiveHeight - (bubble.y / 100) * effectiveHeight;
131
135
 
132
136
  // Determine color
133
137
  let bubbleColor = bubble.color;
@@ -1,7 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { fn } from '@storybook/test';
3
- import React from 'react';
4
- import { useState } from 'react';
3
+ import React, { useMemo, useState } from 'react';
5
4
  import { Container, Grid, GridCol } from '../../layouts/Grid';
6
5
  import { Badge } from '../Badge';
7
6
  import { Button } from '../Button';
@@ -12,7 +11,6 @@ import { SideMenuItem } from '../Navigation/SideMenu/SideMenuItem';
12
11
  import { SideMenuList } from '../Navigation/SideMenu/SideMenuList';
13
12
 
14
13
  import {
15
- AnimatedChart,
16
14
  AreaChart,
17
15
  BarChart,
18
16
  BubbleChart,
@@ -123,28 +121,13 @@ Charts provide a comprehensive charting library with 20+ chart types including l
123
121
  defaultValue: { summary: '-' },
124
122
  },
125
123
  },
126
- width: {
127
- control: 'text',
128
- description: 'Width of the chart',
129
- table: {
130
- type: { summary: 'string | number' },
131
- defaultValue: { summary: '100%' },
132
- },
133
- },
134
- height: {
135
- control: 'text',
136
- description: 'Height of the chart',
137
- table: {
138
- type: { summary: 'string | number' },
139
- defaultValue: { summary: '400px' },
140
- },
141
- },
124
+
142
125
  glass: {
143
126
  control: 'boolean',
144
127
  description: 'Whether to apply glass effect',
145
128
  table: {
146
129
  type: { summary: 'boolean' },
147
- defaultValue: { summary: false },
130
+ defaultValue: { summary: 'false' },
148
131
  },
149
132
  },
150
133
  showToolbar: {
@@ -152,7 +135,7 @@ Charts provide a comprehensive charting library with 20+ chart types including l
152
135
  description: 'Whether to show the toolbar',
153
136
  table: {
154
137
  type: { summary: 'boolean' },
155
- defaultValue: { summary: false },
138
+ defaultValue: { summary: 'false' },
156
139
  },
157
140
  },
158
141
  config: {
@@ -163,14 +146,10 @@ Charts provide a comprehensive charting library with 20+ chart types including l
163
146
  defaultValue: { summary: '{}' },
164
147
  },
165
148
  },
166
- onPointClick: {
167
- action: 'point clicked',
149
+ onDataPointClick: {
150
+ action: 'data point clicked',
168
151
  description: 'Callback when a data point is clicked',
169
152
  },
170
- onPointHover: {
171
- action: 'point hovered',
172
- description: 'Callback when hovering over a data point',
173
- },
174
153
  },
175
154
  } satisfies Meta<typeof Chart>;
176
155
 
@@ -208,10 +187,10 @@ export const GlassVariant: Story = {
208
187
  >
209
188
  <Container>
210
189
  <Grid>
211
- <GridCol col={12}>
190
+ <GridCol xs={12}>
212
191
  <h2 style={{ color: 'white', marginBottom: '2rem' }}>Chart Glass Variant</h2>
213
192
  </GridCol>
214
- <GridCol col={12} md={6}>
193
+ <GridCol xs={12} md={6} className="u-mb-4">
215
194
  <LineChart
216
195
  {...args}
217
196
  title="Sales Performance"
@@ -225,7 +204,7 @@ export const GlassVariant: Story = {
225
204
  }}
226
205
  />
227
206
  </GridCol>
228
- <GridCol col={12} md={6}>
207
+ <GridCol xs={12} md={6}>
229
208
  <BarChart
230
209
  {...args}
231
210
  title="Revenue by Month"
@@ -239,10 +218,10 @@ export const GlassVariant: Story = {
239
218
  }}
240
219
  />
241
220
  </GridCol>
242
- <GridCol col={12} md={6}>
221
+ <GridCol xs={12} md={6}>
243
222
  <PieChart title="Market Share" data={sampleData} glass={true} showToolbar={true} />
244
223
  </GridCol>
245
- <GridCol col={12} md={6}>
224
+ <GridCol xs={12} md={6}>
246
225
  <AreaChart
247
226
  title="Growth Trend"
248
227
  subtitle="Year over year"
@@ -261,8 +240,7 @@ export const GlassVariant: Story = {
261
240
  );
262
241
  },
263
242
  args: {
264
- onPointClick: fn(),
265
- onPointHover: fn(),
243
+ onDataPointClick: fn(),
266
244
  },
267
245
  parameters: {
268
246
  docs: {
@@ -375,7 +353,7 @@ const generateFunnelData = () => {
375
353
 
376
354
  return stages.map(stage => ({
377
355
  ...stage,
378
- percentage: ((stage.value / stages[0].value) * 100).toFixed(1),
356
+ percentage: Number(((stage.value / stages[0].value) * 100).toFixed(1)),
379
357
  }));
380
358
  };
381
359
 
@@ -391,7 +369,9 @@ export const ChartGallery: Story = {
391
369
  const [selectedType, setSelectedType] = useState('line');
392
370
  const [animated, setAnimated] = useState(true);
393
371
  const [showLegend, setShowLegend] = useState(true);
372
+ const [glassEffect, setGlassEffect] = useState(false);
394
373
  const [dataPoints, setDataPoints] = useState(12);
374
+ const [dataSeed, setDataSeed] = useState(0);
395
375
 
396
376
  const chartTypes = [
397
377
  { key: 'line', icon: 'TrendUp', label: 'Line', desc: 'Trends over time' },
@@ -413,26 +393,55 @@ export const ChartGallery: Story = {
413
393
  { key: 'waterfall', icon: 'Drop', label: 'Waterfall', desc: 'Cumulative flow' },
414
394
  { key: 'funnel', icon: 'Funnel', label: 'Funnel', desc: 'Process stages' },
415
395
  { key: 'treemap', icon: 'Tree', label: 'Treemap', desc: 'Hierarchical data' },
416
- { key: 'animated', icon: 'Sparkle', label: 'Animated', desc: 'Motion graphics' },
417
396
  { key: 'multiaxis', icon: 'ChartLineUp', label: 'Multi-axis', desc: 'Multiple scales' },
418
397
  ];
419
398
 
420
- // Generate dynamic data based on dataPoints
421
- const dynamicDatasets = [
422
- { label: 'Sales', data: generateData(dataPoints), color: 'var(--atomix-primary)' },
423
- { label: 'Revenue', data: generateData(dataPoints), color: 'var(--atomix-success)' },
424
- { label: 'Profit', data: generateData(dataPoints), color: 'var(--atomix-warning)' },
425
- ];
399
+ // Generate dynamic data based on dataPoints and dataSeed
400
+ const dynamicDatasets = useMemo(
401
+ () => [
402
+ { label: 'Sales', data: generateData(dataPoints), color: 'var(--atomix-primary)' },
403
+ { label: 'Revenue', data: generateData(dataPoints), color: 'var(--atomix-success)' },
404
+ { label: 'Profit', data: generateData(dataPoints), color: 'var(--atomix-warning)' },
405
+ // eslint-disable-next-line react-hooks/exhaustive-deps
406
+ ],
407
+ [dataPoints, dataSeed]
408
+ );
409
+
410
+ const pieDatasets = useMemo(
411
+ () => [
412
+ { label: 'Distribution', data: generateData(Math.min(dataPoints, 8)) },
413
+ // eslint-disable-next-line react-hooks/exhaustive-deps
414
+ ],
415
+ [dataPoints, dataSeed]
416
+ );
417
+
418
+ // eslint-disable-next-line react-hooks/exhaustive-deps
419
+ const bubbleData = useMemo(() => generateBubbleData(dataPoints), [dataPoints, dataSeed]);
420
+ // eslint-disable-next-line react-hooks/exhaustive-deps
421
+ const heatmapData = useMemo(() => generateHeatmapData(), [dataSeed]);
422
+ // eslint-disable-next-line react-hooks/exhaustive-deps
423
+ const candlestickData = useMemo(
424
+ () => generateCandlestickData(dataPoints),
425
+ [dataPoints, dataSeed]
426
+ );
427
+ // eslint-disable-next-line react-hooks/exhaustive-deps
428
+ const funnelData = useMemo(() => generateFunnelData(), [dataSeed]);
429
+ // eslint-disable-next-line react-hooks/exhaustive-deps
430
+ const treemapData = useMemo(
431
+ () => generateTreemapData(Math.min(dataPoints, 20)),
432
+ [dataPoints, dataSeed]
433
+ );
426
434
 
427
435
  const renderChart = () => {
428
436
  const commonProps = {
429
437
  title: `${chartTypes.find(t => t.key === selectedType)?.label} Chart`,
430
438
  config: { showLegend, animate: animated },
431
439
  showToolbar: true,
440
+ glass: glassEffect,
432
441
  enableFullscreen: true,
433
442
  enableExport: true,
434
443
  enableRefresh: true,
435
- onRefresh: () => console.log('Story: Refresh clicked'),
444
+ onRefresh: () => setDataSeed(s => s + 1), // Regenerate on refresh button click
436
445
  onExport: (format: string) => console.log('Story: Export clicked', format),
437
446
  onFullscreen: (isFullscreen: boolean) =>
438
447
  console.log('Story: Fullscreen toggled', isFullscreen),
@@ -440,39 +449,58 @@ export const ChartGallery: Story = {
440
449
 
441
450
  // Custom toolbar with chart controls
442
451
  const customToolbar = (
443
- <div className="u-flex u-gap-2 u-items-center u-flex-wrap u-mb-5">
444
- <div className="u-border-start u-ps-2 u-flex u-gap-2 u-items-center">
445
- {/* Data Points Control */}
446
- <div className="u-flex u-items-center u-gap-1">
447
- <Icon name="Database" size="sm" />
452
+ <div className="u-flex u-gap-4 u-items-center u-flex-wrap u-mb-5 u-p-3">
453
+ {/* Data Control Group */}
454
+ <div className="u-flex u-items-center u-gap-3">
455
+ <div className="u-flex u-items-center u-gap-2">
456
+ <Icon name="Database" size="sm" className="u-text-muted" />
448
457
  <input
449
458
  type="range"
450
459
  min="4"
451
460
  max="20"
452
461
  value={dataPoints}
453
462
  onChange={e => setDataPoints(Number(e.target.value))}
454
- className="u-w-16"
463
+ className="u-w-25"
455
464
  title="Adjust data points"
456
465
  />
457
466
  <Badge label={dataPoints.toString()} variant="info" size="sm" />
458
467
  </div>
459
468
 
460
- {/* Legend Toggle */}
461
469
  <Button
462
470
  size="sm"
463
- variant={showLegend ? 'info' : 'secondary'}
471
+ variant="secondary"
472
+ onClick={() => setDataSeed(s => s + 1)}
473
+ iconName="ArrowsClockwise"
474
+ label="Regenerate"
475
+ />
476
+ </div>
477
+
478
+ <div className="u-flex-1" />
479
+
480
+ {/* View Toggles Group */}
481
+ <div className="u-flex u-gap-2">
482
+ <Button
483
+ size="sm"
484
+ variant={showLegend ? 'primary' : 'secondary'}
464
485
  onClick={() => setShowLegend(!showLegend)}
465
- icon={<Icon name="List" />}
466
- label={`${showLegend ? 'Hide' : 'Show'} legend`}
486
+ iconName="List"
487
+ label="Legend"
467
488
  />
468
489
 
469
- {/* Animation Toggle */}
470
490
  <Button
471
491
  size="sm"
472
- variant={animated ? 'success' : 'secondary'}
492
+ variant={animated ? 'primary' : 'secondary'}
473
493
  onClick={() => setAnimated(!animated)}
474
- icon={<Icon name="Sparkle" />}
475
- label={`${animated ? 'Disable' : 'Enable'} animations`}
494
+ iconName="Sparkle"
495
+ label="Animations"
496
+ />
497
+
498
+ <Button
499
+ size="sm"
500
+ variant={glassEffect ? 'primary' : 'secondary'}
501
+ onClick={() => setGlassEffect(!glassEffect)}
502
+ iconName="Drop"
503
+ label="Glass"
476
504
  />
477
505
  </div>
478
506
  </div>
@@ -481,93 +509,84 @@ export const ChartGallery: Story = {
481
509
  switch (selectedType) {
482
510
  case 'line':
483
511
  return (
484
- <div>
512
+ <div className="u-h-100 u-flex u-flex-column">
485
513
  {customToolbar}
486
514
  <LineChart datasets={dynamicDatasets} {...commonProps} />
487
515
  </div>
488
516
  );
489
517
  case 'area':
490
518
  return (
491
- <div>
519
+ <div className="u-h-100 u-flex u-flex-column">
492
520
  {customToolbar}
493
521
  <AreaChart datasets={dynamicDatasets} {...commonProps} />
494
522
  </div>
495
523
  );
496
524
  case 'bar':
497
525
  return (
498
- <div>
526
+ <div className="u-h-100 u-flex u-flex-column">
499
527
  {customToolbar}
500
528
  <BarChart datasets={dynamicDatasets} {...commonProps} />
501
529
  </div>
502
530
  );
503
531
  case 'pie':
504
532
  return (
505
- <div>
533
+ <div className="u-h-100 u-flex u-flex-column">
506
534
  {customToolbar}
507
- <PieChart
508
- datasets={[{ label: 'Distribution', data: generateData(6) }]}
509
- {...commonProps}
510
- />
535
+ <PieChart datasets={pieDatasets} {...commonProps} />
511
536
  </div>
512
537
  );
513
538
  case 'donut':
514
539
  return (
515
- <div>
540
+ <div className="u-h-100 u-flex u-flex-column">
516
541
  {customToolbar}
517
- <DonutChart
518
- datasets={[{ label: 'Distribution', data: generateData(6) }]}
519
- {...commonProps}
520
- />
542
+ <DonutChart datasets={pieDatasets} {...commonProps} />
521
543
  </div>
522
544
  );
523
545
  case 'scatter':
524
546
  return (
525
- <div>
547
+ <div className="u-h-100 u-flex u-flex-column">
526
548
  {customToolbar}
527
549
  <ScatterChart datasets={dynamicDatasets} {...commonProps} />
528
550
  </div>
529
551
  );
530
552
  case 'radar':
531
553
  return (
532
- <div>
554
+ <div className="u-h-100 u-flex u-flex-column">
533
555
  {customToolbar}
534
556
  <RadarChart datasets={dynamicDatasets} {...commonProps} />
535
557
  </div>
536
558
  );
537
559
  case 'bubble':
538
560
  return (
539
- <div>
561
+ <div className="u-h-100 u-flex u-flex-column">
540
562
  {customToolbar}
541
- <BubbleChart bubbleData={generateBubbleData(dataPoints)} {...commonProps} />
563
+ <BubbleChart bubbleData={bubbleData} {...commonProps} />
542
564
  </div>
543
565
  );
544
566
  case 'gauge':
545
567
  return (
546
- <div>
568
+ <div className="u-h-100 u-flex u-flex-column">
547
569
  {customToolbar}
548
570
  <GaugeChart value={75} max={100} {...commonProps} />
549
571
  </div>
550
572
  );
551
573
  case 'heatmap':
552
574
  return (
553
- <div>
575
+ <div className="u-h-100 u-flex u-flex-column">
554
576
  {customToolbar}
555
- <HeatmapChart data={generateHeatmapData()} {...commonProps} />
577
+ <HeatmapChart data={heatmapData} {...commonProps} />
556
578
  </div>
557
579
  );
558
580
  case 'candlestick':
559
581
  return (
560
- <div>
582
+ <div className="u-h-100 u-flex u-flex-column">
561
583
  {customToolbar}
562
- <CandlestickChart
563
- candlestickData={generateCandlestickData(dataPoints)}
564
- {...commonProps}
565
- />
584
+ <CandlestickChart candlestickData={candlestickData} {...commonProps} />
566
585
  </div>
567
586
  );
568
587
  case 'waterfall':
569
588
  return (
570
- <div>
589
+ <div className="u-h-100 u-flex u-flex-column">
571
590
  {customToolbar}
572
591
  <WaterfallChart
573
592
  waterfallData={[
@@ -583,10 +602,10 @@ export const ChartGallery: Story = {
583
602
  );
584
603
  case 'funnel':
585
604
  return (
586
- <div>
605
+ <div className="u-h-100 u-flex u-flex-column">
587
606
  {customToolbar}
588
607
  <FunnelChart
589
- funnelData={generateFunnelData()}
608
+ funnelData={funnelData}
590
609
  funnelOptions={{
591
610
  showLabels: true,
592
611
  showValues: true,
@@ -601,10 +620,10 @@ export const ChartGallery: Story = {
601
620
  );
602
621
  case 'treemap':
603
622
  return (
604
- <div>
623
+ <div className="u-h-100 u-flex u-flex-column">
605
624
  {customToolbar}
606
625
  <TreemapChart
607
- data={generateTreemapData(Math.min(dataPoints, 20))}
626
+ data={treemapData}
608
627
  algorithm="squarified"
609
628
  colorConfig={{ scheme: 'category' }}
610
629
  labelConfig={{
@@ -617,16 +636,9 @@ export const ChartGallery: Story = {
617
636
  />
618
637
  </div>
619
638
  );
620
- case 'animated':
621
- return (
622
- <div>
623
- {customToolbar}
624
- <AnimatedChart datasets={dynamicDatasets} {...commonProps} />
625
- </div>
626
- );
627
639
  case 'multiaxis':
628
640
  return (
629
- <div>
641
+ <div className="u-h-100 u-flex u-flex-column">
630
642
  {customToolbar}
631
643
  <MultiAxisChart
632
644
  datasets={dynamicDatasets.map((d, i) => ({ ...d, yAxisId: `axis${i}` }))}
@@ -641,7 +653,7 @@ export const ChartGallery: Story = {
641
653
  );
642
654
  default:
643
655
  return (
644
- <div>
656
+ <div className="u-h-100 u-flex u-flex-column">
645
657
  {customToolbar}
646
658
  <LineChart datasets={dynamicDatasets} {...commonProps} />
647
659
  </div>
@@ -1,8 +1,9 @@
1
- import { forwardRef, memo, useCallback, useEffect, useRef, useState } from 'react';
1
+ import { forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { CHART } from '../../lib/constants/components';
3
3
  import { Icon } from '../Icon';
4
4
  import type { PhosphorIconsType } from '../Icon/Icon';
5
5
  import { ChartType } from './types';
6
+ import { Variant } from '../../lib/types/components';
6
7
 
7
8
  export interface ChartToolbarAction {
8
9
  id: string;
@@ -11,7 +12,7 @@ export interface ChartToolbarAction {
11
12
  onClick: () => void;
12
13
  disabled?: boolean;
13
14
  active?: boolean;
14
- variant?: 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'error';
15
+ variant?: Variant;
15
16
  tooltip?: string;
16
17
  shortcut?: string;
17
18
  }
@@ -155,7 +156,7 @@ const ChartToolbar = memo(
155
156
  const settingsButtonRef = useRef<HTMLButtonElement>(null);
156
157
 
157
158
  // Compute effective defaults based on provided groups
158
- const effectiveDefaults =
159
+ const effectiveDefaults = useMemo(() => (
159
160
  groups && groups.length > 0
160
161
  ? {
161
162
  refresh: defaults.refresh ?? true,
@@ -172,7 +173,8 @@ const ChartToolbar = memo(
172
173
  tooltips: defaults.tooltips ?? true,
173
174
  animations: defaults.animations ?? true,
174
175
  }
175
- : defaults;
176
+ : defaults
177
+ ), [groups, defaults]);
176
178
 
177
179
  // Close menus when clicking outside
178
180
  useEffect(() => {
@@ -63,15 +63,16 @@ const ChartTooltip = memo<ChartTooltipProps>(
63
63
  ref={tooltipRef}
64
64
  className="c-chart__tooltip"
65
65
  style={{
66
- left: `${adjustedPosition.x}px`,
67
- top: `${adjustedPosition.y}px`,
66
+ transform: `translate3d(${adjustedPosition.x}px, ${adjustedPosition.y}px, 0)`,
68
67
  opacity: visible ? 1 : 0,
69
68
  visibility: visible ? 'visible' : 'hidden',
70
- transition: 'opacity 0.2s ease, transform 0.2s ease',
71
- transform: 'translateZ(0)',
69
+ transition: 'opacity 0.2s ease',
72
70
  position: 'fixed',
71
+ left: 0,
72
+ top: 0,
73
73
  zIndex: 1000,
74
74
  pointerEvents: 'none',
75
+ willChange: 'transform',
75
76
  }}
76
77
  >
77
78
  {customRenderer ? (
@@ -208,11 +208,13 @@ const GaugeChart = memo(
208
208
  // Create ticks
209
209
  const ticks = [];
210
210
  if (showTicks) {
211
+ const innerRadius = radius * (1 - thickness);
212
+
211
213
  // Major ticks
212
214
  for (let i = 0; i <= majorTicks; i++) {
213
215
  const tickValue = min + (max - min) * (i / majorTicks);
214
216
  const tickAngle = startAngleRad + (i / majorTicks) * angleRange;
215
- const tickRadius = radius * 0.95;
217
+ const tickRadius = innerRadius - 2;
216
218
  const tickLength = radius * 0.05;
217
219
  const x1 = centerX + tickRadius * Math.cos(tickAngle);
218
220
  const y1 = centerY + tickRadius * Math.sin(tickAngle);
@@ -233,8 +235,9 @@ const GaugeChart = memo(
233
235
 
234
236
  // Labels for major ticks
235
237
  if (showMinMaxLabels) {
236
- const labelX = centerX + (tickRadius - tickLength - 10) * Math.cos(tickAngle);
237
- const labelY = centerY + (tickRadius - tickLength - 10) * Math.sin(tickAngle);
238
+ const labelRadius = tickRadius - tickLength - 15;
239
+ const labelX = centerX + labelRadius * Math.cos(tickAngle);
240
+ const labelY = centerY + labelRadius * Math.sin(tickAngle);
238
241
 
239
242
  ticks.push(
240
243
  <text
@@ -242,7 +245,7 @@ const GaugeChart = memo(
242
245
  x={labelX}
243
246
  y={labelY}
244
247
  textAnchor="middle"
245
- dominantBaseline="middle"
248
+ dominantBaseline="central"
246
249
  fontSize="12"
247
250
  fill="var(--atomix-brand-text-emphasis)"
248
251
  >
@@ -253,9 +256,11 @@ const GaugeChart = memo(
253
256
  }
254
257
 
255
258
  // Minor ticks
256
- for (let i = 0; i < majorTicks * minorTicks; i++) {
259
+ for (let i = 0; i <= majorTicks * minorTicks; i++) {
260
+ if (i % minorTicks === 0) continue;
261
+
257
262
  const tickAngle = startAngleRad + (i / (majorTicks * minorTicks)) * angleRange;
258
- const tickRadius = radius * 0.95;
263
+ const tickRadius = innerRadius - 2;
259
264
  const tickLength = radius * 0.025;
260
265
  const x1 = centerX + tickRadius * Math.cos(tickAngle);
261
266
  const y1 = centerY + tickRadius * Math.sin(tickAngle);
@@ -276,19 +281,22 @@ const GaugeChart = memo(
276
281
  }
277
282
  }
278
283
 
284
+ const innerRadius = radius * (1 - thickness);
285
+
279
286
  // Create needle
280
287
  const needle = showNeedle ? (
281
288
  <g>
282
289
  <line
283
290
  x1={centerX}
284
291
  y1={centerY}
285
- x2={centerX + radius * 0.8 * Math.cos(valueAngle)}
286
- y2={centerY + radius * 0.8 * Math.sin(valueAngle)}
292
+ x2={centerX + (innerRadius - 15) * Math.cos(valueAngle)}
293
+ y2={centerY + (innerRadius - 15) * Math.sin(valueAngle)}
287
294
  stroke={needleColor}
288
- strokeWidth="3"
295
+ strokeWidth="4"
289
296
  strokeLinecap="round"
290
297
  />
291
298
  <circle cx={centerX} cy={centerY} r="8" fill={needleColor} />
299
+ <circle cx={centerX} cy={centerY} r="3" fill="var(--atomix-primary-bg, #fff)" />
292
300
  </g>
293
301
  ) : null;
294
302
 
@@ -296,9 +304,9 @@ const GaugeChart = memo(
296
304
  const valueText = showValue ? (
297
305
  <text
298
306
  x={centerX}
299
- y={centerY + 10}
307
+ y={centerY + 35}
300
308
  textAnchor="middle"
301
- fontSize="24"
309
+ fontSize="32"
302
310
  fontWeight="bold"
303
311
  fill="var(--atomix-primary-text-emphasis)"
304
312
  >
@@ -310,7 +318,7 @@ const GaugeChart = memo(
310
318
  const labelText = label ? (
311
319
  <text
312
320
  x={centerX}
313
- y={labelPosition === 'top' ? centerY - radius * 0.7 : centerY + radius * 0.7}
321
+ y={labelPosition === 'top' ? centerY - radius * 0.7 : centerY + radius * 0.7 + 10}
314
322
  textAnchor="middle"
315
323
  fontSize="16"
316
324
  fill="var(--atomix-brand-text-emphasis)"