@seedgrid/fe-components 0.2.9 → 2026.3.1

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 (155) hide show
  1. package/dist/buttons/SgFloatActionButton.d.ts.map +1 -1
  2. package/dist/buttons/SgFloatActionButton.js +168 -38
  3. package/dist/commons/SgAvatar.d.ts +66 -0
  4. package/dist/commons/SgAvatar.d.ts.map +1 -0
  5. package/dist/commons/SgAvatar.js +136 -0
  6. package/dist/commons/SgSkeleton.d.ts +16 -0
  7. package/dist/commons/SgSkeleton.d.ts.map +1 -0
  8. package/dist/commons/SgSkeleton.js +58 -0
  9. package/dist/commons/SgToaster.d.ts +9 -0
  10. package/dist/commons/SgToaster.d.ts.map +1 -1
  11. package/dist/commons/SgToaster.js +86 -17
  12. package/dist/digits/discard-digit/SgDiscardDigit.d.ts +39 -0
  13. package/dist/digits/discard-digit/SgDiscardDigit.d.ts.map +1 -0
  14. package/dist/digits/discard-digit/SgDiscardDigit.js +303 -0
  15. package/dist/digits/discard-digit/index.d.ts +3 -0
  16. package/dist/digits/discard-digit/index.d.ts.map +1 -0
  17. package/dist/digits/discard-digit/index.js +1 -0
  18. package/dist/digits/fade-digit/SgFadeDigit.d.ts +27 -0
  19. package/dist/digits/fade-digit/SgFadeDigit.d.ts.map +1 -0
  20. package/dist/digits/fade-digit/SgFadeDigit.js +85 -0
  21. package/dist/digits/fade-digit/index.d.ts +3 -0
  22. package/dist/digits/fade-digit/index.d.ts.map +1 -0
  23. package/dist/digits/fade-digit/index.js +1 -0
  24. package/dist/digits/flip-digit/SgFlipDigit.d.ts +27 -0
  25. package/dist/digits/flip-digit/SgFlipDigit.d.ts.map +1 -0
  26. package/dist/digits/flip-digit/SgFlipDigit.js +70 -0
  27. package/dist/digits/flip-digit/index.d.ts.map +1 -0
  28. package/dist/digits/matrix-digit/SgMatrixDigit.d.ts +32 -0
  29. package/dist/digits/matrix-digit/SgMatrixDigit.d.ts.map +1 -0
  30. package/dist/digits/matrix-digit/SgMatrixDigit.js +86 -0
  31. package/dist/digits/matrix-digit/index.d.ts +3 -0
  32. package/dist/digits/matrix-digit/index.d.ts.map +1 -0
  33. package/dist/digits/matrix-digit/index.js +1 -0
  34. package/dist/digits/neon-digit/SgNeonDigit.d.ts +37 -0
  35. package/dist/digits/neon-digit/SgNeonDigit.d.ts.map +1 -0
  36. package/dist/digits/neon-digit/SgNeonDigit.js +59 -0
  37. package/dist/digits/neon-digit/index.d.ts +3 -0
  38. package/dist/digits/neon-digit/index.d.ts.map +1 -0
  39. package/dist/digits/neon-digit/index.js +1 -0
  40. package/dist/digits/roller3d-digit/SgRoller3DDigit.d.ts +37 -0
  41. package/dist/digits/roller3d-digit/SgRoller3DDigit.d.ts.map +1 -0
  42. package/dist/digits/roller3d-digit/SgRoller3DDigit.js +47 -0
  43. package/dist/digits/roller3d-digit/index.d.ts +3 -0
  44. package/dist/digits/roller3d-digit/index.d.ts.map +1 -0
  45. package/dist/digits/roller3d-digit/index.js +1 -0
  46. package/dist/environment/SgEnvironmentProvider.d.ts +1 -0
  47. package/dist/environment/SgEnvironmentProvider.d.ts.map +1 -1
  48. package/dist/environment/SgEnvironmentProvider.js +51 -12
  49. package/dist/gadgets/clock/SgClock.d.ts +3 -1
  50. package/dist/gadgets/clock/SgClock.d.ts.map +1 -1
  51. package/dist/gadgets/clock/SgClock.js +111 -180
  52. package/dist/gadgets/clock/SgTimeProvider.d.ts +1 -0
  53. package/dist/gadgets/clock/SgTimeProvider.d.ts.map +1 -1
  54. package/dist/gadgets/clock/SgTimeProvider.js +11 -4
  55. package/dist/gadgets/gauge/SgLinearGauge.d.ts +59 -0
  56. package/dist/gadgets/gauge/SgLinearGauge.d.ts.map +1 -0
  57. package/dist/gadgets/gauge/SgLinearGauge.js +258 -0
  58. package/dist/gadgets/gauge/SgRadialGauge.d.ts +73 -0
  59. package/dist/gadgets/gauge/SgRadialGauge.d.ts.map +1 -0
  60. package/dist/gadgets/gauge/SgRadialGauge.js +311 -0
  61. package/dist/gadgets/gauge/index.d.ts +5 -0
  62. package/dist/gadgets/gauge/index.d.ts.map +1 -0
  63. package/dist/gadgets/gauge/index.js +2 -0
  64. package/dist/gadgets/qr-code/SgQRCode.d.ts +25 -0
  65. package/dist/gadgets/qr-code/SgQRCode.d.ts.map +1 -0
  66. package/dist/gadgets/qr-code/SgQRCode.js +75 -0
  67. package/dist/gadgets/qr-code/index.d.ts +3 -0
  68. package/dist/gadgets/qr-code/index.d.ts.map +1 -0
  69. package/dist/gadgets/qr-code/index.js +1 -0
  70. package/dist/gadgets/string-animator/SgStringAnimator.d.ts +91 -0
  71. package/dist/gadgets/string-animator/SgStringAnimator.d.ts.map +1 -0
  72. package/dist/gadgets/string-animator/SgStringAnimator.js +145 -0
  73. package/dist/gadgets/string-animator/index.d.ts +3 -0
  74. package/dist/gadgets/string-animator/index.d.ts.map +1 -0
  75. package/dist/gadgets/string-animator/index.js +1 -0
  76. package/dist/i18n/en-US.json +9 -1
  77. package/dist/i18n/es.json +55 -47
  78. package/dist/i18n/index.d.ts +32 -0
  79. package/dist/i18n/index.d.ts.map +1 -1
  80. package/dist/i18n/pt-BR.json +9 -1
  81. package/dist/i18n/pt-PT.json +9 -1
  82. package/dist/index.d.ts +53 -5
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +25 -1
  85. package/dist/inputs/SgAutocomplete.js +21 -5
  86. package/dist/inputs/SgCombobox.d.ts +26 -0
  87. package/dist/inputs/SgCombobox.d.ts.map +1 -0
  88. package/dist/inputs/SgCombobox.js +354 -0
  89. package/dist/inputs/SgInputOTP.d.ts.map +1 -1
  90. package/dist/inputs/SgInputOTP.js +9 -2
  91. package/dist/inputs/SgRadioGroup.d.ts +37 -0
  92. package/dist/inputs/SgRadioGroup.d.ts.map +1 -0
  93. package/dist/inputs/SgRadioGroup.js +139 -0
  94. package/dist/inputs/SgRating.d.ts +55 -0
  95. package/dist/inputs/SgRating.d.ts.map +1 -0
  96. package/dist/inputs/SgRating.js +135 -0
  97. package/dist/inputs/SgSlider.d.ts +20 -0
  98. package/dist/inputs/SgSlider.d.ts.map +1 -0
  99. package/dist/inputs/SgSlider.js +40 -0
  100. package/dist/inputs/SgStepperInput.d.ts +22 -0
  101. package/dist/inputs/SgStepperInput.d.ts.map +1 -0
  102. package/dist/inputs/SgStepperInput.js +51 -0
  103. package/dist/inputs/SgTextEditor.d.ts +1 -0
  104. package/dist/inputs/SgTextEditor.d.ts.map +1 -1
  105. package/dist/inputs/SgTextEditor.js +19 -3
  106. package/dist/inputs/SgToggleSwitch.d.ts +36 -0
  107. package/dist/inputs/SgToggleSwitch.d.ts.map +1 -0
  108. package/dist/inputs/SgToggleSwitch.js +174 -0
  109. package/dist/layout/SgAccordion.d.ts +39 -0
  110. package/dist/layout/SgAccordion.d.ts.map +1 -0
  111. package/dist/layout/SgAccordion.js +116 -0
  112. package/dist/layout/SgBreadcrumb.d.ts +33 -0
  113. package/dist/layout/SgBreadcrumb.d.ts.map +1 -0
  114. package/dist/layout/SgBreadcrumb.js +121 -0
  115. package/dist/layout/SgCarousel.d.ts +43 -0
  116. package/dist/layout/SgCarousel.d.ts.map +1 -0
  117. package/dist/layout/SgCarousel.js +166 -0
  118. package/dist/layout/SgDockLayout.d.ts +14 -0
  119. package/dist/layout/SgDockLayout.d.ts.map +1 -1
  120. package/dist/layout/SgDockLayout.js +145 -13
  121. package/dist/layout/SgDockScreen.d.ts +15 -0
  122. package/dist/layout/SgDockScreen.d.ts.map +1 -0
  123. package/dist/layout/SgDockScreen.js +13 -0
  124. package/dist/layout/SgDockZone.d.ts.map +1 -1
  125. package/dist/layout/SgDockZone.js +36 -2
  126. package/dist/layout/SgExpandablePanel.d.ts +50 -0
  127. package/dist/layout/SgExpandablePanel.d.ts.map +1 -0
  128. package/dist/layout/SgExpandablePanel.js +302 -0
  129. package/dist/layout/SgMainPanel.d.ts.map +1 -1
  130. package/dist/layout/SgMainPanel.js +36 -14
  131. package/dist/layout/SgMenu.d.ts +91 -0
  132. package/dist/layout/SgMenu.d.ts.map +1 -0
  133. package/dist/layout/SgMenu.js +939 -0
  134. package/dist/layout/SgPageControl.d.ts +49 -0
  135. package/dist/layout/SgPageControl.d.ts.map +1 -0
  136. package/dist/layout/SgPageControl.js +152 -0
  137. package/dist/layout/SgPanel.d.ts.map +1 -1
  138. package/dist/layout/SgPanel.js +10 -1
  139. package/dist/layout/SgScreen.d.ts +2 -0
  140. package/dist/layout/SgScreen.d.ts.map +1 -1
  141. package/dist/layout/SgScreen.js +4 -2
  142. package/dist/layout/SgToolBar.d.ts +9 -3
  143. package/dist/layout/SgToolBar.d.ts.map +1 -1
  144. package/dist/layout/SgToolBar.js +461 -55
  145. package/dist/menus/SgDockMenu.d.ts +62 -0
  146. package/dist/menus/SgDockMenu.d.ts.map +1 -0
  147. package/dist/menus/SgDockMenu.js +480 -0
  148. package/dist/others/SgPlayground.js +73 -73
  149. package/package.json +72 -57
  150. package/dist/gadgets/flip-digit/SgFlipDigit.d.ts +0 -23
  151. package/dist/gadgets/flip-digit/SgFlipDigit.d.ts.map +0 -1
  152. package/dist/gadgets/flip-digit/SgFlipDigit.js +0 -118
  153. package/dist/gadgets/flip-digit/index.d.ts.map +0 -1
  154. /package/dist/{gadgets → digits}/flip-digit/index.d.ts +0 -0
  155. /package/dist/{gadgets → digits}/flip-digit/index.js +0 -0
