awesome-speedometer-gauge 0.1.0
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/README.md +368 -0
- package/dist/cjs/awesome-speedometer-gauge.css +1 -0
- package/dist/cjs/gauge-data.json +12 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/vite.svg +1 -0
- package/dist/esm/awesome-speedometer-gauge.css +1 -0
- package/dist/esm/gauge-data.json +12 -0
- package/dist/esm/index.js +754 -0
- package/dist/esm/vite.svg +1 -0
- package/dist/style.css +32 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# awesome-speedometer-gauge
|
|
2
|
+
|
|
3
|
+
A fully-featured **React KPI speedometer gauge** component. Five visual modes, animated needle/arc, threshold colour bands, dark/light/purple themes, optional live data fetching, tooltip, target marker, and a real-time flash effect — all in a single self-contained card widget.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **5 gauge modes** — `standard` (half-dial with needle), `semicircle` (wide arc), `full` (360° dial), `arc` (270° gradient arc), `multiband` (coloured bands with dot marker)
|
|
10
|
+
- **Animated transitions** — smooth eased needle/arc animation on value changes via `requestAnimationFrame`
|
|
11
|
+
- **Real-time mode** — fast 220ms transitions + a flash effect when the value crosses a threshold band
|
|
12
|
+
- **Threshold colour bands** — supply any number of `{ value, color }` breakpoints; defaults to poor/warning/good traffic-light colours
|
|
13
|
+
- **Target marker** — optional secondary marker at a target value with its own colour
|
|
14
|
+
- **Three built-in themes** — `light`, `dark`, `purple`; all colours driven by CSS custom properties so you can override anything
|
|
15
|
+
- **Tooltip** — hover to reveal value, target, min, and max in a clean floating chip
|
|
16
|
+
- **Live data fetching** — pass an `apiEndpoint` URL and the component fetches, normalises, and merges JSON automatically
|
|
17
|
+
- **`data` prop shortcut** — pass a plain object or array directly to skip the fetch
|
|
18
|
+
- **CSS Module styles** — scoped styles with no global pollution; `style.css` is the only thing you need to import
|
|
19
|
+
- **ESM + CJS dual build** — works in Vite, Next.js, CRA, and any modern bundler
|
|
20
|
+
- **Zero runtime dependencies** beyond React
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install awesome-speedometer-gauge
|
|
28
|
+
# or
|
|
29
|
+
yarn add awesome-speedometer-gauge
|
|
30
|
+
# or
|
|
31
|
+
pnpm add awesome-speedometer-gauge
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
> **Peer dependencies** — React ≥ 18 and ReactDOM ≥ 18 must already be installed.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Importing Styles
|
|
39
|
+
|
|
40
|
+
Import the stylesheet **once** at your app root:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
import 'awesome-speedometer-gauge/style.css';
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
For the full typographic effect, load this Google Font in your `index.html` `<head>`:
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
50
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
51
|
+
<link
|
|
52
|
+
href="https://fonts.googleapis.com/css2?family=Source+Sans+3:wght@400;500;600;700&display=swap"
|
|
53
|
+
rel="stylesheet"
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
> Falls back to `"Segoe UI"` / `system-ui` without the font.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
```jsx
|
|
64
|
+
import { AwesomeSpeedometerGauge } from 'awesome-speedometer-gauge';
|
|
65
|
+
import 'awesome-speedometer-gauge/style.css';
|
|
66
|
+
|
|
67
|
+
export default function App() {
|
|
68
|
+
return (
|
|
69
|
+
<AwesomeSpeedometerGauge
|
|
70
|
+
title="Server CPU"
|
|
71
|
+
value={72}
|
|
72
|
+
min={0}
|
|
73
|
+
max={100}
|
|
74
|
+
units="%"
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Props
|
|
83
|
+
|
|
84
|
+
| Prop | Type | Default | Description |
|
|
85
|
+
|---|---|---|---|
|
|
86
|
+
| `type` | `'standard' \| 'semicircle' \| 'full' \| 'arc' \| 'multiband'` | `'standard'` | Visual mode of the gauge. |
|
|
87
|
+
| `value` | `number` | — | Current value. Clamped to `[min, max]`. Can also come from `data` or `apiEndpoint`. |
|
|
88
|
+
| `min` | `number` | `0` | Minimum scale value. |
|
|
89
|
+
| `max` | `number` | `100` | Maximum scale value. |
|
|
90
|
+
| `target` | `number` | — | Optional target/goal value. Shown as a separate marker when `showTarget` is true. |
|
|
91
|
+
| `thresholds` | `Threshold[]` | auto | Array of `{ value, color, label? }` breakpoints sorted ascending. Defaults to 60/80/100 traffic-light bands. |
|
|
92
|
+
| `showTicks` | `boolean` | `true` | Show tick marks on the gauge arc. |
|
|
93
|
+
| `showValue` | `boolean` | `true` | Show the current value label inside the gauge SVG. |
|
|
94
|
+
| `showTarget` | `boolean` | `false` | Show the target marker and include target in the value label. |
|
|
95
|
+
| `animated` | `boolean` | `true` | Animate value changes with a 500ms eased transition. |
|
|
96
|
+
| `realtime` | `boolean` | `false` | Use a faster 220ms transition and flash the card when the threshold band changes. |
|
|
97
|
+
| `size` | `number` | `220` | SVG canvas size in px (width and height). |
|
|
98
|
+
| `thickness` | `number` | `18` | Arc stroke thickness in px. |
|
|
99
|
+
| `startAngle` | `number` | mode default | Arc start angle in degrees. Each mode has a sensible default. |
|
|
100
|
+
| `endAngle` | `number` | mode default | Arc end angle in degrees. |
|
|
101
|
+
| `units` | `string` | `''` | Unit label appended to the value (e.g. `'%'`, `'rpm'`, `'°C'`). |
|
|
102
|
+
| `title` | `string` | `'KPI Gauge'` | Card heading. |
|
|
103
|
+
| `subtitle` | `string` | `''` | Optional secondary line below the title. |
|
|
104
|
+
| `theme` | `'light' \| 'dark' \| 'purple'` | `'light'` | Built-in colour theme. |
|
|
105
|
+
| `data` | `object \| array` | — | Inline data object (or array whose first element is used). Merged with and overridden by direct props. |
|
|
106
|
+
| `apiEndpoint` | `string` | `'/gauge-data.json'` | URL to fetch gauge data from. Only used when `data` is not provided. |
|
|
107
|
+
| `className` | `string` | — | Additional CSS class on the root card element. |
|
|
108
|
+
| `style` | `object` | — | Inline styles on the root card element. |
|
|
109
|
+
| `onClick` | `(value: number) => void` | — | Called with the current value when the card is clicked. |
|
|
110
|
+
|
|
111
|
+
### `Threshold` object
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
{ value: 60, color: '#ef4444' }, // 0–60 → red
|
|
115
|
+
{ value: 80, color: '#f59e0b' }, // 60–80 → amber
|
|
116
|
+
{ value: 100, color: '#22c55e' }, // 80–100 → green
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Examples
|
|
122
|
+
|
|
123
|
+
### Example 1 — All five modes side by side
|
|
124
|
+
|
|
125
|
+
```jsx
|
|
126
|
+
import { AwesomeSpeedometerGauge } from 'awesome-speedometer-gauge';
|
|
127
|
+
import 'awesome-speedometer-gauge/style.css';
|
|
128
|
+
|
|
129
|
+
var thresholds = [
|
|
130
|
+
{ value: 40, color: '#ef4444' },
|
|
131
|
+
{ value: 70, color: '#f59e0b' },
|
|
132
|
+
{ value: 100, color: '#22c55e' },
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
var modes = ['standard', 'semicircle', 'full', 'arc', 'multiband'];
|
|
136
|
+
|
|
137
|
+
export default function AllModesDemo() {
|
|
138
|
+
return (
|
|
139
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 24, padding: 32, background: '#f1f5f9' }}>
|
|
140
|
+
{modes.map(function(mode) {
|
|
141
|
+
return (
|
|
142
|
+
<AwesomeSpeedometerGauge
|
|
143
|
+
key={mode}
|
|
144
|
+
type={mode}
|
|
145
|
+
title={mode.charAt(0).toUpperCase() + mode.slice(1)}
|
|
146
|
+
subtitle="CPU Usage"
|
|
147
|
+
value={63}
|
|
148
|
+
min={0}
|
|
149
|
+
max={100}
|
|
150
|
+
units="%"
|
|
151
|
+
thresholds={thresholds}
|
|
152
|
+
size={200}
|
|
153
|
+
/>
|
|
154
|
+
);
|
|
155
|
+
})}
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### Example 2 — Dark theme, real-time updates, target marker
|
|
164
|
+
|
|
165
|
+
Simulates a live-updating gauge that flashes when the value crosses a threshold band.
|
|
166
|
+
|
|
167
|
+
```jsx
|
|
168
|
+
import { useState, useEffect } from 'react';
|
|
169
|
+
import { AwesomeSpeedometerGauge } from 'awesome-speedometer-gauge';
|
|
170
|
+
import 'awesome-speedometer-gauge/style.css';
|
|
171
|
+
|
|
172
|
+
var thresholds = [
|
|
173
|
+
{ value: 50, color: '#22c55e' },
|
|
174
|
+
{ value: 80, color: '#f59e0b' },
|
|
175
|
+
{ value: 100, color: '#ef4444' },
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
export default function RealtimeDemo() {
|
|
179
|
+
var [rpm, setRpm] = useState(3200);
|
|
180
|
+
|
|
181
|
+
useEffect(function() {
|
|
182
|
+
var id = setInterval(function() {
|
|
183
|
+
setRpm(function(prev) {
|
|
184
|
+
var delta = (Math.random() - 0.48) * 400;
|
|
185
|
+
return Math.round(Math.max(0, Math.min(8000, prev + delta)));
|
|
186
|
+
});
|
|
187
|
+
}, 1200);
|
|
188
|
+
return function() { clearInterval(id); };
|
|
189
|
+
}, []);
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<div style={{ padding: 40, background: '#0b1220', minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
193
|
+
<AwesomeSpeedometerGauge
|
|
194
|
+
type="standard"
|
|
195
|
+
theme="dark"
|
|
196
|
+
title="Engine RPM"
|
|
197
|
+
subtitle="Live telemetry"
|
|
198
|
+
value={rpm}
|
|
199
|
+
min={0}
|
|
200
|
+
max={8000}
|
|
201
|
+
units="rpm"
|
|
202
|
+
target={4500}
|
|
203
|
+
showTarget={true}
|
|
204
|
+
thresholds={thresholds}
|
|
205
|
+
realtime={true}
|
|
206
|
+
size={240}
|
|
207
|
+
thickness={20}
|
|
208
|
+
/>
|
|
209
|
+
</div>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
### Example 3 — Dashboard grid with `onClick`, custom thresholds, and mixed themes
|
|
217
|
+
|
|
218
|
+
```jsx
|
|
219
|
+
import { useState } from 'react';
|
|
220
|
+
import { AwesomeSpeedometerGauge } from 'awesome-speedometer-gauge';
|
|
221
|
+
import 'awesome-speedometer-gauge/style.css';
|
|
222
|
+
|
|
223
|
+
var kpis = [
|
|
224
|
+
{
|
|
225
|
+
type: 'arc', theme: 'light', title: 'Conversion Rate', subtitle: 'Last 30 days',
|
|
226
|
+
value: 4.7, min: 0, max: 10, units: '%', target: 5, showTarget: true,
|
|
227
|
+
thresholds: [{ value: 3, color: '#ef4444' }, { value: 6, color: '#f59e0b' }, { value: 10, color: '#22c55e' }],
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
type: 'multiband', theme: 'dark', title: 'Memory Usage', subtitle: 'App server',
|
|
231
|
+
value: 71, min: 0, max: 100, units: '%',
|
|
232
|
+
thresholds: [{ value: 50, color: '#22c55e' }, { value: 80, color: '#f59e0b' }, { value: 100, color: '#ef4444' }],
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
type: 'semicircle', theme: 'purple', title: 'NPS Score', subtitle: 'Q2 2025',
|
|
236
|
+
value: 58, min: -100, max: 100, target: 50, showTarget: true,
|
|
237
|
+
thresholds: [{ value: 0, color: '#ef4444' }, { value: 50, color: '#f59e0b' }, { value: 100, color: '#22c55e' }],
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
type: 'full', theme: 'dark', title: 'Disk I/O', subtitle: 'Primary volume',
|
|
241
|
+
value: 340, min: 0, max: 500, units: 'MB/s',
|
|
242
|
+
thresholds: [{ value: 200, color: '#22c55e' }, { value: 400, color: '#f59e0b' }, { value: 500, color: '#ef4444' }],
|
|
243
|
+
},
|
|
244
|
+
];
|
|
245
|
+
|
|
246
|
+
var overlayStyle = {
|
|
247
|
+
position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)',
|
|
248
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 100,
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
var popupStyle = {
|
|
252
|
+
background: '#fff', borderRadius: 16, padding: '32px 40px',
|
|
253
|
+
fontFamily: 'sans-serif', minWidth: 260, textAlign: 'center',
|
|
254
|
+
boxShadow: '0 20px 60px rgba(0,0,0,0.25)',
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export default function DashboardDemo() {
|
|
258
|
+
var [clicked, setClicked] = useState(null);
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<div style={{ padding: 32, background: '#f1f5f9', minHeight: '100vh' }}>
|
|
262
|
+
<h2 style={{ fontFamily: 'sans-serif', marginBottom: 24, color: '#0f172a' }}>KPI Dashboard</h2>
|
|
263
|
+
|
|
264
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(240px, 1fr))', gap: 20 }}>
|
|
265
|
+
{kpis.map(function(kpi, i) {
|
|
266
|
+
return (
|
|
267
|
+
<AwesomeSpeedometerGauge
|
|
268
|
+
key={i}
|
|
269
|
+
type={kpi.type}
|
|
270
|
+
theme={kpi.theme}
|
|
271
|
+
title={kpi.title}
|
|
272
|
+
subtitle={kpi.subtitle}
|
|
273
|
+
value={kpi.value}
|
|
274
|
+
min={kpi.min}
|
|
275
|
+
max={kpi.max}
|
|
276
|
+
units={kpi.units}
|
|
277
|
+
target={kpi.target}
|
|
278
|
+
showTarget={kpi.showTarget}
|
|
279
|
+
thresholds={kpi.thresholds}
|
|
280
|
+
animated={true}
|
|
281
|
+
size={210}
|
|
282
|
+
onClick={function(val) { setClicked({ title: kpi.title, value: val, units: kpi.units || '' }); }}
|
|
283
|
+
/>
|
|
284
|
+
);
|
|
285
|
+
})}
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
{clicked && (
|
|
289
|
+
<div style={overlayStyle} onClick={function() { setClicked(null); }}>
|
|
290
|
+
<div style={popupStyle} onClick={function(e) { e.stopPropagation(); }}>
|
|
291
|
+
<div style={{ fontSize: 13, color: '#64748b', letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 8 }}>
|
|
292
|
+
{clicked.title}
|
|
293
|
+
</div>
|
|
294
|
+
<div style={{ fontSize: 52, fontWeight: 700, color: '#0f172a' }}>
|
|
295
|
+
{clicked.value}{clicked.units}
|
|
296
|
+
</div>
|
|
297
|
+
<button
|
|
298
|
+
onClick={function() { setClicked(null); }}
|
|
299
|
+
style={{ marginTop: 20, padding: '8px 24px', borderRadius: 8, border: 'none', background: '#0f172a', color: '#fff', cursor: 'pointer', fontSize: 13 }}
|
|
300
|
+
>
|
|
301
|
+
Close
|
|
302
|
+
</button>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
)}
|
|
306
|
+
</div>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
> **Note:** `AwesomeSpeedometerGauge.module.css` is bundled directly into `dist/esm/index.js` and `dist/cjs/index.js` by Vite's CSS Modules pipeline. You do **not** need to import it separately — only `style.css` needs an explicit import in your app.
|
|
314
|
+
|
|
315
|
+
## CSS Custom Properties
|
|
316
|
+
|
|
317
|
+
All colours in the gauge are driven by CSS variables set on the `.gaugeCard` root element. Override them per-instance with the `style` prop or globally in your own CSS:
|
|
318
|
+
|
|
319
|
+
```css
|
|
320
|
+
.my-gauge {
|
|
321
|
+
--gauge-progress: #6366f1; /* filled arc colour */
|
|
322
|
+
--gauge-track: #e2e8f0; /* background arc */
|
|
323
|
+
--gauge-needle: #1e293b; /* needle / dot marker */
|
|
324
|
+
--gauge-target: #f59e0b; /* target marker */
|
|
325
|
+
--gauge-range-poor: #ef4444;
|
|
326
|
+
--gauge-range-warning: #f59e0b;
|
|
327
|
+
--gauge-range-good: #22c55e;
|
|
328
|
+
--gauge-text: #0f172a;
|
|
329
|
+
--gauge-text-muted: #64748b;
|
|
330
|
+
--gauge-tick: #cbd5e1;
|
|
331
|
+
--gauge-surface: #ffffff;
|
|
332
|
+
--gauge-bg: #f6f7f9;
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Pass the class via the `className` prop:
|
|
337
|
+
|
|
338
|
+
```jsx
|
|
339
|
+
<AwesomeSpeedometerGauge className="my-gauge" value={55} />
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Live Data Fetching
|
|
345
|
+
|
|
346
|
+
When `data` is not provided, the component fetches from `apiEndpoint`. The endpoint should return JSON matching the prop shape:
|
|
347
|
+
|
|
348
|
+
```json
|
|
349
|
+
{
|
|
350
|
+
"value": 73,
|
|
351
|
+
"min": 0,
|
|
352
|
+
"max": 100,
|
|
353
|
+
"target": 80,
|
|
354
|
+
"units": "%",
|
|
355
|
+
"type": "standard",
|
|
356
|
+
"thresholds": [
|
|
357
|
+
{ "value": 50, "color": "#ef4444" },
|
|
358
|
+
{ "value": 75, "color": "#f59e0b" },
|
|
359
|
+
{ "value": 100, "color": "#22c55e" }
|
|
360
|
+
]
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
An array response is also accepted — the first element is used. Direct props always override fetched values.
|
|
365
|
+
|
|
366
|
+
## 📄 License
|
|
367
|
+
|
|
368
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
._gaugeCard_6w996_1{--gauge-bg: #f6f7f9;--gauge-surface: #ffffff;--gauge-text: #0f172a;--gauge-text-muted: #64748b;--gauge-tick: #cbd5f5;--gauge-track: #e2e8f0;--gauge-progress: #22c55e;--gauge-needle: #0f172a;--gauge-target: #f59e0b;--gauge-range-poor: #ef4444;--gauge-range-warning: #f59e0b;--gauge-range-good: #22c55e;background:var(--gauge-surface);border-radius:18px;padding:18px 20px;box-shadow:0 12px 30px #0f172a14,0 0 18px color-mix(in srgb,var(--gauge-active) 35%,transparent);display:grid;gap:12px;font-family:"Source Sans 3",Segoe UI,system-ui,sans-serif;color:var(--gauge-text);position:relative;overflow:hidden}._gaugeCard_6w996_1:after{content:attr(data-tooltip);position:absolute;left:50%;bottom:14px;background:#0f172aeb;color:#f8fafc;padding:6px 10px;border-radius:8px;font-size:11px;font-weight:500;white-space:nowrap;opacity:0;transform:translate(-50%,6px);transition:opacity .15s ease,transform .15s ease;pointer-events:none;z-index:2}._gaugeCard_6w996_1[data-tooltip-open=true]:after{opacity:1;transform:translate(-50%)}._gaugeCard_6w996_1:hover{box-shadow:0 14px 34px #0f172a1a,0 0 28px color-mix(in srgb,var(--gauge-active) 50%,transparent)}._light_6w996_58{--gauge-bg: #f5f6fa;--gauge-surface: #ffffff;--gauge-text: #0f172a;--gauge-text-muted: #64748b;--gauge-tick: #cbd5f5;--gauge-track: #e2e8f0}._dark_6w996_67{--gauge-bg: #0b1220;--gauge-surface: #111827;--gauge-text: #e2e8f0;--gauge-text-muted: #94a3b8;--gauge-tick: #334155;--gauge-track: #1f2937;--gauge-needle: #e2e8f0}._purple_6w996_77{--gauge-bg: #1b1035;--gauge-surface: #221344;--gauge-text: #f8fafc;--gauge-text-muted: #cbd5f5;--gauge-tick: #4c1d95;--gauge-track: #312e81;--gauge-needle: #f8fafc}._dark_6w996_67._gaugeCard_6w996_1:after{background:#0f172af2}._purple_6w996_77._gaugeCard_6w996_1:after{background:#2c1454f2}._header_6w996_95{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}._title_6w996_102{font-size:16px;font-weight:600;letter-spacing:.2px}._subtitle_6w996_108{font-size:12px;color:var(--gauge-text-muted);margin-top:4px}._meta_6w996_114{text-align:right;font-size:20px;font-weight:700;color:var(--gauge-active);display:flex;align-items:baseline;gap:6px}._units_6w996_124{font-size:12px;font-weight:500;color:var(--gauge-text-muted)}._body_6w996_130{background:radial-gradient(circle at 50% 0%,var(--gauge-bg),transparent 65%);border-radius:16px;padding:8px 0 12px;display:grid;place-items:center}._gaugeStage_6w996_138{display:grid;place-items:center}._state_6w996_143{font-size:12px;color:var(--gauge-text-muted);padding:12px 0}._flash_6w996_149{animation:_gauge-flash_6w996_1 .3s ease-in-out}@keyframes _gauge-flash_6w996_1{0%{box-shadow:0 0 #22c55e00}50%{box-shadow:0 0 18px #22c55e59}to{box-shadow:0 0 #22c55e00}}._gaugeCard_6w996_1 svg text{font-family:"Source Sans 3",Segoe UI,system-ui,sans-serif;fill:var(--gauge-text)}._gaugeCard_6w996_1 svg ._gauge-value_6w996_170 text{fill:var(--gauge-text)}._gaugeCard_6w996_1 svg ._gauge-ticks_6w996_174 text{fill:var(--gauge-text-muted)}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"value": 72,
|
|
3
|
+
"min": 0,
|
|
4
|
+
"max": 100,
|
|
5
|
+
"target": 85,
|
|
6
|
+
"units": "%",
|
|
7
|
+
"thresholds": [
|
|
8
|
+
{ "value": 60, "color": "var(--gauge-range-poor)", "label": "Poor" },
|
|
9
|
+
{ "value": 80, "color": "var(--gauge-range-warning)", "label": "Average" },
|
|
10
|
+
{ "value": 100, "color": "var(--gauge-range-good)", "label": "Good" }
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react/jsx-runtime"),m=require("react"),je="_gaugeCard_6w996_1",Ae="_light_6w996_58",pe="_dark_6w996_67",be="_purple_6w996_77",_e="_header_6w996_95",we="_title_6w996_102",Me="_subtitle_6w996_108",ke="_meta_6w996_114",$e="_units_6w996_124",Ne="_body_6w996_130",Re="_gaugeStage_6w996_138",ze="_state_6w996_143",Se="_flash_6w996_149",p={gaugeCard:je,light:Ae,dark:pe,purple:be,header:_e,title:we,subtitle:Me,meta:ke,units:$e,body:Ne,gaugeStage:Re,state:ze,flash:Se,"gauge-flash":"_gauge-flash_6w996_1","gauge-value":"_gauge-value_6w996_170","gauge-ticks":"_gauge-ticks_6w996_174"},k=(e,t,a,o)=>{const r=(o-90)*(Math.PI/180);return{x:e+a*Math.cos(r),y:t+a*Math.sin(r)}},ee=(e,t,a,o,r)=>{const l=k(e,t,a,r),d=k(e,t,a,o),x=Math.abs(r-o)<=180?"0":"1";return["M",l.x,l.y,"A",a,a,0,x,0,d.x,d.y].join(" ")};function Te({cx:e,cy:t,radius:a,startAngle:o,endAngle:r,color:l,thickness:d}){const x=ee(e,t,a,o,r);return n.jsx("path",{d:x,fill:"none",stroke:l,strokeWidth:d,strokeLinecap:"round"})}const F=m.memo(Te);function Ve({cx:e,cy:t,radius:a,angle:o,color:r,width:l=3}){const d=k(e,t,a,o),x=k(e,t,10,o-90),f=k(e,t,10,o+90);return n.jsxs("g",{className:"gauge-needle","aria-hidden":"true",children:[n.jsx("path",{d:`M ${x.x} ${x.y} L ${d.x} ${d.y} L ${f.x} ${f.y} Z`,fill:r}),n.jsx("circle",{cx:e,cy:t,r:l*2,fill:r})]})}const de=m.memo(Ve);function Fe({ticks:e,color:t,labelColor:a,showLabels:o}){return n.jsx("g",{className:"gauge-ticks","aria-hidden":"true",children:e.map(r=>n.jsxs("g",{children:[n.jsx("line",{x1:r.outer.x,y1:r.outer.y,x2:r.inner.x,y2:r.inner.y,stroke:t,strokeWidth:"2"}),o&&n.jsx("text",{x:r.label.x,y:r.label.y,textAnchor:"middle",dominantBaseline:"middle",fill:a,fontSize:"10",children:Math.round(r.value)})]},r.value))})}const he=m.memo(Fe);function Ce({cx:e,cy:t,value:a,target:o,units:r,showTarget:l}){return n.jsxs("g",{className:"gauge-value","aria-hidden":"true",children:[n.jsxs("text",{x:e,y:t,textAnchor:"middle",dominantBaseline:"middle",fontSize:"20",children:[a,r?` ${r}`:""]}),l&&typeof o=="number"&&n.jsxs("text",{x:e,y:t+22,textAnchor:"middle",dominantBaseline:"middle",fontSize:"11",children:["Target ",o]})]})}const D=m.memo(Ce);function Ge({cx:e,cy:t,radius:a,angle:o,color:r}){const l=k(e,t,a,o);return n.jsx("circle",{cx:l.x,cy:l.y,r:"3",fill:r})}const te=m.memo(Ge),se=(e,t,a)=>Math.min(Math.max(e,t),a),me=(e,t,a)=>e+(t-e)*a,xe=(e,t,a)=>a===t?0:(e-t)/(a-t),R=(e,t,a,o,r)=>{const l=se(e,t,a),d=xe(l,t,a);return me(o,r,d)},Le=(e,t,a)=>{if(!Array.isArray(t)||t.length===0)return a;const o=[...t].filter(l=>typeof(l==null?void 0:l.value)=="number").sort((l,d)=>l.value-d.value),r=o.find(l=>e<=l.value)||o[o.length-1];return(r==null?void 0:r.color)||a},fe=(e,t,a)=>!Array.isArray(e)||e.length===0?[{value:t+(a-t)*.6,color:"var(--gauge-range-poor)"},{value:t+(a-t)*.8,color:"var(--gauge-range-warning)"},{value:a,color:"var(--gauge-range-good)"}]:e.map(o=>({value:typeof o.value=="number"?o.value:a,color:o.color||"var(--gauge-range-good)",label:o.label||""}));function I({value:e,min:t,max:a,startAngle:o,endAngle:r,size:l,thickness:d,thresholds:x,tickCount:f=5,arcPadding:y=0}){return m.useMemo(()=>{const c=Number.isFinite(t)?t:0,s=Number.isFinite(a)?a:100,g=Number.isFinite(e)?e:c,u=se(g,c,s),v=Number.isFinite(o)?o:-90,j=Number.isFinite(r)?r:90,$=Number.isFinite(l)?l:200,z=Number.isFinite(d)?d:18,b=$/2,_=$/2,N=$/2,C=N-z,G=(N+C)/2,L=R(u,c,s,v,j),O=fe(x,c,s);let P=c;const K=O.map(S=>{const w=P,M=S.value;return P=M,{...S,startValue:w,endValue:M,startAngle:R(w,c,s,v,j),endAngle:R(M,c,s,v,j)}}),W=f>1?(s-c)/(f-1):0,B=Array.from({length:f},(S,w)=>{const M=c+W*w,i=R(M,c,s,v,j),Q=k(b,_,N-y,i),U=k(b,_,N-y-10,i),X=k(b,_,C-18,i);return{value:M,angle:i,outer:Q,inner:U,label:X}}),Z=ee(b,_,G,v,j),H=xe(u,c,s),E=R(u,c,s,v,j),J=ee(b,_,G,v,E);return{cx:b,cy:_,outerRadius:N,innerRadius:C,midRadius:G,value:u,valueAngle:L,startAngle:v,endAngle:j,arcPath:Z,progressPath:J,progressAngle:E,valueProgress:H,ranges:K,ticks:B,size:$,thickness:z}},[e,t,a,o,r,l,d,x,f,y])}function Pe({value:e,min:t,max:a,startAngle:o,endAngle:r,size:l,thickness:d,thresholds:x,showTicks:f,showValue:y,showTarget:c,target:s,units:g}){const u=I({value:e,min:t,max:a,startAngle:o,endAngle:r,size:l,thickness:d,thresholds:x,tickCount:6,arcPadding:2}),v=typeof s=="number"?R(s,u.min,u.max,u.startAngle,u.endAngle):null;return n.jsxs("svg",{width:u.size,height:u.size,viewBox:`0 0 ${u.size} ${u.size}`,children:[n.jsx("g",{className:"gauge-ranges",children:u.ranges.map(j=>n.jsx(F,{cx:u.cx,cy:u.cy,radius:u.midRadius,startAngle:j.startAngle,endAngle:j.endAngle,color:j.color,thickness:u.thickness},j.endValue))}),f&&n.jsx(he,{ticks:u.ticks,color:"var(--gauge-tick)",labelColor:"var(--gauge-text-muted)",showLabels:!0}),typeof v=="number"&&c&&n.jsx(te,{cx:u.cx,cy:u.cy,radius:u.outerRadius-4,angle:v,color:"var(--gauge-target)"}),n.jsx(de,{cx:u.cx,cy:u.cy,radius:u.innerRadius-4,angle:u.valueAngle,color:"var(--gauge-needle)"}),y&&n.jsx(D,{cx:u.cx,cy:u.cy+28,value:Math.round(u.value),target:s,units:g,showTarget:c})]})}const Be=m.memo(Pe);function Ee({value:e,min:t,max:a,startAngle:o,endAngle:r,size:l,thickness:d,thresholds:x,showTicks:f,showValue:y,units:c}){const s=I({value:e,min:t,max:a,startAngle:o,endAngle:r,size:l,thickness:d,thresholds:x,tickCount:4,arcPadding:4});return n.jsxs("svg",{width:s.size,height:s.size/1.2,viewBox:`0 0 ${s.size} ${s.size}`,children:[n.jsx("g",{className:"gauge-ranges",children:s.ranges.map(g=>n.jsx(F,{cx:s.cx,cy:s.cy,radius:s.midRadius,startAngle:g.startAngle,endAngle:g.endAngle,color:g.color,thickness:s.thickness},g.endValue))}),f&&n.jsx(he,{ticks:s.ticks,color:"var(--gauge-tick)",labelColor:"var(--gauge-text-muted)",showLabels:!1}),y&&n.jsx(D,{cx:s.cx,cy:s.cy+14,value:Math.round(s.value),units:c,showTarget:!1})]})}const qe=m.memo(Ee);function De({value:e,min:t,max:a,startAngle:o,endAngle:r,size:l,thickness:d,thresholds:x,showValue:f,units:y}){const c=I({value:e,min:t,max:a,startAngle:o,endAngle:r,size:l,thickness:d,thresholds:x,tickCount:8,arcPadding:0});return n.jsxs("svg",{width:c.size,height:c.size,viewBox:`0 0 ${c.size} ${c.size}`,children:[n.jsx("g",{className:"gauge-ranges",children:c.ranges.map(s=>n.jsx(F,{cx:c.cx,cy:c.cy,radius:c.midRadius,startAngle:s.startAngle,endAngle:s.endAngle,color:s.color,thickness:c.thickness},s.endValue))}),n.jsx(de,{cx:c.cx,cy:c.cy,radius:c.innerRadius-2,angle:c.valueAngle,color:"var(--gauge-needle)"}),f&&n.jsx(D,{cx:c.cx,cy:c.cy+24,value:Math.round(c.value),units:y,showTarget:!1})]})}const Ie=m.memo(De);function Oe({value:e,min:t,max:a,startAngle:o,endAngle:r,size:l,thickness:d,thresholds:x,showValue:f,units:y,gradientId:c}){const s=I({value:e,min:t,max:a,startAngle:o,endAngle:r,size:l,thickness:d,thresholds:x,tickCount:0,arcPadding:0});return n.jsxs("svg",{width:s.size,height:s.size,viewBox:`0 0 ${s.size} ${s.size}`,children:[c&&n.jsx("defs",{children:n.jsxs("linearGradient",{id:c,x1:"0%",y1:"0%",x2:"100%",y2:"0%",children:[n.jsx("stop",{offset:"0%",stopColor:"var(--gauge-range-poor)"}),n.jsx("stop",{offset:"55%",stopColor:"var(--gauge-range-warning)"}),n.jsx("stop",{offset:"100%",stopColor:"var(--gauge-range-good)"})]})}),n.jsx(F,{cx:s.cx,cy:s.cy,radius:s.midRadius,startAngle:s.startAngle,endAngle:s.endAngle,color:"var(--gauge-track)",thickness:s.thickness}),n.jsx(F,{cx:s.cx,cy:s.cy,radius:s.midRadius,startAngle:s.startAngle,endAngle:s.progressAngle,color:c?`url(#${c})`:"var(--gauge-progress)",thickness:s.thickness}),f&&n.jsx(D,{cx:s.cx,cy:s.cy+8,value:Math.round(s.value),units:y,showTarget:!1})]})}const Ke=m.memo(Oe);function We({value:e,min:t,max:a,startAngle:o,endAngle:r,size:l,thickness:d,thresholds:x,showValue:f,units:y,target:c,showTarget:s}){const g=I({value:e,min:t,max:a,startAngle:o,endAngle:r,size:l,thickness:d,thresholds:x,tickCount:0,arcPadding:0}),u=typeof c=="number"?R(c,g.min,g.max,g.startAngle,g.endAngle):null;return n.jsxs("svg",{width:g.size,height:g.size,viewBox:`0 0 ${g.size} ${g.size}`,children:[n.jsx("g",{className:"gauge-ranges",children:g.ranges.map(v=>n.jsx(F,{cx:g.cx,cy:g.cy,radius:g.midRadius,startAngle:v.startAngle,endAngle:v.endAngle,color:v.color,thickness:g.thickness},v.endValue))}),n.jsx(te,{cx:g.cx,cy:g.cy,radius:g.outerRadius-4,angle:g.valueAngle,color:"var(--gauge-needle)"}),s&&typeof u=="number"&&n.jsx(te,{cx:g.cx,cy:g.cy,radius:g.outerRadius-4,angle:u,color:"var(--gauge-target)"}),f&&n.jsx(D,{cx:g.cx,cy:g.cy+8,value:Math.round(g.value),units:y,target:c,showTarget:s})]})}const Ze=m.memo(We),ie=()=>typeof performance<"u"?performance.now():Date.now();function He(e,{enabled:t,duration:a=500,easing:o}={}){const r=m.useRef(null),l=m.useRef(0),d=m.useRef(e),[x,f]=m.useState(e);return m.useEffect(()=>{if(!t)return f(e),d.current=e,()=>{};const y=d.current,c=ie();l.current=c;const s=typeof o=="function"?o:u=>1-Math.pow(1-u,3),g=()=>{const u=Math.min(1,(ie()-c)/a),v=me(y,e,s(u));f(v),u<1?r.current=requestAnimationFrame(g):d.current=e};return r.current=requestAnimationFrame(g),()=>{r.current&&cancelAnimationFrame(r.current)}},[e,t,a,o]),x}const Je=(...e)=>e.filter(Boolean).join(" "),Qe=null,ue={standard:{start:-90,end:90},semicircle:{start:-110,end:110},full:{start:0,end:360},arc:{start:-225,end:45},multiband:{start:-120,end:120}},ge=e=>e?Array.isArray(e)?e[0]||null:Array.isArray(e.data)?e.data[0]||null:typeof e=="object"?e:null:null,A=(e,t)=>e!==void 0?e:t,Ue=(e,t,a,o)=>{const r=fe(t,a,o);for(let l=0;l<r.length;l+=1)if(e<=r[l].value)return l;return r.length-1};function Xe({type:e="standard",value:t,min:a,max:o,target:r,thresholds:l,showTicks:d=!0,showValue:x=!0,showTarget:f=!1,animated:y=!0,realtime:c=!1,size:s=220,thickness:g=18,startAngle:u,endAngle:v,units:j="",data:$=Qe,apiEndpoint:z="/gauge-data.json",title:b="KPI Gauge",subtitle:_="",theme:N="light",className:C,style:G,onClick:L}){const[O,P]=m.useState(null),[K,W]=m.useState(!1),[B,Z]=m.useState(""),[H,E]=m.useState(!1),[J,S]=m.useState(!1),w=m.useRef(null),M=m.useCallback(async()=>{if(z){W(!0),Z("");try{const h=await fetch(z);if(!h.ok)throw new Error(`Request failed: ${h.status}`);const V=await h.json();P(ge(V))}catch(h){Z("Failed to load gauge data."),console.error("Failed to load gauge data",h)}finally{W(!1)}}},[z]);m.useEffect(()=>{if($){P(ge($));return}M()},[$,M]);const i=m.useMemo(()=>{const h=O||{},V=String(e||h.type||"standard").toLowerCase(),ne=ue[V]||ue.standard,ae=A(a,h.min),re=A(o,h.max),Y=Number.isFinite(ae)?ae:0,oe=Number.isFinite(re)?re:100,ce=A(t,h.value),ve=Number.isFinite(ce)?ce:Y,le=A(r,h.target),ye=Number.isFinite(le)?le:null;return{type:V,value:se(ve,Y,oe),min:Y,max:oe,target:ye,thresholds:A(l,h.thresholds),showTicks:A(d,h.showTicks??d),showValue:A(x,h.showValue??x),showTarget:A(f,h.showTarget??f),size:A(s,h.size??s),thickness:A(g,h.thickness??g),units:A(j,h.units??j),startAngle:A(u,h.startAngle??ne.start),endAngle:A(v,h.endAngle??ne.end)}},[O,e,a,o,t,r,l,d,x,f,s,g,j,u,v]),Q=He(i.value,{enabled:y||c,duration:c?220:500});m.useEffect(()=>{if(!c)return;const h=Ue(i.value,i.thresholds,i.min,i.max);if(w.current===null){w.current=h;return}if(w.current!==h){w.current=h,E(!0);const V=setTimeout(()=>E(!1),300);return()=>clearTimeout(V)}},[c,i.value,i.thresholds,i.min,i.max]);const U=Le(i.value,i.thresholds,"var(--gauge-range-good)"),X=m.useMemo(()=>{const h=[`Value: ${Math.round(i.value)}${i.units?` ${i.units}`:""}`,`Min: ${i.min}`,`Max: ${i.max}`];return typeof i.target=="number"&&h.splice(1,0,`Target: ${i.target}`),h.join(" • ")},[i.value,i.units,i.min,i.max,i.target]),q={value:Q,min:i.min,max:i.max,startAngle:i.startAngle,endAngle:i.endAngle,size:i.size,thickness:i.thickness,thresholds:i.thresholds,showTicks:i.showTicks,showValue:i.showValue,showTarget:i.showTarget,target:i.target,units:i.units},T=i.type;return n.jsxs("section",{className:Je(p.gaugeCard,p[N],H&&p.flash,C),style:{...G,"--gauge-active":U},onClick:()=>L==null?void 0:L(i.value),onMouseEnter:()=>S(!0),onMouseLeave:()=>S(!1),role:"figure","aria-label":b,"data-tooltip":X,"data-tooltip-open":J?"true":"false",children:[n.jsxs("header",{className:p.header,children:[n.jsxs("div",{children:[n.jsx("div",{className:p.title,children:b}),_&&n.jsx("div",{className:p.subtitle,children:_})]}),n.jsxs("div",{className:p.meta,children:[n.jsx("span",{children:Math.round(i.value)}),i.units&&n.jsx("span",{className:p.units,children:i.units})]})]}),n.jsxs("div",{className:p.body,children:[K&&n.jsx("div",{className:p.state,children:"Loading KPI..."}),B&&n.jsx("div",{className:p.state,children:B}),!K&&!B&&n.jsxs("div",{className:p.gaugeStage,children:[T==="semicircle"&&n.jsx(qe,{...q}),T==="full"&&n.jsx(Ie,{...q}),T==="arc"&&n.jsx(Ke,{...q,gradientId:"gaugeGradient"}),T==="multiband"&&n.jsx(Ze,{...q}),(T==="standard"||!T)&&n.jsx(Be,{...q})]})]})]})}exports.AwesomeSpeedometerGauge=Xe;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
._gaugeCard_6w996_1{--gauge-bg: #f6f7f9;--gauge-surface: #ffffff;--gauge-text: #0f172a;--gauge-text-muted: #64748b;--gauge-tick: #cbd5f5;--gauge-track: #e2e8f0;--gauge-progress: #22c55e;--gauge-needle: #0f172a;--gauge-target: #f59e0b;--gauge-range-poor: #ef4444;--gauge-range-warning: #f59e0b;--gauge-range-good: #22c55e;background:var(--gauge-surface);border-radius:18px;padding:18px 20px;box-shadow:0 12px 30px #0f172a14,0 0 18px color-mix(in srgb,var(--gauge-active) 35%,transparent);display:grid;gap:12px;font-family:"Source Sans 3",Segoe UI,system-ui,sans-serif;color:var(--gauge-text);position:relative;overflow:hidden}._gaugeCard_6w996_1:after{content:attr(data-tooltip);position:absolute;left:50%;bottom:14px;background:#0f172aeb;color:#f8fafc;padding:6px 10px;border-radius:8px;font-size:11px;font-weight:500;white-space:nowrap;opacity:0;transform:translate(-50%,6px);transition:opacity .15s ease,transform .15s ease;pointer-events:none;z-index:2}._gaugeCard_6w996_1[data-tooltip-open=true]:after{opacity:1;transform:translate(-50%)}._gaugeCard_6w996_1:hover{box-shadow:0 14px 34px #0f172a1a,0 0 28px color-mix(in srgb,var(--gauge-active) 50%,transparent)}._light_6w996_58{--gauge-bg: #f5f6fa;--gauge-surface: #ffffff;--gauge-text: #0f172a;--gauge-text-muted: #64748b;--gauge-tick: #cbd5f5;--gauge-track: #e2e8f0}._dark_6w996_67{--gauge-bg: #0b1220;--gauge-surface: #111827;--gauge-text: #e2e8f0;--gauge-text-muted: #94a3b8;--gauge-tick: #334155;--gauge-track: #1f2937;--gauge-needle: #e2e8f0}._purple_6w996_77{--gauge-bg: #1b1035;--gauge-surface: #221344;--gauge-text: #f8fafc;--gauge-text-muted: #cbd5f5;--gauge-tick: #4c1d95;--gauge-track: #312e81;--gauge-needle: #f8fafc}._dark_6w996_67._gaugeCard_6w996_1:after{background:#0f172af2}._purple_6w996_77._gaugeCard_6w996_1:after{background:#2c1454f2}._header_6w996_95{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}._title_6w996_102{font-size:16px;font-weight:600;letter-spacing:.2px}._subtitle_6w996_108{font-size:12px;color:var(--gauge-text-muted);margin-top:4px}._meta_6w996_114{text-align:right;font-size:20px;font-weight:700;color:var(--gauge-active);display:flex;align-items:baseline;gap:6px}._units_6w996_124{font-size:12px;font-weight:500;color:var(--gauge-text-muted)}._body_6w996_130{background:radial-gradient(circle at 50% 0%,var(--gauge-bg),transparent 65%);border-radius:16px;padding:8px 0 12px;display:grid;place-items:center}._gaugeStage_6w996_138{display:grid;place-items:center}._state_6w996_143{font-size:12px;color:var(--gauge-text-muted);padding:12px 0}._flash_6w996_149{animation:_gauge-flash_6w996_1 .3s ease-in-out}@keyframes _gauge-flash_6w996_1{0%{box-shadow:0 0 #22c55e00}50%{box-shadow:0 0 18px #22c55e59}to{box-shadow:0 0 #22c55e00}}._gaugeCard_6w996_1 svg text{font-family:"Source Sans 3",Segoe UI,system-ui,sans-serif;fill:var(--gauge-text)}._gaugeCard_6w996_1 svg ._gauge-value_6w996_170 text{fill:var(--gauge-text)}._gaugeCard_6w996_1 svg ._gauge-ticks_6w996_174 text{fill:var(--gauge-text-muted)}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"value": 72,
|
|
3
|
+
"min": 0,
|
|
4
|
+
"max": 100,
|
|
5
|
+
"target": 85,
|
|
6
|
+
"units": "%",
|
|
7
|
+
"thresholds": [
|
|
8
|
+
{ "value": 60, "color": "var(--gauge-range-poor)", "label": "Poor" },
|
|
9
|
+
{ "value": 80, "color": "var(--gauge-range-warning)", "label": "Average" },
|
|
10
|
+
{ "value": 100, "color": "var(--gauge-range-good)", "label": "Good" }
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
import { jsx as i, jsxs as x } from "react/jsx-runtime";
|
|
2
|
+
import b, { useMemo as ae, useRef as Q, useState as G, useEffect as se, useCallback as ke } from "react";
|
|
3
|
+
const Me = "_gaugeCard_6w996_1", $e = "_light_6w996_58", Ne = "_dark_6w996_67", ze = "_purple_6w996_77", Re = "_header_6w996_95", Te = "_title_6w996_102", Ve = "_subtitle_6w996_108", Fe = "_meta_6w996_114", Ce = "_units_6w996_124", Se = "_body_6w996_130", Ge = "_gaugeStage_6w996_138", Le = "_state_6w996_143", Be = "_flash_6w996_149", _ = {
|
|
4
|
+
gaugeCard: Me,
|
|
5
|
+
light: $e,
|
|
6
|
+
dark: Ne,
|
|
7
|
+
purple: ze,
|
|
8
|
+
header: Re,
|
|
9
|
+
title: Te,
|
|
10
|
+
subtitle: Ve,
|
|
11
|
+
meta: Fe,
|
|
12
|
+
units: Ce,
|
|
13
|
+
body: Se,
|
|
14
|
+
gaugeStage: Ge,
|
|
15
|
+
state: Le,
|
|
16
|
+
flash: Be,
|
|
17
|
+
"gauge-flash": "_gauge-flash_6w996_1",
|
|
18
|
+
"gauge-value": "_gauge-value_6w996_170",
|
|
19
|
+
"gauge-ticks": "_gauge-ticks_6w996_174"
|
|
20
|
+
}, N = (e, t, a, r) => {
|
|
21
|
+
const s = (r - 90) * (Math.PI / 180);
|
|
22
|
+
return {
|
|
23
|
+
x: e + a * Math.cos(s),
|
|
24
|
+
y: t + a * Math.sin(s)
|
|
25
|
+
};
|
|
26
|
+
}, re = (e, t, a, r, s) => {
|
|
27
|
+
const c = N(e, t, a, s), d = N(e, t, a, r), m = Math.abs(s - r) <= 180 ? "0" : "1";
|
|
28
|
+
return [
|
|
29
|
+
"M",
|
|
30
|
+
c.x,
|
|
31
|
+
c.y,
|
|
32
|
+
"A",
|
|
33
|
+
a,
|
|
34
|
+
a,
|
|
35
|
+
0,
|
|
36
|
+
m,
|
|
37
|
+
0,
|
|
38
|
+
d.x,
|
|
39
|
+
d.y
|
|
40
|
+
].join(" ");
|
|
41
|
+
};
|
|
42
|
+
function Pe({ cx: e, cy: t, radius: a, startAngle: r, endAngle: s, color: c, thickness: d }) {
|
|
43
|
+
const m = re(e, t, a, r, s);
|
|
44
|
+
return /* @__PURE__ */ i(
|
|
45
|
+
"path",
|
|
46
|
+
{
|
|
47
|
+
d: m,
|
|
48
|
+
fill: "none",
|
|
49
|
+
stroke: c,
|
|
50
|
+
strokeWidth: d,
|
|
51
|
+
strokeLinecap: "round"
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const L = b.memo(Pe);
|
|
56
|
+
function je({ cx: e, cy: t, radius: a, angle: r, color: s, width: c = 3 }) {
|
|
57
|
+
const d = N(e, t, a, r), m = N(e, t, 10, r - 90), f = N(e, t, 10, r + 90);
|
|
58
|
+
return /* @__PURE__ */ x("g", { className: "gauge-needle", "aria-hidden": "true", children: [
|
|
59
|
+
/* @__PURE__ */ i(
|
|
60
|
+
"path",
|
|
61
|
+
{
|
|
62
|
+
d: `M ${m.x} ${m.y} L ${d.x} ${d.y} L ${f.x} ${f.y} Z`,
|
|
63
|
+
fill: s
|
|
64
|
+
}
|
|
65
|
+
),
|
|
66
|
+
/* @__PURE__ */ i("circle", { cx: e, cy: t, r: c * 2, fill: s })
|
|
67
|
+
] });
|
|
68
|
+
}
|
|
69
|
+
const ye = b.memo(je);
|
|
70
|
+
function De({ ticks: e, color: t, labelColor: a, showLabels: r }) {
|
|
71
|
+
return /* @__PURE__ */ i("g", { className: "gauge-ticks", "aria-hidden": "true", children: e.map((s) => /* @__PURE__ */ x("g", { children: [
|
|
72
|
+
/* @__PURE__ */ i(
|
|
73
|
+
"line",
|
|
74
|
+
{
|
|
75
|
+
x1: s.outer.x,
|
|
76
|
+
y1: s.outer.y,
|
|
77
|
+
x2: s.inner.x,
|
|
78
|
+
y2: s.inner.y,
|
|
79
|
+
stroke: t,
|
|
80
|
+
strokeWidth: "2"
|
|
81
|
+
}
|
|
82
|
+
),
|
|
83
|
+
r && /* @__PURE__ */ i(
|
|
84
|
+
"text",
|
|
85
|
+
{
|
|
86
|
+
x: s.label.x,
|
|
87
|
+
y: s.label.y,
|
|
88
|
+
textAnchor: "middle",
|
|
89
|
+
dominantBaseline: "middle",
|
|
90
|
+
fill: a,
|
|
91
|
+
fontSize: "10",
|
|
92
|
+
children: Math.round(s.value)
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
] }, s.value)) });
|
|
96
|
+
}
|
|
97
|
+
const xe = b.memo(De);
|
|
98
|
+
function Ee({ cx: e, cy: t, value: a, target: r, units: s, showTarget: c }) {
|
|
99
|
+
return /* @__PURE__ */ x("g", { className: "gauge-value", "aria-hidden": "true", children: [
|
|
100
|
+
/* @__PURE__ */ x(
|
|
101
|
+
"text",
|
|
102
|
+
{
|
|
103
|
+
x: e,
|
|
104
|
+
y: t,
|
|
105
|
+
textAnchor: "middle",
|
|
106
|
+
dominantBaseline: "middle",
|
|
107
|
+
fontSize: "20",
|
|
108
|
+
children: [
|
|
109
|
+
a,
|
|
110
|
+
s ? ` ${s}` : ""
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
),
|
|
114
|
+
c && typeof r == "number" && /* @__PURE__ */ x(
|
|
115
|
+
"text",
|
|
116
|
+
{
|
|
117
|
+
x: e,
|
|
118
|
+
y: t + 22,
|
|
119
|
+
textAnchor: "middle",
|
|
120
|
+
dominantBaseline: "middle",
|
|
121
|
+
fontSize: "11",
|
|
122
|
+
children: [
|
|
123
|
+
"Target ",
|
|
124
|
+
r
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
] });
|
|
129
|
+
}
|
|
130
|
+
const K = b.memo(Ee);
|
|
131
|
+
function qe({ cx: e, cy: t, radius: a, angle: r, color: s }) {
|
|
132
|
+
const c = N(e, t, a, r);
|
|
133
|
+
return /* @__PURE__ */ i("circle", { cx: c.x, cy: c.y, r: "3", fill: s });
|
|
134
|
+
}
|
|
135
|
+
const oe = b.memo(qe), ce = (e, t, a) => Math.min(Math.max(e, t), a), Ae = (e, t, a) => e + (t - e) * a, pe = (e, t, a) => a === t ? 0 : (e - t) / (a - t), T = (e, t, a, r, s) => {
|
|
136
|
+
const c = ce(e, t, a), d = pe(c, t, a);
|
|
137
|
+
return Ae(r, s, d);
|
|
138
|
+
}, Ie = (e, t, a) => {
|
|
139
|
+
if (!Array.isArray(t) || t.length === 0) return a;
|
|
140
|
+
const r = [...t].filter((c) => typeof (c == null ? void 0 : c.value) == "number").sort((c, d) => c.value - d.value), s = r.find((c) => e <= c.value) || r[r.length - 1];
|
|
141
|
+
return (s == null ? void 0 : s.color) || a;
|
|
142
|
+
}, _e = (e, t, a) => !Array.isArray(e) || e.length === 0 ? [
|
|
143
|
+
{ value: t + (a - t) * 0.6, color: "var(--gauge-range-poor)" },
|
|
144
|
+
{ value: t + (a - t) * 0.8, color: "var(--gauge-range-warning)" },
|
|
145
|
+
{ value: a, color: "var(--gauge-range-good)" }
|
|
146
|
+
] : e.map((r) => ({
|
|
147
|
+
value: typeof r.value == "number" ? r.value : a,
|
|
148
|
+
color: r.color || "var(--gauge-range-good)",
|
|
149
|
+
label: r.label || ""
|
|
150
|
+
}));
|
|
151
|
+
function O({
|
|
152
|
+
value: e,
|
|
153
|
+
min: t,
|
|
154
|
+
max: a,
|
|
155
|
+
startAngle: r,
|
|
156
|
+
endAngle: s,
|
|
157
|
+
size: c,
|
|
158
|
+
thickness: d,
|
|
159
|
+
thresholds: m,
|
|
160
|
+
tickCount: f = 5,
|
|
161
|
+
arcPadding: y = 0
|
|
162
|
+
}) {
|
|
163
|
+
return ae(() => {
|
|
164
|
+
const o = Number.isFinite(t) ? t : 0, n = Number.isFinite(a) ? a : 100, g = Number.isFinite(e) ? e : o, u = ce(g, o, n), v = Number.isFinite(r) ? r : -90, A = Number.isFinite(s) ? s : 90, z = Number.isFinite(c) ? c : 200, V = Number.isFinite(d) ? d : 18, w = z / 2, k = z / 2, R = z / 2, B = R - V, P = (R + B) / 2, j = T(u, o, n, v, A), W = _e(m, o, n);
|
|
165
|
+
let D = o;
|
|
166
|
+
const Z = W.map((F) => {
|
|
167
|
+
const M = D, $ = F.value;
|
|
168
|
+
return D = $, {
|
|
169
|
+
...F,
|
|
170
|
+
startValue: M,
|
|
171
|
+
endValue: $,
|
|
172
|
+
startAngle: T(M, o, n, v, A),
|
|
173
|
+
endAngle: T($, o, n, v, A)
|
|
174
|
+
};
|
|
175
|
+
}), H = f > 1 ? (n - o) / (f - 1) : 0, E = Array.from({ length: f }, (F, M) => {
|
|
176
|
+
const $ = o + H * M, l = T($, o, n, v, A), Y = N(w, k, R - y, l), ee = N(w, k, R - y - 10, l), te = N(w, k, B - 18, l);
|
|
177
|
+
return { value: $, angle: l, outer: Y, inner: ee, label: te };
|
|
178
|
+
}), J = re(w, k, P, v, A), U = pe(u, o, n), q = T(u, o, n, v, A), X = re(w, k, P, v, q);
|
|
179
|
+
return {
|
|
180
|
+
cx: w,
|
|
181
|
+
cy: k,
|
|
182
|
+
outerRadius: R,
|
|
183
|
+
innerRadius: B,
|
|
184
|
+
midRadius: P,
|
|
185
|
+
value: u,
|
|
186
|
+
valueAngle: j,
|
|
187
|
+
startAngle: v,
|
|
188
|
+
endAngle: A,
|
|
189
|
+
arcPath: J,
|
|
190
|
+
progressPath: X,
|
|
191
|
+
progressAngle: q,
|
|
192
|
+
valueProgress: U,
|
|
193
|
+
ranges: Z,
|
|
194
|
+
ticks: E,
|
|
195
|
+
size: z,
|
|
196
|
+
thickness: V
|
|
197
|
+
};
|
|
198
|
+
}, [
|
|
199
|
+
e,
|
|
200
|
+
t,
|
|
201
|
+
a,
|
|
202
|
+
r,
|
|
203
|
+
s,
|
|
204
|
+
c,
|
|
205
|
+
d,
|
|
206
|
+
m,
|
|
207
|
+
f,
|
|
208
|
+
y
|
|
209
|
+
]);
|
|
210
|
+
}
|
|
211
|
+
function Ke({
|
|
212
|
+
value: e,
|
|
213
|
+
min: t,
|
|
214
|
+
max: a,
|
|
215
|
+
startAngle: r,
|
|
216
|
+
endAngle: s,
|
|
217
|
+
size: c,
|
|
218
|
+
thickness: d,
|
|
219
|
+
thresholds: m,
|
|
220
|
+
showTicks: f,
|
|
221
|
+
showValue: y,
|
|
222
|
+
showTarget: o,
|
|
223
|
+
target: n,
|
|
224
|
+
units: g
|
|
225
|
+
}) {
|
|
226
|
+
const u = O({
|
|
227
|
+
value: e,
|
|
228
|
+
min: t,
|
|
229
|
+
max: a,
|
|
230
|
+
startAngle: r,
|
|
231
|
+
endAngle: s,
|
|
232
|
+
size: c,
|
|
233
|
+
thickness: d,
|
|
234
|
+
thresholds: m,
|
|
235
|
+
tickCount: 6,
|
|
236
|
+
arcPadding: 2
|
|
237
|
+
}), v = typeof n == "number" ? T(n, u.min, u.max, u.startAngle, u.endAngle) : null;
|
|
238
|
+
return /* @__PURE__ */ x("svg", { width: u.size, height: u.size, viewBox: `0 0 ${u.size} ${u.size}`, children: [
|
|
239
|
+
/* @__PURE__ */ i("g", { className: "gauge-ranges", children: u.ranges.map((A) => /* @__PURE__ */ i(
|
|
240
|
+
L,
|
|
241
|
+
{
|
|
242
|
+
cx: u.cx,
|
|
243
|
+
cy: u.cy,
|
|
244
|
+
radius: u.midRadius,
|
|
245
|
+
startAngle: A.startAngle,
|
|
246
|
+
endAngle: A.endAngle,
|
|
247
|
+
color: A.color,
|
|
248
|
+
thickness: u.thickness
|
|
249
|
+
},
|
|
250
|
+
A.endValue
|
|
251
|
+
)) }),
|
|
252
|
+
f && /* @__PURE__ */ i(
|
|
253
|
+
xe,
|
|
254
|
+
{
|
|
255
|
+
ticks: u.ticks,
|
|
256
|
+
color: "var(--gauge-tick)",
|
|
257
|
+
labelColor: "var(--gauge-text-muted)",
|
|
258
|
+
showLabels: !0
|
|
259
|
+
}
|
|
260
|
+
),
|
|
261
|
+
typeof v == "number" && o && /* @__PURE__ */ i(
|
|
262
|
+
oe,
|
|
263
|
+
{
|
|
264
|
+
cx: u.cx,
|
|
265
|
+
cy: u.cy,
|
|
266
|
+
radius: u.outerRadius - 4,
|
|
267
|
+
angle: v,
|
|
268
|
+
color: "var(--gauge-target)"
|
|
269
|
+
}
|
|
270
|
+
),
|
|
271
|
+
/* @__PURE__ */ i(
|
|
272
|
+
ye,
|
|
273
|
+
{
|
|
274
|
+
cx: u.cx,
|
|
275
|
+
cy: u.cy,
|
|
276
|
+
radius: u.innerRadius - 4,
|
|
277
|
+
angle: u.valueAngle,
|
|
278
|
+
color: "var(--gauge-needle)"
|
|
279
|
+
}
|
|
280
|
+
),
|
|
281
|
+
y && /* @__PURE__ */ i(
|
|
282
|
+
K,
|
|
283
|
+
{
|
|
284
|
+
cx: u.cx,
|
|
285
|
+
cy: u.cy + 28,
|
|
286
|
+
value: Math.round(u.value),
|
|
287
|
+
target: n,
|
|
288
|
+
units: g,
|
|
289
|
+
showTarget: o
|
|
290
|
+
}
|
|
291
|
+
)
|
|
292
|
+
] });
|
|
293
|
+
}
|
|
294
|
+
const Oe = b.memo(Ke);
|
|
295
|
+
function We({
|
|
296
|
+
value: e,
|
|
297
|
+
min: t,
|
|
298
|
+
max: a,
|
|
299
|
+
startAngle: r,
|
|
300
|
+
endAngle: s,
|
|
301
|
+
size: c,
|
|
302
|
+
thickness: d,
|
|
303
|
+
thresholds: m,
|
|
304
|
+
showTicks: f,
|
|
305
|
+
showValue: y,
|
|
306
|
+
units: o
|
|
307
|
+
}) {
|
|
308
|
+
const n = O({
|
|
309
|
+
value: e,
|
|
310
|
+
min: t,
|
|
311
|
+
max: a,
|
|
312
|
+
startAngle: r,
|
|
313
|
+
endAngle: s,
|
|
314
|
+
size: c,
|
|
315
|
+
thickness: d,
|
|
316
|
+
thresholds: m,
|
|
317
|
+
tickCount: 4,
|
|
318
|
+
arcPadding: 4
|
|
319
|
+
});
|
|
320
|
+
return /* @__PURE__ */ x("svg", { width: n.size, height: n.size / 1.2, viewBox: `0 0 ${n.size} ${n.size}`, children: [
|
|
321
|
+
/* @__PURE__ */ i("g", { className: "gauge-ranges", children: n.ranges.map((g) => /* @__PURE__ */ i(
|
|
322
|
+
L,
|
|
323
|
+
{
|
|
324
|
+
cx: n.cx,
|
|
325
|
+
cy: n.cy,
|
|
326
|
+
radius: n.midRadius,
|
|
327
|
+
startAngle: g.startAngle,
|
|
328
|
+
endAngle: g.endAngle,
|
|
329
|
+
color: g.color,
|
|
330
|
+
thickness: n.thickness
|
|
331
|
+
},
|
|
332
|
+
g.endValue
|
|
333
|
+
)) }),
|
|
334
|
+
f && /* @__PURE__ */ i(
|
|
335
|
+
xe,
|
|
336
|
+
{
|
|
337
|
+
ticks: n.ticks,
|
|
338
|
+
color: "var(--gauge-tick)",
|
|
339
|
+
labelColor: "var(--gauge-text-muted)",
|
|
340
|
+
showLabels: !1
|
|
341
|
+
}
|
|
342
|
+
),
|
|
343
|
+
y && /* @__PURE__ */ i(
|
|
344
|
+
K,
|
|
345
|
+
{
|
|
346
|
+
cx: n.cx,
|
|
347
|
+
cy: n.cy + 14,
|
|
348
|
+
value: Math.round(n.value),
|
|
349
|
+
units: o,
|
|
350
|
+
showTarget: !1
|
|
351
|
+
}
|
|
352
|
+
)
|
|
353
|
+
] });
|
|
354
|
+
}
|
|
355
|
+
const Ze = b.memo(We);
|
|
356
|
+
function He({
|
|
357
|
+
value: e,
|
|
358
|
+
min: t,
|
|
359
|
+
max: a,
|
|
360
|
+
startAngle: r,
|
|
361
|
+
endAngle: s,
|
|
362
|
+
size: c,
|
|
363
|
+
thickness: d,
|
|
364
|
+
thresholds: m,
|
|
365
|
+
showValue: f,
|
|
366
|
+
units: y
|
|
367
|
+
}) {
|
|
368
|
+
const o = O({
|
|
369
|
+
value: e,
|
|
370
|
+
min: t,
|
|
371
|
+
max: a,
|
|
372
|
+
startAngle: r,
|
|
373
|
+
endAngle: s,
|
|
374
|
+
size: c,
|
|
375
|
+
thickness: d,
|
|
376
|
+
thresholds: m,
|
|
377
|
+
tickCount: 8,
|
|
378
|
+
arcPadding: 0
|
|
379
|
+
});
|
|
380
|
+
return /* @__PURE__ */ x("svg", { width: o.size, height: o.size, viewBox: `0 0 ${o.size} ${o.size}`, children: [
|
|
381
|
+
/* @__PURE__ */ i("g", { className: "gauge-ranges", children: o.ranges.map((n) => /* @__PURE__ */ i(
|
|
382
|
+
L,
|
|
383
|
+
{
|
|
384
|
+
cx: o.cx,
|
|
385
|
+
cy: o.cy,
|
|
386
|
+
radius: o.midRadius,
|
|
387
|
+
startAngle: n.startAngle,
|
|
388
|
+
endAngle: n.endAngle,
|
|
389
|
+
color: n.color,
|
|
390
|
+
thickness: o.thickness
|
|
391
|
+
},
|
|
392
|
+
n.endValue
|
|
393
|
+
)) }),
|
|
394
|
+
/* @__PURE__ */ i(
|
|
395
|
+
ye,
|
|
396
|
+
{
|
|
397
|
+
cx: o.cx,
|
|
398
|
+
cy: o.cy,
|
|
399
|
+
radius: o.innerRadius - 2,
|
|
400
|
+
angle: o.valueAngle,
|
|
401
|
+
color: "var(--gauge-needle)"
|
|
402
|
+
}
|
|
403
|
+
),
|
|
404
|
+
f && /* @__PURE__ */ i(
|
|
405
|
+
K,
|
|
406
|
+
{
|
|
407
|
+
cx: o.cx,
|
|
408
|
+
cy: o.cy + 24,
|
|
409
|
+
value: Math.round(o.value),
|
|
410
|
+
units: y,
|
|
411
|
+
showTarget: !1
|
|
412
|
+
}
|
|
413
|
+
)
|
|
414
|
+
] });
|
|
415
|
+
}
|
|
416
|
+
const Je = b.memo(He);
|
|
417
|
+
function Qe({
|
|
418
|
+
value: e,
|
|
419
|
+
min: t,
|
|
420
|
+
max: a,
|
|
421
|
+
startAngle: r,
|
|
422
|
+
endAngle: s,
|
|
423
|
+
size: c,
|
|
424
|
+
thickness: d,
|
|
425
|
+
thresholds: m,
|
|
426
|
+
showValue: f,
|
|
427
|
+
units: y,
|
|
428
|
+
gradientId: o
|
|
429
|
+
}) {
|
|
430
|
+
const n = O({
|
|
431
|
+
value: e,
|
|
432
|
+
min: t,
|
|
433
|
+
max: a,
|
|
434
|
+
startAngle: r,
|
|
435
|
+
endAngle: s,
|
|
436
|
+
size: c,
|
|
437
|
+
thickness: d,
|
|
438
|
+
thresholds: m,
|
|
439
|
+
tickCount: 0,
|
|
440
|
+
arcPadding: 0
|
|
441
|
+
});
|
|
442
|
+
return /* @__PURE__ */ x("svg", { width: n.size, height: n.size, viewBox: `0 0 ${n.size} ${n.size}`, children: [
|
|
443
|
+
o && /* @__PURE__ */ i("defs", { children: /* @__PURE__ */ x("linearGradient", { id: o, x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: [
|
|
444
|
+
/* @__PURE__ */ i("stop", { offset: "0%", stopColor: "var(--gauge-range-poor)" }),
|
|
445
|
+
/* @__PURE__ */ i("stop", { offset: "55%", stopColor: "var(--gauge-range-warning)" }),
|
|
446
|
+
/* @__PURE__ */ i("stop", { offset: "100%", stopColor: "var(--gauge-range-good)" })
|
|
447
|
+
] }) }),
|
|
448
|
+
/* @__PURE__ */ i(
|
|
449
|
+
L,
|
|
450
|
+
{
|
|
451
|
+
cx: n.cx,
|
|
452
|
+
cy: n.cy,
|
|
453
|
+
radius: n.midRadius,
|
|
454
|
+
startAngle: n.startAngle,
|
|
455
|
+
endAngle: n.endAngle,
|
|
456
|
+
color: "var(--gauge-track)",
|
|
457
|
+
thickness: n.thickness
|
|
458
|
+
}
|
|
459
|
+
),
|
|
460
|
+
/* @__PURE__ */ i(
|
|
461
|
+
L,
|
|
462
|
+
{
|
|
463
|
+
cx: n.cx,
|
|
464
|
+
cy: n.cy,
|
|
465
|
+
radius: n.midRadius,
|
|
466
|
+
startAngle: n.startAngle,
|
|
467
|
+
endAngle: n.progressAngle,
|
|
468
|
+
color: o ? `url(#${o})` : "var(--gauge-progress)",
|
|
469
|
+
thickness: n.thickness
|
|
470
|
+
}
|
|
471
|
+
),
|
|
472
|
+
f && /* @__PURE__ */ i(
|
|
473
|
+
K,
|
|
474
|
+
{
|
|
475
|
+
cx: n.cx,
|
|
476
|
+
cy: n.cy + 8,
|
|
477
|
+
value: Math.round(n.value),
|
|
478
|
+
units: y,
|
|
479
|
+
showTarget: !1
|
|
480
|
+
}
|
|
481
|
+
)
|
|
482
|
+
] });
|
|
483
|
+
}
|
|
484
|
+
const Ue = b.memo(Qe);
|
|
485
|
+
function Xe({
|
|
486
|
+
value: e,
|
|
487
|
+
min: t,
|
|
488
|
+
max: a,
|
|
489
|
+
startAngle: r,
|
|
490
|
+
endAngle: s,
|
|
491
|
+
size: c,
|
|
492
|
+
thickness: d,
|
|
493
|
+
thresholds: m,
|
|
494
|
+
showValue: f,
|
|
495
|
+
units: y,
|
|
496
|
+
target: o,
|
|
497
|
+
showTarget: n
|
|
498
|
+
}) {
|
|
499
|
+
const g = O({
|
|
500
|
+
value: e,
|
|
501
|
+
min: t,
|
|
502
|
+
max: a,
|
|
503
|
+
startAngle: r,
|
|
504
|
+
endAngle: s,
|
|
505
|
+
size: c,
|
|
506
|
+
thickness: d,
|
|
507
|
+
thresholds: m,
|
|
508
|
+
tickCount: 0,
|
|
509
|
+
arcPadding: 0
|
|
510
|
+
}), u = typeof o == "number" ? T(o, g.min, g.max, g.startAngle, g.endAngle) : null;
|
|
511
|
+
return /* @__PURE__ */ x("svg", { width: g.size, height: g.size, viewBox: `0 0 ${g.size} ${g.size}`, children: [
|
|
512
|
+
/* @__PURE__ */ i("g", { className: "gauge-ranges", children: g.ranges.map((v) => /* @__PURE__ */ i(
|
|
513
|
+
L,
|
|
514
|
+
{
|
|
515
|
+
cx: g.cx,
|
|
516
|
+
cy: g.cy,
|
|
517
|
+
radius: g.midRadius,
|
|
518
|
+
startAngle: v.startAngle,
|
|
519
|
+
endAngle: v.endAngle,
|
|
520
|
+
color: v.color,
|
|
521
|
+
thickness: g.thickness
|
|
522
|
+
},
|
|
523
|
+
v.endValue
|
|
524
|
+
)) }),
|
|
525
|
+
/* @__PURE__ */ i(
|
|
526
|
+
oe,
|
|
527
|
+
{
|
|
528
|
+
cx: g.cx,
|
|
529
|
+
cy: g.cy,
|
|
530
|
+
radius: g.outerRadius - 4,
|
|
531
|
+
angle: g.valueAngle,
|
|
532
|
+
color: "var(--gauge-needle)"
|
|
533
|
+
}
|
|
534
|
+
),
|
|
535
|
+
n && typeof u == "number" && /* @__PURE__ */ i(
|
|
536
|
+
oe,
|
|
537
|
+
{
|
|
538
|
+
cx: g.cx,
|
|
539
|
+
cy: g.cy,
|
|
540
|
+
radius: g.outerRadius - 4,
|
|
541
|
+
angle: u,
|
|
542
|
+
color: "var(--gauge-target)"
|
|
543
|
+
}
|
|
544
|
+
),
|
|
545
|
+
f && /* @__PURE__ */ i(
|
|
546
|
+
K,
|
|
547
|
+
{
|
|
548
|
+
cx: g.cx,
|
|
549
|
+
cy: g.cy + 8,
|
|
550
|
+
value: Math.round(g.value),
|
|
551
|
+
units: y,
|
|
552
|
+
target: o,
|
|
553
|
+
showTarget: n
|
|
554
|
+
}
|
|
555
|
+
)
|
|
556
|
+
] });
|
|
557
|
+
}
|
|
558
|
+
const Ye = b.memo(Xe), me = () => typeof performance < "u" ? performance.now() : Date.now();
|
|
559
|
+
function et(e, { enabled: t, duration: a = 500, easing: r } = {}) {
|
|
560
|
+
const s = Q(null), c = Q(0), d = Q(e), [m, f] = G(e);
|
|
561
|
+
return se(() => {
|
|
562
|
+
if (!t)
|
|
563
|
+
return f(e), d.current = e, () => {
|
|
564
|
+
};
|
|
565
|
+
const y = d.current, o = me();
|
|
566
|
+
c.current = o;
|
|
567
|
+
const n = typeof r == "function" ? r : (u) => 1 - Math.pow(1 - u, 3), g = () => {
|
|
568
|
+
const u = Math.min(1, (me() - o) / a), v = Ae(y, e, n(u));
|
|
569
|
+
f(v), u < 1 ? s.current = requestAnimationFrame(g) : d.current = e;
|
|
570
|
+
};
|
|
571
|
+
return s.current = requestAnimationFrame(g), () => {
|
|
572
|
+
s.current && cancelAnimationFrame(s.current);
|
|
573
|
+
};
|
|
574
|
+
}, [e, t, a, r]), m;
|
|
575
|
+
}
|
|
576
|
+
const tt = (...e) => e.filter(Boolean).join(" "), nt = null, fe = {
|
|
577
|
+
standard: { start: -90, end: 90 },
|
|
578
|
+
semicircle: { start: -110, end: 110 },
|
|
579
|
+
full: { start: 0, end: 360 },
|
|
580
|
+
arc: { start: -225, end: 45 },
|
|
581
|
+
multiband: { start: -120, end: 120 }
|
|
582
|
+
}, ve = (e) => e ? Array.isArray(e) ? e[0] || null : Array.isArray(e.data) ? e.data[0] || null : typeof e == "object" ? e : null : null, p = (e, t) => e !== void 0 ? e : t, at = (e, t, a, r) => {
|
|
583
|
+
const s = _e(t, a, r);
|
|
584
|
+
for (let c = 0; c < s.length; c += 1)
|
|
585
|
+
if (e <= s[c].value) return c;
|
|
586
|
+
return s.length - 1;
|
|
587
|
+
};
|
|
588
|
+
function ot({
|
|
589
|
+
type: e = "standard",
|
|
590
|
+
value: t,
|
|
591
|
+
min: a,
|
|
592
|
+
max: r,
|
|
593
|
+
target: s,
|
|
594
|
+
thresholds: c,
|
|
595
|
+
showTicks: d = !0,
|
|
596
|
+
showValue: m = !0,
|
|
597
|
+
showTarget: f = !1,
|
|
598
|
+
animated: y = !0,
|
|
599
|
+
realtime: o = !1,
|
|
600
|
+
size: n = 220,
|
|
601
|
+
thickness: g = 18,
|
|
602
|
+
startAngle: u,
|
|
603
|
+
endAngle: v,
|
|
604
|
+
units: A = "",
|
|
605
|
+
data: z = nt,
|
|
606
|
+
apiEndpoint: V = "/gauge-data.json",
|
|
607
|
+
title: w = "KPI Gauge",
|
|
608
|
+
subtitle: k = "",
|
|
609
|
+
theme: R = "light",
|
|
610
|
+
className: B,
|
|
611
|
+
style: P,
|
|
612
|
+
onClick: j
|
|
613
|
+
}) {
|
|
614
|
+
const [W, D] = G(null), [Z, H] = G(!1), [E, J] = G(""), [U, q] = G(!1), [X, F] = G(!1), M = Q(null), $ = ke(async () => {
|
|
615
|
+
if (V) {
|
|
616
|
+
H(!0), J("");
|
|
617
|
+
try {
|
|
618
|
+
const h = await fetch(V);
|
|
619
|
+
if (!h.ok)
|
|
620
|
+
throw new Error(`Request failed: ${h.status}`);
|
|
621
|
+
const S = await h.json();
|
|
622
|
+
D(ve(S));
|
|
623
|
+
} catch (h) {
|
|
624
|
+
J("Failed to load gauge data."), console.error("Failed to load gauge data", h);
|
|
625
|
+
} finally {
|
|
626
|
+
H(!1);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}, [V]);
|
|
630
|
+
se(() => {
|
|
631
|
+
if (z) {
|
|
632
|
+
D(ve(z));
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
$();
|
|
636
|
+
}, [z, $]);
|
|
637
|
+
const l = ae(() => {
|
|
638
|
+
const h = W || {}, S = String(e || h.type || "standard").toLowerCase(), le = fe[S] || fe.standard, ie = p(a, h.min), ue = p(r, h.max), ne = Number.isFinite(ie) ? ie : 0, ge = Number.isFinite(ue) ? ue : 100, de = p(t, h.value), be = Number.isFinite(de) ? de : ne, he = p(s, h.target), we = Number.isFinite(he) ? he : null;
|
|
639
|
+
return {
|
|
640
|
+
type: S,
|
|
641
|
+
value: ce(be, ne, ge),
|
|
642
|
+
min: ne,
|
|
643
|
+
max: ge,
|
|
644
|
+
target: we,
|
|
645
|
+
thresholds: p(c, h.thresholds),
|
|
646
|
+
showTicks: p(d, h.showTicks ?? d),
|
|
647
|
+
showValue: p(m, h.showValue ?? m),
|
|
648
|
+
showTarget: p(f, h.showTarget ?? f),
|
|
649
|
+
size: p(n, h.size ?? n),
|
|
650
|
+
thickness: p(g, h.thickness ?? g),
|
|
651
|
+
units: p(A, h.units ?? A),
|
|
652
|
+
startAngle: p(u, h.startAngle ?? le.start),
|
|
653
|
+
endAngle: p(v, h.endAngle ?? le.end)
|
|
654
|
+
};
|
|
655
|
+
}, [
|
|
656
|
+
W,
|
|
657
|
+
e,
|
|
658
|
+
a,
|
|
659
|
+
r,
|
|
660
|
+
t,
|
|
661
|
+
s,
|
|
662
|
+
c,
|
|
663
|
+
d,
|
|
664
|
+
m,
|
|
665
|
+
f,
|
|
666
|
+
n,
|
|
667
|
+
g,
|
|
668
|
+
A,
|
|
669
|
+
u,
|
|
670
|
+
v
|
|
671
|
+
]), Y = et(l.value, {
|
|
672
|
+
enabled: y || o,
|
|
673
|
+
duration: o ? 220 : 500
|
|
674
|
+
});
|
|
675
|
+
se(() => {
|
|
676
|
+
if (!o) return;
|
|
677
|
+
const h = at(l.value, l.thresholds, l.min, l.max);
|
|
678
|
+
if (M.current === null) {
|
|
679
|
+
M.current = h;
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (M.current !== h) {
|
|
683
|
+
M.current = h, q(!0);
|
|
684
|
+
const S = setTimeout(() => q(!1), 300);
|
|
685
|
+
return () => clearTimeout(S);
|
|
686
|
+
}
|
|
687
|
+
}, [o, l.value, l.thresholds, l.min, l.max]);
|
|
688
|
+
const ee = Ie(
|
|
689
|
+
l.value,
|
|
690
|
+
l.thresholds,
|
|
691
|
+
"var(--gauge-range-good)"
|
|
692
|
+
), te = ae(() => {
|
|
693
|
+
const h = [
|
|
694
|
+
`Value: ${Math.round(l.value)}${l.units ? ` ${l.units}` : ""}`,
|
|
695
|
+
`Min: ${l.min}`,
|
|
696
|
+
`Max: ${l.max}`
|
|
697
|
+
];
|
|
698
|
+
return typeof l.target == "number" && h.splice(1, 0, `Target: ${l.target}`), h.join(" • ");
|
|
699
|
+
}, [l.value, l.units, l.min, l.max, l.target]), I = {
|
|
700
|
+
value: Y,
|
|
701
|
+
min: l.min,
|
|
702
|
+
max: l.max,
|
|
703
|
+
startAngle: l.startAngle,
|
|
704
|
+
endAngle: l.endAngle,
|
|
705
|
+
size: l.size,
|
|
706
|
+
thickness: l.thickness,
|
|
707
|
+
thresholds: l.thresholds,
|
|
708
|
+
showTicks: l.showTicks,
|
|
709
|
+
showValue: l.showValue,
|
|
710
|
+
showTarget: l.showTarget,
|
|
711
|
+
target: l.target,
|
|
712
|
+
units: l.units
|
|
713
|
+
}, C = l.type;
|
|
714
|
+
return /* @__PURE__ */ x(
|
|
715
|
+
"section",
|
|
716
|
+
{
|
|
717
|
+
className: tt(_.gaugeCard, _[R], U && _.flash, B),
|
|
718
|
+
style: { ...P, "--gauge-active": ee },
|
|
719
|
+
onClick: () => j == null ? void 0 : j(l.value),
|
|
720
|
+
onMouseEnter: () => F(!0),
|
|
721
|
+
onMouseLeave: () => F(!1),
|
|
722
|
+
role: "figure",
|
|
723
|
+
"aria-label": w,
|
|
724
|
+
"data-tooltip": te,
|
|
725
|
+
"data-tooltip-open": X ? "true" : "false",
|
|
726
|
+
children: [
|
|
727
|
+
/* @__PURE__ */ x("header", { className: _.header, children: [
|
|
728
|
+
/* @__PURE__ */ x("div", { children: [
|
|
729
|
+
/* @__PURE__ */ i("div", { className: _.title, children: w }),
|
|
730
|
+
k && /* @__PURE__ */ i("div", { className: _.subtitle, children: k })
|
|
731
|
+
] }),
|
|
732
|
+
/* @__PURE__ */ x("div", { className: _.meta, children: [
|
|
733
|
+
/* @__PURE__ */ i("span", { children: Math.round(l.value) }),
|
|
734
|
+
l.units && /* @__PURE__ */ i("span", { className: _.units, children: l.units })
|
|
735
|
+
] })
|
|
736
|
+
] }),
|
|
737
|
+
/* @__PURE__ */ x("div", { className: _.body, children: [
|
|
738
|
+
Z && /* @__PURE__ */ i("div", { className: _.state, children: "Loading KPI..." }),
|
|
739
|
+
E && /* @__PURE__ */ i("div", { className: _.state, children: E }),
|
|
740
|
+
!Z && !E && /* @__PURE__ */ x("div", { className: _.gaugeStage, children: [
|
|
741
|
+
C === "semicircle" && /* @__PURE__ */ i(Ze, { ...I }),
|
|
742
|
+
C === "full" && /* @__PURE__ */ i(Je, { ...I }),
|
|
743
|
+
C === "arc" && /* @__PURE__ */ i(Ue, { ...I, gradientId: "gaugeGradient" }),
|
|
744
|
+
C === "multiband" && /* @__PURE__ */ i(Ye, { ...I }),
|
|
745
|
+
(C === "standard" || !C) && /* @__PURE__ */ i(Oe, { ...I })
|
|
746
|
+
] })
|
|
747
|
+
] })
|
|
748
|
+
]
|
|
749
|
+
}
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
export {
|
|
753
|
+
ot as AwesomeSpeedometerGauge
|
|
754
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: "Source Sans 3", "Segoe UI", system-ui, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
color: #0f172a;
|
|
6
|
+
background-color: #f1f5f9;
|
|
7
|
+
font-synthesis: none;
|
|
8
|
+
text-rendering: optimizeLegibility;
|
|
9
|
+
-webkit-font-smoothing: antialiased;
|
|
10
|
+
-moz-osx-font-smoothing: grayscale;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
a {
|
|
14
|
+
font-weight: 500;
|
|
15
|
+
color: inherit;
|
|
16
|
+
text-decoration: none;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
a:hover {
|
|
20
|
+
color: #2563eb;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
body {
|
|
24
|
+
margin: 0;
|
|
25
|
+
min-width: 320px;
|
|
26
|
+
min-height: 100vh;
|
|
27
|
+
background: linear-gradient(180deg, #f8fafc 0%, #e2e8f0 100%);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
* {
|
|
31
|
+
box-sizing: border-box;
|
|
32
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "awesome-speedometer-gauge",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "A fully-featured React KPI speedometer gauge with 5 visual modes (Standard, SemiCircle, FullCircle, Arc, MultiBand), animated needle, threshold colour bands, dark/light/purple themes, and live data fetching.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/cjs/index.js",
|
|
8
|
+
"module": "./dist/esm/index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/esm/index.js",
|
|
12
|
+
"require": "./dist/cjs/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./style.css": "./dist/style.css"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": [
|
|
20
|
+
"*.css"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "npm run build:esm && npm run build:cjs && npm run build:css",
|
|
24
|
+
"build:esm": "vite build --mode esm",
|
|
25
|
+
"build:cjs": "vite build --mode cjs",
|
|
26
|
+
"build:css": "node -e \"require('fs').copyFileSync('src/style.css','dist/style.css')\"",
|
|
27
|
+
"dev": "vite",
|
|
28
|
+
"prepublishOnly": "npm run build"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"react",
|
|
32
|
+
"gauge",
|
|
33
|
+
"speedometer",
|
|
34
|
+
"kpi",
|
|
35
|
+
"dashboard",
|
|
36
|
+
"chart",
|
|
37
|
+
"arc",
|
|
38
|
+
"needle",
|
|
39
|
+
"animation",
|
|
40
|
+
"component"
|
|
41
|
+
],
|
|
42
|
+
"author": "Pravinkumar Dabade",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"react": ">=18.0.0",
|
|
46
|
+
"react-dom": ">=18.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^25.5.0",
|
|
50
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
51
|
+
"vite": "^6.0.5"
|
|
52
|
+
}
|
|
53
|
+
}
|