@@ -0,0 +1,258 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ function cn(...parts) {
5
+ return parts.filter(Boolean).join(" ");
6
+ }
7
+ function clamp(value, min, max) {
8
+ return Math.max(min, Math.min(max, value));
9
+ }
10
+ function toNumber(value, fallback) {
11
+ return Number.isFinite(value) ? value : fallback;
12
+ }
13
+ const MAIN_POINTER_ID = "__sg-linear-main-pointer__";
14
+ export function SgLinearGauge(props) {
15
+ const { min = 0, max = 100, value, defaultValue = min, onValueChange, pointers = [], onPointerValueChange, ranges = [], orientation = "horizontal", isAxisInversed = false, showPrimaryPointer = true, primaryPointerShape = "triangle", primaryPointerColor = "hsl(var(--primary))", primaryPointerSize = 11, primaryPointerDraggable = false, barPointer = true, barColor = "hsl(var(--primary))", barThickness = 8, showTicks = true, showLabels = true, majorTickCount = 5, minorTicksPerInterval = 1, labelFormatter, axisColor = "hsl(var(--border))", axisThickness = 10, width = orientation === "horizontal" ? 360 : 140, height = orientation === "horizontal" ? 120 : 360, animate = true, animationDuration = 350, className, style, ariaLabel = "Linear gauge" } = props;
16
+ const safeMin = toNumber(min, 0);
17
+ const safeMax = toNumber(max, 100);
18
+ const hasRange = safeMax > safeMin;
19
+ const span = hasRange ? safeMax - safeMin : 1;
20
+ const [innerValue, setInnerValue] = React.useState(defaultValue);
21
+ const isControlled = value !== undefined;
22
+ const currentValueRaw = isControlled ? value : innerValue;
23
+ const currentValue = clamp(currentValueRaw, safeMin, safeMax);
24
+ const setMainValue = React.useCallback((next) => {
25
+ const clamped = clamp(next, safeMin, safeMax);
26
+ if (!isControlled)
27
+ setInnerValue(clamped);
28
+ onValueChange?.(clamped);
29
+ }, [isControlled, onValueChange, safeMax, safeMin]);
30
+ React.useEffect(() => {
31
+ if (isControlled)
32
+ return;
33
+ setInnerValue((prev) => clamp(prev, safeMin, safeMax));
34
+ }, [isControlled, safeMin, safeMax]);
35
+ const pointerIds = React.useMemo(() => pointers.map((pointer, index) => pointer.id ?? `sg-linear-pointer-${index}`), [pointers]);
36
+ const [dragPointerValues, setDragPointerValues] = React.useState({});
37
+ React.useEffect(() => {
38
+ setDragPointerValues((prev) => {
39
+ const next = { ...prev };
40
+ let changed = false;
41
+ for (let i = 0; i < pointers.length; i += 1) {
42
+ const pointer = pointers[i];
43
+ if (!pointer)
44
+ continue;
45
+ const pointerId = pointerIds[i] ?? `sg-linear-pointer-${i}`;
46
+ const clamped = clamp(pointer.value, safeMin, safeMax);
47
+ if (next[pointerId] !== clamped) {
48
+ next[pointerId] = clamped;
49
+ changed = true;
50
+ }
51
+ }
52
+ for (const key of Object.keys(next)) {
53
+ if (!pointerIds.includes(key)) {
54
+ delete next[key];
55
+ changed = true;
56
+ }
57
+ }
58
+ return changed ? next : prev;
59
+ });
60
+ }, [pointerIds, pointers, safeMax, safeMin]);
61
+ const majorCount = Math.max(1, Math.floor(majorTickCount));
62
+ const minorCount = Math.max(0, Math.floor(minorTicksPerInterval));
63
+ const totalWidth = Math.max(120, Math.floor(width));
64
+ const totalHeight = Math.max(90, Math.floor(height));
65
+ const labelSpace = showLabels ? 20 : 8;
66
+ const tickSpace = showTicks ? 14 : 6;
67
+ const axisStart = orientation === "horizontal"
68
+ ? { x: 16, y: totalHeight - labelSpace - tickSpace - 4 }
69
+ : { x: totalWidth - labelSpace - tickSpace - 4, y: totalHeight - 16 };
70
+ const axisEnd = orientation === "horizontal"
71
+ ? { x: totalWidth - 16, y: totalHeight - labelSpace - tickSpace - 4 }
72
+ : { x: totalWidth - labelSpace - tickSpace - 4, y: 16 };
73
+ const valueToRatio = React.useCallback((raw) => {
74
+ const normalized = clamp((raw - safeMin) / span, 0, 1);
75
+ return isAxisInversed ? 1 - normalized : normalized;
76
+ }, [isAxisInversed, safeMin, span]);
77
+ const ratioToValue = React.useCallback((ratioRaw) => {
78
+ const ratio = clamp(ratioRaw, 0, 1);
79
+ const normalized = isAxisInversed ? 1 - ratio : ratio;
80
+ return safeMin + normalized * span;
81
+ }, [isAxisInversed, safeMin, span]);
82
+ const valueToPoint = React.useCallback((raw) => {
83
+ const ratio = valueToRatio(raw);
84
+ const x = axisStart.x + ratio * (axisEnd.x - axisStart.x);
85
+ const y = axisStart.y + ratio * (axisEnd.y - axisStart.y);
86
+ return { x, y };
87
+ }, [axisEnd.x, axisEnd.y, axisStart.x, axisStart.y, valueToRatio]);
88
+ const pointToValue = React.useCallback((x, y) => {
89
+ if (orientation === "horizontal") {
90
+ const ratio = (x - axisStart.x) / (axisEnd.x - axisStart.x || 1);
91
+ return ratioToValue(ratio);
92
+ }
93
+ const ratio = (y - axisStart.y) / (axisEnd.y - axisStart.y || 1);
94
+ return ratioToValue(ratio);
95
+ }, [axisEnd.x, axisEnd.y, axisStart.x, axisStart.y, orientation, ratioToValue]);
96
+ const pointerList = React.useMemo(() => {
97
+ const list = [];
98
+ if (showPrimaryPointer) {
99
+ list.push({
100
+ id: MAIN_POINTER_ID,
101
+ isPrimary: true,
102
+ pointer: {
103
+ id: MAIN_POINTER_ID,
104
+ value: currentValue,
105
+ color: primaryPointerColor,
106
+ shape: primaryPointerShape,
107
+ size: primaryPointerSize,
108
+ draggable: primaryPointerDraggable
109
+ },
110
+ value: currentValue
111
+ });
112
+ }
113
+ for (let i = 0; i < pointers.length; i += 1) {
114
+ const pointer = pointers[i];
115
+ if (!pointer)
116
+ continue;
117
+ const pointerId = pointerIds[i] ?? `sg-linear-pointer-${i}`;
118
+ const dragValue = dragPointerValues[pointerId];
119
+ list.push({
120
+ id: pointerId,
121
+ isPrimary: false,
122
+ pointer,
123
+ value: clamp(dragValue ?? pointer.value, safeMin, safeMax)
124
+ });
125
+ }
126
+ return list;
127
+ }, [
128
+ currentValue,
129
+ dragPointerValues,
130
+ pointerIds,
131
+ pointers,
132
+ primaryPointerColor,
133
+ primaryPointerDraggable,
134
+ primaryPointerShape,
135
+ primaryPointerSize,
136
+ safeMax,
137
+ safeMin,
138
+ showPrimaryPointer
139
+ ]);
140
+ const pointerMap = React.useMemo(() => {
141
+ const map = new Map();
142
+ for (const item of pointerList)
143
+ map.set(item.id, item);
144
+ return map;
145
+ }, [pointerList]);
146
+ const svgRef = React.useRef(null);
147
+ const [draggingPointerId, setDraggingPointerId] = React.useState(null);
148
+ React.useEffect(() => {
149
+ if (!draggingPointerId)
150
+ return;
151
+ const onMove = (event) => {
152
+ const svg = svgRef.current;
153
+ const pointerMeta = pointerMap.get(draggingPointerId);
154
+ if (!svg || !pointerMeta)
155
+ return;
156
+ const rect = svg.getBoundingClientRect();
157
+ if (rect.width <= 0 || rect.height <= 0)
158
+ return;
159
+ const localX = ((event.clientX - rect.left) / rect.width) * totalWidth;
160
+ const localY = ((event.clientY - rect.top) / rect.height) * totalHeight;
161
+ const next = clamp(pointToValue(localX, localY), safeMin, safeMax);
162
+ if (pointerMeta.isPrimary) {
163
+ setMainValue(next);
164
+ }
165
+ else {
166
+ setDragPointerValues((prev) => ({
167
+ ...prev,
168
+ [pointerMeta.id]: next
169
+ }));
170
+ pointerMeta.pointer.onValueChange?.(next);
171
+ onPointerValueChange?.(pointerMeta.id, next, pointerMeta.pointer);
172
+ }
173
+ };
174
+ const onEnd = () => {
175
+ setDraggingPointerId(null);
176
+ };
177
+ window.addEventListener("pointermove", onMove);
178
+ window.addEventListener("pointerup", onEnd);
179
+ window.addEventListener("pointercancel", onEnd);
180
+ return () => {
181
+ window.removeEventListener("pointermove", onMove);
182
+ window.removeEventListener("pointerup", onEnd);
183
+ window.removeEventListener("pointercancel", onEnd);
184
+ };
185
+ }, [
186
+ draggingPointerId,
187
+ onPointerValueChange,
188
+ pointToValue,
189
+ pointerMap,
190
+ safeMax,
191
+ safeMin,
192
+ setMainValue,
193
+ totalHeight,
194
+ totalWidth
195
+ ]);
196
+ const majorValues = React.useMemo(() => {
197
+ const values = [];
198
+ for (let i = 0; i <= majorCount; i += 1) {
199
+ values.push(safeMin + (span * i) / majorCount);
200
+ }
201
+ return values;
202
+ }, [majorCount, safeMin, span]);
203
+ const animatedStyle = animate
204
+ ? { transition: `all ${animationDuration}ms ease-out` }
205
+ : {};
206
+ const primaryBarEnd = valueToPoint(currentValue);
207
+ const primaryBarStart = valueToPoint(safeMin);
208
+ return (_jsx("div", { className: cn("inline-flex select-none", className), role: "meter", "aria-label": ariaLabel, "aria-valuemin": safeMin, "aria-valuemax": safeMax, "aria-valuenow": currentValue, style: style, children: _jsxs("svg", { ref: svgRef, width: totalWidth, height: totalHeight, viewBox: `0 0 ${totalWidth} ${totalHeight}`, className: "overflow-visible", children: [_jsx("line", { x1: axisStart.x, y1: axisStart.y, x2: axisEnd.x, y2: axisEnd.y, stroke: axisColor, strokeLinecap: "round", strokeWidth: axisThickness }), ranges.map((range, index) => {
209
+ const start = clamp(Math.min(range.start, range.end), safeMin, safeMax);
210
+ const end = clamp(Math.max(range.start, range.end), safeMin, safeMax);
211
+ const p1 = valueToPoint(start);
212
+ const p2 = valueToPoint(end);
213
+ return (_jsx("line", { x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y, stroke: range.color ?? "hsl(var(--primary))", strokeLinecap: "round", strokeWidth: range.thickness ?? axisThickness + 2, strokeOpacity: range.opacity ?? 0.55 }, `range-${index}`));
214
+ }), barPointer ? (_jsx("line", { x1: primaryBarStart.x, y1: primaryBarStart.y, x2: primaryBarEnd.x, y2: primaryBarEnd.y, stroke: barColor, strokeLinecap: "round", strokeWidth: barThickness, style: animatedStyle })) : null, showTicks
215
+ ? majorValues.map((majorValue, index) => {
216
+ const point = valueToPoint(majorValue);
217
+ const nextValue = index < majorValues.length - 1
218
+ ? majorValues[index + 1] ?? majorValue
219
+ : majorValue;
220
+ const segment = nextValue - majorValue;
221
+ return (_jsxs("g", { children: [orientation === "horizontal" ? (_jsx("line", { x1: point.x, y1: axisStart.y + axisThickness / 2, x2: point.x, y2: axisStart.y + axisThickness / 2 + 10, stroke: "hsl(var(--muted-foreground))", strokeWidth: 1.4 })) : (_jsx("line", { x1: axisStart.x - axisThickness / 2, y1: point.y, x2: axisStart.x - axisThickness / 2 - 10, y2: point.y, stroke: "hsl(var(--muted-foreground))", strokeWidth: 1.4 })), index < majorValues.length - 1 && minorCount > 0
222
+ ? Array.from({ length: minorCount }).map((_, minorIndex) => {
223
+ const t = (minorIndex + 1) / (minorCount + 1);
224
+ const minorValue = majorValue + segment * t;
225
+ const minorPoint = valueToPoint(minorValue);
226
+ return orientation === "horizontal" ? (_jsx("line", { x1: minorPoint.x, y1: axisStart.y + axisThickness / 2, x2: minorPoint.x, y2: axisStart.y + axisThickness / 2 + 6, stroke: "hsl(var(--muted-foreground))", strokeWidth: 1, strokeOpacity: 0.75 }, `minor-${index}-${minorIndex}`)) : (_jsx("line", { x1: axisStart.x - axisThickness / 2, y1: minorPoint.y, x2: axisStart.x - axisThickness / 2 - 6, y2: minorPoint.y, stroke: "hsl(var(--muted-foreground))", strokeWidth: 1, strokeOpacity: 0.75 }, `minor-${index}-${minorIndex}`));
227
+ })
228
+ : null] }, `major-${index}`));
229
+ })
230
+ : null, showLabels
231
+ ? majorValues.map((majorValue, index) => {
232
+ const point = valueToPoint(majorValue);
233
+ return orientation === "horizontal" ? (_jsx("text", { x: point.x, y: axisStart.y + axisThickness / 2 + 23, textAnchor: "middle", className: "fill-muted-foreground text-[11px]", children: labelFormatter ? labelFormatter(majorValue) : Math.round(majorValue) }, `label-${index}`)) : (_jsx("text", { x: axisStart.x - axisThickness / 2 - 14, y: point.y + 3, textAnchor: "end", className: "fill-muted-foreground text-[11px]", children: labelFormatter ? labelFormatter(majorValue) : Math.round(majorValue) }, `label-${index}`));
234
+ })
235
+ : null, pointerList.map((entry, index) => {
236
+ const pointer = entry.pointer;
237
+ const point = valueToPoint(entry.value);
238
+ const color = pointer.color ??
239
+ (entry.isPrimary ? primaryPointerColor : "hsl(var(--secondary))");
240
+ const size = pointer.size ?? (entry.isPrimary ? primaryPointerSize : 9);
241
+ const shape = pointer.shape ?? "circle";
242
+ const draggable = !!pointer.draggable;
243
+ const pointerStyle = animatedStyle;
244
+ const handlePointerDown = (event) => {
245
+ if (!draggable)
246
+ return;
247
+ event.preventDefault();
248
+ setDraggingPointerId(entry.id);
249
+ };
250
+ const label = pointer.label !== undefined
251
+ ? pointer.label
252
+ : entry.isPrimary
253
+ ? Math.round(entry.value)
254
+ : null;
255
+ return (_jsxs("g", { style: pointerStyle, className: draggable ? "cursor-pointer" : "", onPointerDown: handlePointerDown, children: [shape === "circle" ? (_jsx("circle", { cx: point.x, cy: point.y, r: size / 2, fill: color })) : shape === "diamond" ? (_jsx("polygon", { points: `${point.x},${point.y - size / 2} ${point.x + size / 2},${point.y} ${point.x},${point.y + size / 2} ${point.x - size / 2},${point.y}`, fill: color })) : shape === "rect" ? (_jsx("rect", { x: point.x - size / 2, y: point.y - size / 2, width: size, height: size, rx: 2, fill: color })) : shape === "inverted-triangle" ? (orientation === "horizontal" ? (_jsx("polygon", { points: `${point.x - size / 2},${point.y - size / 2} ${point.x + size / 2},${point.y - size / 2} ${point.x},${point.y + size / 2}`, fill: color })) : (_jsx("polygon", { points: `${point.x - size / 2},${point.y - size / 2} ${point.x - size / 2},${point.y + size / 2} ${point.x + size / 2},${point.y}`, fill: color }))) : orientation === "horizontal" ? (_jsx("polygon", { points: `${point.x - size / 2},${point.y + size / 2} ${point.x + size / 2},${point.y + size / 2} ${point.x},${point.y - size / 2}`, fill: color })) : (_jsx("polygon", { points: `${point.x + size / 2},${point.y - size / 2} ${point.x + size / 2},${point.y + size / 2} ${point.x - size / 2},${point.y}`, fill: color })), label !== null ? (orientation === "horizontal" ? (_jsx("text", { x: point.x, y: point.y - size - 6 - index * 13, textAnchor: "middle", className: "fill-foreground text-[11px]", children: label })) : (_jsx("text", { x: point.x + size + 8, y: point.y + 3, textAnchor: "start", className: "fill-foreground text-[11px]", children: label }))) : null] }, entry.id));
256
+ })] }) }));
257
+ }
258
+ SgLinearGauge.displayName = "SgLinearGauge";
@@ -0,0 +1,73 @@
1
+ import * as React from "react";
2
+ export type SgRadialGaugeRange = {
3
+ start: number;
4
+ end: number;
5
+ color?: string;
6
+ width?: number;
7
+ opacity?: number;
8
+ };
9
+ export type SgRadialGaugePointerType = "needle" | "marker" | "range";
10
+ export type SgRadialGaugeMarkerShape = "circle" | "diamond" | "triangle" | "square";
11
+ export type SgRadialGaugePointer = {
12
+ id?: string;
13
+ type?: SgRadialGaugePointerType;
14
+ value: number;
15
+ color?: string;
16
+ width?: number;
17
+ size?: number;
18
+ shape?: SgRadialGaugeMarkerShape;
19
+ draggable?: boolean;
20
+ onValueChange?: (value: number) => void;
21
+ label?: React.ReactNode;
22
+ };
23
+ export type SgRadialGaugeAnnotation = {
24
+ id?: string;
25
+ value?: number;
26
+ angle?: number;
27
+ radiusFactor?: number;
28
+ content: React.ReactNode;
29
+ };
30
+ export type SgRadialGaugeProps = {
31
+ min?: number;
32
+ max?: number;
33
+ value?: number;
34
+ defaultValue?: number;
35
+ onValueChange?: (value: number) => void;
36
+ pointers?: SgRadialGaugePointer[];
37
+ onPointerValueChange?: (pointerId: string, value: number, pointer: SgRadialGaugePointer) => void;
38
+ ranges?: SgRadialGaugeRange[];
39
+ annotations?: SgRadialGaugeAnnotation[];
40
+ startAngle?: number;
41
+ endAngle?: number;
42
+ isAxisInversed?: boolean;
43
+ showPrimaryPointer?: boolean;
44
+ primaryPointerType?: SgRadialGaugePointerType;
45
+ primaryPointerColor?: string;
46
+ primaryPointerWidth?: number;
47
+ primaryPointerSize?: number;
48
+ primaryPointerShape?: SgRadialGaugeMarkerShape;
49
+ primaryPointerDraggable?: boolean;
50
+ primaryPointerLabel?: React.ReactNode;
51
+ showTicks?: boolean;
52
+ showLabels?: boolean;
53
+ majorTickCount?: number;
54
+ minorTicksPerInterval?: number;
55
+ labelFormatter?: (value: number) => React.ReactNode;
56
+ axisColor?: string;
57
+ ringThickness?: number;
58
+ axisWidth?: number;
59
+ radiusFactor?: number;
60
+ centerContent?: React.ReactNode;
61
+ width?: number;
62
+ height?: number;
63
+ animate?: boolean;
64
+ animationDuration?: number;
65
+ className?: string;
66
+ style?: React.CSSProperties;
67
+ ariaLabel?: string;
68
+ };
69
+ export declare function SgRadialGauge(props: Readonly<SgRadialGaugeProps>): import("react/jsx-runtime").JSX.Element;
70
+ export declare namespace SgRadialGauge {
71
+ var displayName: string;
72
+ }
73
+ //# sourceMappingURL=SgRadialGauge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgRadialGauge.d.ts","sourceRoot":"","sources":["../../../src/gadgets/gauge/SgRadialGauge.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,311 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ function cn(...parts) {
5
+ return parts.filter(Boolean).join(" ");
6
+ }
7
+ function clamp(value, min, max) {
8
+ return Math.max(min, Math.min(max, value));
9
+ }
10
+ function normalizeAngle(angle) {
11
+ const normalized = angle % 360;
12
+ return normalized < 0 ? normalized + 360 : normalized;
13
+ }
14
+ function toSweep(startAngle, endAngle) {
15
+ const start = normalizeAngle(startAngle);
16
+ const end = normalizeAngle(endAngle);
17
+ const sweep = end >= start ? end - start : end + 360 - start;
18
+ return sweep === 0 ? 360 : sweep;
19
+ }
20
+ function polar(cx, cy, radius, angleDeg) {
21
+ const rad = (angleDeg * Math.PI) / 180;
22
+ return {
23
+ x: cx + radius * Math.cos(rad),
24
+ y: cy + radius * Math.sin(rad)
25
+ };
26
+ }
27
+ function arcPath(cx, cy, radius, startAngle, endAngle) {
28
+ let sweep = toSweep(startAngle, endAngle);
29
+ if (sweep >= 360)
30
+ sweep = 359.999;
31
+ const start = polar(cx, cy, radius, startAngle);
32
+ const end = polar(cx, cy, radius, startAngle + sweep);
33
+ const largeArcFlag = sweep > 180 ? 1 : 0;
34
+ return `M ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${end.x} ${end.y}`;
35
+ }
36
+ const MAIN_POINTER_ID = "__sg-radial-main-pointer__";
37
+ export function SgRadialGauge(props) {
38
+ const { min = 0, max = 100, value, defaultValue = min, onValueChange, pointers = [], onPointerValueChange, ranges = [], annotations = [], startAngle = 135, endAngle = 45, isAxisInversed = false, showPrimaryPointer = true, primaryPointerType = "needle", primaryPointerColor = "hsl(var(--primary))", primaryPointerWidth = 3, primaryPointerSize = 12, primaryPointerShape = "circle", primaryPointerDraggable = false, primaryPointerLabel, showTicks = true, showLabels = true, majorTickCount = 5, minorTicksPerInterval = 1, labelFormatter, axisColor = "hsl(var(--border))", ringThickness, axisWidth = 14, radiusFactor = 0.9, centerContent, width = 300, height = 300, animate = true, animationDuration = 350, className, style, ariaLabel = "Radial gauge" } = props;
39
+ const safeMin = Number.isFinite(min) ? min : 0;
40
+ const safeMax = Number.isFinite(max) && max > safeMin ? max : safeMin + 100;
41
+ const span = safeMax - safeMin;
42
+ const [innerValue, setInnerValue] = React.useState(defaultValue);
43
+ const isControlled = value !== undefined;
44
+ const currentValueRaw = isControlled ? value : innerValue;
45
+ const currentValue = clamp(currentValueRaw, safeMin, safeMax);
46
+ const setMainValue = React.useCallback((next) => {
47
+ const clamped = clamp(next, safeMin, safeMax);
48
+ if (!isControlled)
49
+ setInnerValue(clamped);
50
+ onValueChange?.(clamped);
51
+ }, [isControlled, onValueChange, safeMax, safeMin]);
52
+ React.useEffect(() => {
53
+ if (isControlled)
54
+ return;
55
+ setInnerValue((prev) => clamp(prev, safeMin, safeMax));
56
+ }, [isControlled, safeMin, safeMax]);
57
+ const pointerIds = React.useMemo(() => pointers.map((pointer, index) => pointer.id ?? `sg-radial-pointer-${index}`), [pointers]);
58
+ const [dragPointerValues, setDragPointerValues] = React.useState({});
59
+ React.useEffect(() => {
60
+ setDragPointerValues((prev) => {
61
+ const next = { ...prev };
62
+ let changed = false;
63
+ for (let i = 0; i < pointers.length; i += 1) {
64
+ const pointer = pointers[i];
65
+ if (!pointer)
66
+ continue;
67
+ const pointerId = pointerIds[i] ?? `sg-radial-pointer-${i}`;
68
+ const clamped = clamp(pointer.value, safeMin, safeMax);
69
+ if (next[pointerId] !== clamped) {
70
+ next[pointerId] = clamped;
71
+ changed = true;
72
+ }
73
+ }
74
+ for (const key of Object.keys(next)) {
75
+ if (!pointerIds.includes(key)) {
76
+ delete next[key];
77
+ changed = true;
78
+ }
79
+ }
80
+ return changed ? next : prev;
81
+ });
82
+ }, [pointerIds, pointers, safeMax, safeMin]);
83
+ const totalWidth = Math.max(160, Math.floor(width));
84
+ const totalHeight = Math.max(160, Math.floor(height));
85
+ const cx = totalWidth / 2;
86
+ const cy = totalHeight / 2;
87
+ const rawAxisWidth = ringThickness ?? axisWidth;
88
+ const safeAxisWidth = Number.isFinite(rawAxisWidth) ? Math.max(1, rawAxisWidth) : 14;
89
+ const safeRadiusFactorRaw = Number.isFinite(radiusFactor) ? radiusFactor : 0.9;
90
+ const safeRadiusFactor = Math.max(0.1, safeRadiusFactorRaw);
91
+ const availableRadius = Math.max(8, Math.min(totalWidth, totalHeight) / 2 - safeAxisWidth / 2 - 6);
92
+ const baseRadius = clamp(availableRadius * safeRadiusFactor, 8, availableRadius);
93
+ const sweep = React.useMemo(() => toSweep(startAngle, endAngle), [endAngle, startAngle]);
94
+ const valueToRatio = React.useCallback((raw) => {
95
+ const normalized = clamp((raw - safeMin) / span, 0, 1);
96
+ return isAxisInversed ? 1 - normalized : normalized;
97
+ }, [isAxisInversed, safeMin, span]);
98
+ const ratioToValue = React.useCallback((ratioRaw) => {
99
+ const ratio = clamp(ratioRaw, 0, 1);
100
+ const normalized = isAxisInversed ? 1 - ratio : ratio;
101
+ return safeMin + normalized * span;
102
+ }, [isAxisInversed, safeMin, span]);
103
+ const valueToAngle = React.useCallback((raw) => {
104
+ const ratio = valueToRatio(raw);
105
+ return startAngle + sweep * ratio;
106
+ }, [startAngle, sweep, valueToRatio]);
107
+ const angleToValue = React.useCallback((rawAngle) => {
108
+ const start = normalizeAngle(startAngle);
109
+ const target = normalizeAngle(rawAngle);
110
+ const distance = (target - start + 360) % 360;
111
+ const ratio = clamp(distance / sweep, 0, 1);
112
+ return ratioToValue(ratio);
113
+ }, [ratioToValue, startAngle, sweep]);
114
+ const majorCount = Math.max(1, Math.floor(majorTickCount));
115
+ const minorCount = Math.max(0, Math.floor(minorTicksPerInterval));
116
+ const pointerList = React.useMemo(() => {
117
+ const list = [];
118
+ if (showPrimaryPointer) {
119
+ list.push({
120
+ id: MAIN_POINTER_ID,
121
+ isPrimary: true,
122
+ value: currentValue,
123
+ pointer: {
124
+ id: MAIN_POINTER_ID,
125
+ type: primaryPointerType,
126
+ value: currentValue,
127
+ color: primaryPointerColor,
128
+ width: primaryPointerWidth,
129
+ size: primaryPointerSize,
130
+ shape: primaryPointerShape,
131
+ draggable: primaryPointerDraggable,
132
+ label: primaryPointerLabel
133
+ }
134
+ });
135
+ }
136
+ for (let i = 0; i < pointers.length; i += 1) {
137
+ const pointer = pointers[i];
138
+ if (!pointer)
139
+ continue;
140
+ const id = pointerIds[i] ?? `sg-radial-pointer-${i}`;
141
+ const dragValue = dragPointerValues[id];
142
+ list.push({
143
+ id,
144
+ isPrimary: false,
145
+ pointer,
146
+ value: clamp(dragValue ?? pointer.value, safeMin, safeMax)
147
+ });
148
+ }
149
+ return list;
150
+ }, [
151
+ currentValue,
152
+ dragPointerValues,
153
+ pointerIds,
154
+ pointers,
155
+ primaryPointerColor,
156
+ primaryPointerDraggable,
157
+ primaryPointerLabel,
158
+ primaryPointerShape,
159
+ primaryPointerSize,
160
+ primaryPointerType,
161
+ primaryPointerWidth,
162
+ safeMax,
163
+ safeMin,
164
+ showPrimaryPointer
165
+ ]);
166
+ const pointerMap = React.useMemo(() => {
167
+ const map = new Map();
168
+ for (const pointer of pointerList)
169
+ map.set(pointer.id, pointer);
170
+ return map;
171
+ }, [pointerList]);
172
+ const svgRef = React.useRef(null);
173
+ const [draggingPointerId, setDraggingPointerId] = React.useState(null);
174
+ React.useEffect(() => {
175
+ if (!draggingPointerId)
176
+ return;
177
+ const onMove = (event) => {
178
+ const svg = svgRef.current;
179
+ const pointerMeta = pointerMap.get(draggingPointerId);
180
+ if (!svg || !pointerMeta)
181
+ return;
182
+ const rect = svg.getBoundingClientRect();
183
+ if (rect.width <= 0 || rect.height <= 0)
184
+ return;
185
+ const localX = ((event.clientX - rect.left) / rect.width) * totalWidth;
186
+ const localY = ((event.clientY - rect.top) / rect.height) * totalHeight;
187
+ const angle = normalizeAngle((Math.atan2(localY - cy, localX - cx) * 180) / Math.PI);
188
+ const next = clamp(angleToValue(angle), safeMin, safeMax);
189
+ if (pointerMeta.isPrimary) {
190
+ setMainValue(next);
191
+ }
192
+ else {
193
+ setDragPointerValues((prev) => ({
194
+ ...prev,
195
+ [pointerMeta.id]: next
196
+ }));
197
+ pointerMeta.pointer.onValueChange?.(next);
198
+ onPointerValueChange?.(pointerMeta.id, next, pointerMeta.pointer);
199
+ }
200
+ };
201
+ const onEnd = () => {
202
+ setDraggingPointerId(null);
203
+ };
204
+ window.addEventListener("pointermove", onMove);
205
+ window.addEventListener("pointerup", onEnd);
206
+ window.addEventListener("pointercancel", onEnd);
207
+ return () => {
208
+ window.removeEventListener("pointermove", onMove);
209
+ window.removeEventListener("pointerup", onEnd);
210
+ window.removeEventListener("pointercancel", onEnd);
211
+ };
212
+ }, [
213
+ angleToValue,
214
+ cx,
215
+ cy,
216
+ draggingPointerId,
217
+ onPointerValueChange,
218
+ pointerMap,
219
+ safeMax,
220
+ safeMin,
221
+ setMainValue,
222
+ totalHeight,
223
+ totalWidth
224
+ ]);
225
+ const majorValues = React.useMemo(() => {
226
+ const values = [];
227
+ for (let i = 0; i <= majorCount; i += 1) {
228
+ values.push(safeMin + (span * i) / majorCount);
229
+ }
230
+ return values;
231
+ }, [majorCount, safeMin, span]);
232
+ const animatedStyle = animate
233
+ ? { transition: `all ${animationDuration}ms ease-out` }
234
+ : {};
235
+ const centerNode = centerContent ?? (_jsxs("div", { className: "pointer-events-none select-none text-center", children: [_jsx("div", { className: "text-xs uppercase text-muted-foreground", children: "Value" }), _jsx("div", { className: "text-xl font-semibold text-foreground", children: Math.round(currentValue) })] }));
236
+ return (_jsxs("div", { className: cn("relative inline-flex select-none", className), role: "meter", "aria-label": ariaLabel, "aria-valuemin": safeMin, "aria-valuemax": safeMax, "aria-valuenow": currentValue, style: style, children: [_jsxs("svg", { ref: svgRef, width: totalWidth, height: totalHeight, viewBox: `0 0 ${totalWidth} ${totalHeight}`, children: [_jsx("path", { d: arcPath(cx, cy, baseRadius, startAngle, endAngle), fill: "none", stroke: axisColor, strokeWidth: safeAxisWidth, strokeLinecap: "round" }), ranges.map((range, index) => {
237
+ const start = clamp(Math.min(range.start, range.end), safeMin, safeMax);
238
+ const end = clamp(Math.max(range.start, range.end), safeMin, safeMax);
239
+ const startA = valueToAngle(start);
240
+ const endA = valueToAngle(end);
241
+ return (_jsx("path", { d: arcPath(cx, cy, baseRadius, startA, endA), fill: "none", stroke: range.color ?? "hsl(var(--primary))", strokeWidth: range.width ?? safeAxisWidth + 2, strokeOpacity: range.opacity ?? 0.55, strokeLinecap: "round" }, `range-${index}`));
242
+ }), showTicks
243
+ ? majorValues.map((majorValue, index) => {
244
+ const majorAngle = valueToAngle(majorValue);
245
+ const pOuter = polar(cx, cy, baseRadius + safeAxisWidth / 2 + 1, majorAngle);
246
+ const pInner = polar(cx, cy, baseRadius - safeAxisWidth / 2 - 10, majorAngle);
247
+ const nextValue = index < majorValues.length - 1
248
+ ? majorValues[index + 1] ?? majorValue
249
+ : majorValue;
250
+ const segment = nextValue - majorValue;
251
+ return (_jsxs("g", { children: [_jsx("line", { x1: pOuter.x, y1: pOuter.y, x2: pInner.x, y2: pInner.y, stroke: "hsl(var(--muted-foreground))", strokeWidth: 1.4 }), index < majorValues.length - 1 && minorCount > 0
252
+ ? Array.from({ length: minorCount }).map((_, minorIndex) => {
253
+ const t = (minorIndex + 1) / (minorCount + 1);
254
+ const minorValue = majorValue + segment * t;
255
+ const minorAngle = valueToAngle(minorValue);
256
+ const mOuter = polar(cx, cy, baseRadius + safeAxisWidth / 2, minorAngle);
257
+ const mInner = polar(cx, cy, baseRadius - safeAxisWidth / 2 - 6, minorAngle);
258
+ return (_jsx("line", { x1: mOuter.x, y1: mOuter.y, x2: mInner.x, y2: mInner.y, stroke: "hsl(var(--muted-foreground))", strokeWidth: 1, strokeOpacity: 0.8 }, `minor-${index}-${minorIndex}`));
259
+ })
260
+ : null] }, `major-${index}`));
261
+ })
262
+ : null, showLabels
263
+ ? majorValues.map((majorValue, index) => {
264
+ const angle = valueToAngle(majorValue);
265
+ const point = polar(cx, cy, baseRadius - safeAxisWidth / 2 - 22, angle);
266
+ return (_jsx("text", { x: point.x, y: point.y + 3, textAnchor: "middle", className: "fill-muted-foreground text-[11px]", children: labelFormatter ? labelFormatter(majorValue) : Math.round(majorValue) }, `label-${index}`));
267
+ })
268
+ : null, pointerList.map((entry) => {
269
+ const pointer = entry.pointer;
270
+ const pointerType = pointer.type ?? "needle";
271
+ const color = pointer.color ?? (entry.isPrimary ? primaryPointerColor : "hsl(var(--secondary))");
272
+ const angle = valueToAngle(entry.value);
273
+ const size = pointer.size ?? (pointerType === "needle" ? 10 : 12);
274
+ const widthPx = pointer.width ?? (pointerType === "needle" ? 3 : safeAxisWidth);
275
+ const draggable = !!pointer.draggable;
276
+ const label = pointer.label !== undefined
277
+ ? pointer.label
278
+ : entry.isPrimary && pointerType === "needle"
279
+ ? Math.round(entry.value)
280
+ : null;
281
+ const onPointerDown = (event) => {
282
+ if (!draggable)
283
+ return;
284
+ event.preventDefault();
285
+ setDraggingPointerId(entry.id);
286
+ };
287
+ if (pointerType === "range") {
288
+ const endAnglePointer = valueToAngle(entry.value);
289
+ return (_jsx("g", { style: animatedStyle, className: draggable ? "cursor-pointer" : "", onPointerDown: onPointerDown, children: _jsx("path", { d: arcPath(cx, cy, baseRadius, valueToAngle(safeMin), endAnglePointer), fill: "none", stroke: color, strokeWidth: widthPx, strokeLinecap: "round" }) }, entry.id));
290
+ }
291
+ if (pointerType === "marker") {
292
+ const markerPoint = polar(cx, cy, baseRadius - safeAxisWidth / 2, angle);
293
+ return (_jsx("g", { style: animatedStyle, className: draggable ? "cursor-pointer" : "", onPointerDown: onPointerDown, children: pointer.shape === "diamond" ? (_jsx("polygon", { points: `${markerPoint.x},${markerPoint.y - size / 2} ${markerPoint.x + size / 2},${markerPoint.y} ${markerPoint.x},${markerPoint.y + size / 2} ${markerPoint.x - size / 2},${markerPoint.y}`, fill: color })) : pointer.shape === "triangle" ? (_jsx("polygon", { points: `${markerPoint.x - size / 2},${markerPoint.y + size / 2} ${markerPoint.x + size / 2},${markerPoint.y + size / 2} ${markerPoint.x},${markerPoint.y - size / 2}`, fill: color })) : pointer.shape === "square" ? (_jsx("rect", { x: markerPoint.x - size / 2, y: markerPoint.y - size / 2, width: size, height: size, fill: color })) : (_jsx("circle", { cx: markerPoint.x, cy: markerPoint.y, r: size / 2, fill: color })) }, entry.id));
294
+ }
295
+ const tip = polar(cx, cy, baseRadius - safeAxisWidth / 2 - 4, angle);
296
+ const tail = polar(cx, cy, baseRadius * 0.14, angle + 180);
297
+ return (_jsxs("g", { style: animatedStyle, className: draggable ? "cursor-pointer" : "", onPointerDown: onPointerDown, children: [_jsx("line", { x1: tail.x, y1: tail.y, x2: tip.x, y2: tip.y, stroke: color, strokeWidth: widthPx, strokeLinecap: "round" }), _jsx("circle", { cx: cx, cy: cy, r: Math.max(4, size * 0.42), fill: color }), label !== null ? (_jsx("text", { x: tip.x, y: tip.y - 8, textAnchor: "middle", className: "fill-foreground text-[11px]", children: label })) : null] }, entry.id));
298
+ })] }), annotations.map((annotation, index) => {
299
+ const angle = annotation.angle !== undefined
300
+ ? annotation.angle
301
+ : annotation.value !== undefined
302
+ ? valueToAngle(annotation.value)
303
+ : valueToAngle(currentValue);
304
+ const radius = baseRadius * (annotation.radiusFactor ?? 0.62);
305
+ const point = polar(cx, cy, radius, angle);
306
+ const left = `${(point.x / totalWidth) * 100}%`;
307
+ const top = `${(point.y / totalHeight) * 100}%`;
308
+ return (_jsx("div", { className: "pointer-events-none absolute", style: { left, top, transform: "translate(-50%, -50%)" }, children: annotation.content }, annotation.id ?? `annotation-${index}`));
309
+ }), _jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: centerNode })] }));
310
+ }
311
+ SgRadialGauge.displayName = "SgRadialGauge";
@@ -0,0 +1,5 @@
1
+ export { SgLinearGauge } from "./SgLinearGauge";
2
+ export type { SgLinearGaugeProps, SgLinearGaugeRange, SgLinearGaugePointer, SgLinearGaugePointerShape } from "./SgLinearGauge";
3
+ export { SgRadialGauge } from "./SgRadialGauge";
4
+ export type { SgRadialGaugeProps, SgRadialGaugeRange, SgRadialGaugePointer, SgRadialGaugePointerType, SgRadialGaugeMarkerShape, SgRadialGaugeAnnotation } from "./SgRadialGauge";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/gadgets/gauge/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,yBAAyB,EAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,iBAAiB,CAAC"}