@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,354 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { ChevronDown } from "lucide-react";
5
+ import { SgInputText } from "./SgInputText";
6
+ import { t, useComponentsI18n } from "../i18n";
7
+ const TYPEAHEAD_RESET_MS = 700;
8
+ function defaultMapItem(raw) {
9
+ if (typeof raw === "string" || typeof raw === "number") {
10
+ return {
11
+ id: String(raw),
12
+ label: String(raw)
13
+ };
14
+ }
15
+ if (raw && typeof raw === "object") {
16
+ const asRecord = raw;
17
+ const id = asRecord.id ?? asRecord.value ?? asRecord.key ?? asRecord.label;
18
+ const label = asRecord.label ?? asRecord.name ?? asRecord.description ?? asRecord.title ?? id;
19
+ return {
20
+ id: typeof id === "string" || typeof id === "number" ? id : String(id ?? ""),
21
+ label: String(label ?? "")
22
+ };
23
+ }
24
+ return {
25
+ id: String(raw ?? ""),
26
+ label: String(raw ?? "")
27
+ };
28
+ }
29
+ function idEquals(left, right) {
30
+ if (right == null)
31
+ return false;
32
+ return String(left) === String(right);
33
+ }
34
+ function findTypeAheadMatchIndex(entries, query, startIndex) {
35
+ const normalized = query.trim().toLowerCase();
36
+ if (!normalized)
37
+ return -1;
38
+ if (!entries.length)
39
+ return -1;
40
+ for (let offset = 1; offset <= entries.length; offset += 1) {
41
+ const index = (startIndex + offset + entries.length) % entries.length;
42
+ const entry = entries[index];
43
+ if (!entry || entry.item.disabled)
44
+ continue;
45
+ const label = entry.item.label?.toLowerCase() ?? "";
46
+ if (label.startsWith(normalized))
47
+ return index;
48
+ }
49
+ return -1;
50
+ }
51
+ export function SgCombobox(props) {
52
+ const { source, mapItem: mapItemProp, value, onValueChange, grouped, groupped, loadingText: loadingTextProp, emptyText: emptyTextProp, openOnFocus = false, onSelect, renderItem, renderGroupHeader, renderFooter, itemTooltip, inputProps, enabled, borderRadius, ...rest } = props;
53
+ const i18n = useComponentsI18n();
54
+ const loadingText = loadingTextProp ?? t(i18n, "components.autocomplete.loading");
55
+ const emptyText = emptyTextProp ?? t(i18n, "components.autocomplete.empty");
56
+ const effectiveGrouped = grouped ?? groupped ?? false;
57
+ const isControlled = value !== undefined;
58
+ const wrapperRef = React.useRef(null);
59
+ const ignoreBlurRef = React.useRef(false);
60
+ const requestIdRef = React.useRef(0);
61
+ const typeAheadRef = React.useRef({
62
+ buffer: "",
63
+ ts: 0
64
+ });
65
+ const [open, setOpen] = React.useState(false);
66
+ const [loading, setLoading] = React.useState(false);
67
+ const [entries, setEntries] = React.useState([]);
68
+ const [activeIndex, setActiveIndex] = React.useState(-1);
69
+ const [internalValue, setInternalValue] = React.useState(null);
70
+ const [lastSelectedLabel, setLastSelectedLabel] = React.useState("");
71
+ const resolvedBorderRadius = React.useMemo(() => {
72
+ if (borderRadius === undefined)
73
+ return undefined;
74
+ return typeof borderRadius === "number" ? `${borderRadius}px` : borderRadius;
75
+ }, [borderRadius]);
76
+ const resolvedValue = isControlled ? value : internalValue;
77
+ const isDisabled = enabled === false;
78
+ const mapItem = React.useCallback((raw) => {
79
+ if (mapItemProp)
80
+ return mapItemProp(raw);
81
+ return defaultMapItem(raw);
82
+ }, [mapItemProp]);
83
+ const refreshFromSource = React.useCallback(async () => {
84
+ if (Array.isArray(source)) {
85
+ const nextEntries = source.map((raw) => ({
86
+ item: mapItem(raw),
87
+ raw
88
+ }));
89
+ setEntries(nextEntries);
90
+ return nextEntries;
91
+ }
92
+ setLoading(true);
93
+ const currentRequest = ++requestIdRef.current;
94
+ try {
95
+ const result = source("");
96
+ const data = typeof result?.then === "function"
97
+ ? await result
98
+ : result;
99
+ if (currentRequest !== requestIdRef.current)
100
+ return [];
101
+ const nextEntries = data.map((raw) => ({
102
+ item: mapItem(raw),
103
+ raw
104
+ }));
105
+ setEntries(nextEntries);
106
+ return nextEntries;
107
+ }
108
+ catch {
109
+ if (currentRequest !== requestIdRef.current)
110
+ return [];
111
+ setEntries([]);
112
+ return [];
113
+ }
114
+ finally {
115
+ if (currentRequest === requestIdRef.current) {
116
+ setLoading(false);
117
+ }
118
+ }
119
+ }, [mapItem, source]);
120
+ React.useEffect(() => {
121
+ if (!Array.isArray(source))
122
+ return;
123
+ setEntries(source.map((raw) => ({
124
+ item: mapItem(raw),
125
+ raw
126
+ })));
127
+ }, [mapItem, source]);
128
+ const selectedEntry = React.useMemo(() => entries.find((entry) => idEquals(entry.item.id, resolvedValue)), [entries, resolvedValue]);
129
+ React.useEffect(() => {
130
+ if (resolvedValue == null || resolvedValue === "") {
131
+ if (lastSelectedLabel !== "") {
132
+ setLastSelectedLabel("");
133
+ }
134
+ return;
135
+ }
136
+ if (selectedEntry) {
137
+ if (lastSelectedLabel !== selectedEntry.item.label) {
138
+ setLastSelectedLabel(selectedEntry.item.label);
139
+ }
140
+ return;
141
+ }
142
+ if (lastSelectedLabel !== "") {
143
+ setLastSelectedLabel("");
144
+ }
145
+ void refreshFromSource();
146
+ }, [lastSelectedLabel, refreshFromSource, resolvedValue, selectedEntry]);
147
+ const displayedValue = resolvedValue == null || resolvedValue === ""
148
+ ? ""
149
+ : selectedEntry?.item.label ?? lastSelectedLabel;
150
+ const setSelectedValue = React.useCallback((nextValue) => {
151
+ if (!isControlled) {
152
+ setInternalValue(nextValue);
153
+ }
154
+ onValueChange?.(nextValue);
155
+ }, [isControlled, onValueChange]);
156
+ const closeDropdown = React.useCallback(() => {
157
+ setOpen(false);
158
+ setActiveIndex(-1);
159
+ typeAheadRef.current = { buffer: "", ts: 0 };
160
+ }, []);
161
+ const openDropdown = React.useCallback(() => {
162
+ if (isDisabled)
163
+ return;
164
+ setOpen(true);
165
+ void refreshFromSource();
166
+ }, [isDisabled, refreshFromSource]);
167
+ const selectIndex = React.useCallback((index) => {
168
+ const entry = entries[index];
169
+ if (!entry || entry.item.disabled)
170
+ return;
171
+ setSelectedValue(entry.item.id);
172
+ setLastSelectedLabel(entry.item.label);
173
+ onSelect?.(entry.raw);
174
+ closeDropdown();
175
+ }, [closeDropdown, entries, onSelect, setSelectedValue]);
176
+ const handleTypeAhead = React.useCallback(async (typedKey) => {
177
+ const key = typedKey.toLowerCase();
178
+ const now = Date.now();
179
+ const previous = typeAheadRef.current;
180
+ const buffer = now - previous.ts > TYPEAHEAD_RESET_MS
181
+ ? key
182
+ : `${previous.buffer}${key}`;
183
+ typeAheadRef.current = {
184
+ buffer,
185
+ ts: now
186
+ };
187
+ let availableEntries = entries;
188
+ if (!availableEntries.length) {
189
+ availableEntries = await refreshFromSource();
190
+ }
191
+ if (!availableEntries.length)
192
+ return;
193
+ const selectedIndex = availableEntries.findIndex((entry) => idEquals(entry.item.id, resolvedValue));
194
+ const baseIndex = selectedIndex >= 0 ? selectedIndex : activeIndex;
195
+ let nextIndex = findTypeAheadMatchIndex(availableEntries, buffer, baseIndex);
196
+ if (nextIndex < 0 && buffer.length > 1) {
197
+ typeAheadRef.current = { buffer: key, ts: now };
198
+ nextIndex = findTypeAheadMatchIndex(availableEntries, key, baseIndex);
199
+ }
200
+ if (nextIndex < 0)
201
+ return;
202
+ const nextEntry = availableEntries[nextIndex];
203
+ if (!nextEntry || nextEntry.item.disabled)
204
+ return;
205
+ setSelectedValue(nextEntry.item.id);
206
+ setLastSelectedLabel(nextEntry.item.label);
207
+ onSelect?.(nextEntry.raw);
208
+ if (open) {
209
+ setActiveIndex(nextIndex);
210
+ }
211
+ }, [activeIndex, entries, onSelect, open, refreshFromSource, resolvedValue, setSelectedValue]);
212
+ React.useEffect(() => {
213
+ if (!open)
214
+ return;
215
+ const handleOutside = (event) => {
216
+ if (wrapperRef.current?.contains(event.target))
217
+ return;
218
+ closeDropdown();
219
+ };
220
+ document.addEventListener("mousedown", handleOutside);
221
+ return () => document.removeEventListener("mousedown", handleOutside);
222
+ }, [closeDropdown, open]);
223
+ React.useEffect(() => {
224
+ if (!open)
225
+ return;
226
+ if (!entries.length) {
227
+ setActiveIndex(-1);
228
+ return;
229
+ }
230
+ const selectedIndex = entries.findIndex((entry) => idEquals(entry.item.id, resolvedValue));
231
+ if (selectedIndex >= 0) {
232
+ setActiveIndex(selectedIndex);
233
+ return;
234
+ }
235
+ setActiveIndex(0);
236
+ }, [entries, open, resolvedValue]);
237
+ const groupedEntries = React.useMemo(() => {
238
+ if (!effectiveGrouped)
239
+ return null;
240
+ const map = new Map();
241
+ entries.forEach((entry, index) => {
242
+ const groupKey = entry.item.group ?? "";
243
+ if (!map.has(groupKey))
244
+ map.set(groupKey, []);
245
+ map.get(groupKey)?.push({ entry, index });
246
+ });
247
+ return Array.from(map.entries()).map(([group, list]) => ({
248
+ group,
249
+ list
250
+ }));
251
+ }, [effectiveGrouped, entries]);
252
+ const dropdownButton = (_jsx("button", { type: "button", className: "text-foreground/60 hover:text-foreground", onMouseDown: (event) => {
253
+ event.preventDefault();
254
+ ignoreBlurRef.current = true;
255
+ }, onClick: () => {
256
+ if (open) {
257
+ closeDropdown();
258
+ return;
259
+ }
260
+ openDropdown();
261
+ }, "aria-label": "Abrir lista", children: _jsx(ChevronDown, { size: 16 }) }));
262
+ return (_jsxs("div", { className: "relative", ref: wrapperRef, children: [_jsx(SgInputText, { ...rest, enabled: enabled, borderRadius: borderRadius, clearButton: false, readOnly: true, iconButtons: [dropdownButton], inputProps: {
263
+ ...inputProps,
264
+ value: displayedValue,
265
+ onMouseDown: (event) => {
266
+ inputProps?.onMouseDown?.(event);
267
+ if (isDisabled)
268
+ return;
269
+ if (!open) {
270
+ openDropdown();
271
+ }
272
+ },
273
+ onFocus: (event) => {
274
+ inputProps?.onFocus?.(event);
275
+ if (openOnFocus) {
276
+ openDropdown();
277
+ }
278
+ },
279
+ onBlur: (event) => {
280
+ inputProps?.onBlur?.(event);
281
+ if (event.relatedTarget && wrapperRef.current?.contains(event.relatedTarget)) {
282
+ return;
283
+ }
284
+ if (ignoreBlurRef.current) {
285
+ setTimeout(() => {
286
+ ignoreBlurRef.current = false;
287
+ }, 0);
288
+ return;
289
+ }
290
+ closeDropdown();
291
+ },
292
+ onKeyDown: (event) => {
293
+ inputProps?.onKeyDown?.(event);
294
+ if (event.defaultPrevented)
295
+ return;
296
+ if (isDisabled)
297
+ return;
298
+ if (!event.ctrlKey && !event.altKey && !event.metaKey && event.key.length === 1 && event.key !== " ") {
299
+ event.preventDefault();
300
+ void handleTypeAhead(event.key);
301
+ return;
302
+ }
303
+ if (event.key === "Tab") {
304
+ closeDropdown();
305
+ return;
306
+ }
307
+ if (event.key === "Escape") {
308
+ event.preventDefault();
309
+ closeDropdown();
310
+ return;
311
+ }
312
+ if (event.key === "ArrowDown") {
313
+ event.preventDefault();
314
+ if (!open) {
315
+ openDropdown();
316
+ return;
317
+ }
318
+ setActiveIndex((prev) => Math.min(prev + 1, entries.length - 1));
319
+ return;
320
+ }
321
+ if (event.key === "ArrowUp") {
322
+ event.preventDefault();
323
+ if (!open) {
324
+ openDropdown();
325
+ return;
326
+ }
327
+ setActiveIndex((prev) => Math.max(prev - 1, 0));
328
+ return;
329
+ }
330
+ if (event.key === "Enter" || event.key === " ") {
331
+ event.preventDefault();
332
+ if (!open) {
333
+ openDropdown();
334
+ return;
335
+ }
336
+ if (activeIndex >= 0) {
337
+ selectIndex(activeIndex);
338
+ }
339
+ }
340
+ }
341
+ } }), open && !isDisabled ? (_jsxs("div", { className: "absolute left-0 right-0 z-30 mt-1 overflow-hidden rounded-md border border-border bg-white shadow-lg", style: resolvedBorderRadius ? { borderRadius: resolvedBorderRadius } : undefined, children: [_jsx("div", { className: "max-h-64 overflow-auto", children: loading ? (_jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: loadingText })) : entries.length === 0 ? (_jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: emptyText })) : groupedEntries ? (groupedEntries.map(({ group, list }) => (_jsxs("div", { className: "border-b border-border last:border-b-0", children: [_jsx("div", { className: "px-3 py-1 text-xs font-semibold text-muted-foreground", children: renderGroupHeader ? renderGroupHeader(group) : group || " " }), list.map(({ entry, index }) => {
342
+ const isActive = activeIndex === index;
343
+ return (_jsxs("div", { className: `group relative cursor-pointer px-3 py-2 text-sm ${isActive ? "bg-muted/60" : ""} ${entry.item.disabled ? "cursor-not-allowed opacity-50" : "hover:bg-muted/40"}`, onMouseEnter: () => setActiveIndex(index), onMouseDown: (event) => {
344
+ event.preventDefault();
345
+ ignoreBlurRef.current = true;
346
+ }, onClick: () => selectIndex(index), children: [renderItem ? renderItem(entry.item, isActive) : entry.item.label, itemTooltip ? (_jsx("div", { className: "pointer-events-none absolute left-full top-1/2 z-20 ml-2 -translate-y-1/2 rounded border border-border bg-white px-2 py-1 text-xs shadow-md opacity-0 transition-opacity group-hover:opacity-100", children: itemTooltip(entry.item) })) : null] }, entry.item.id));
347
+ })] }, group || "default")))) : (entries.map((entry, index) => {
348
+ const isActive = activeIndex === index;
349
+ return (_jsxs("div", { className: `group relative cursor-pointer px-3 py-2 text-sm ${isActive ? "bg-muted/60" : ""} ${entry.item.disabled ? "cursor-not-allowed opacity-50" : "hover:bg-muted/40"}`, onMouseEnter: () => setActiveIndex(index), onMouseDown: (event) => {
350
+ event.preventDefault();
351
+ ignoreBlurRef.current = true;
352
+ }, onClick: () => selectIndex(index), children: [renderItem ? renderItem(entry.item, isActive) : entry.item.label, itemTooltip ? (_jsx("div", { className: "pointer-events-none absolute left-full top-1/2 z-20 ml-2 -translate-y-1/2 rounded border border-border bg-white px-2 py-1 text-xs shadow-md opacity-0 transition-opacity group-hover:opacity-100", children: itemTooltip(entry.item) })) : null] }, entry.item.id));
353
+ })) }), renderFooter ? (_jsx("div", { className: "border-t border-border bg-white px-3 py-2", children: renderFooter("", entries.length > 0) })) : null] })) : null] }));
354
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"SgInputOTP.d.ts","sourceRoot":"","sources":["../../src/inputs/SgInputOTP.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SgInputOTP.d.ts","sourceRoot":"","sources":["../../src/inputs/SgInputOTP.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAGV,WAAW,EACX,eAAe,EACf,eAAe,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAsB5C,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,WAAW,EAAE,MAAM,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,IAAI,CACf,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAC3C,OAAO,GAAG,cAAc,CACzB,GAAG;QACF,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;KACnC,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACtE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC;IACxC,KAAK,CAAC,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC;CACtC,GAAG,aAAa,CAAC;AAkrBlB,eAAO,MAAM,UAAU;QAntBjB,MAAM;YACF,MAAM;eACH,MAAM;WACV,MAAM;YACL,MAAM;mBACC,MAAM;YACb,MAAM;gBACF,MAAM;qBACD,MAAM;oBACP,MAAM;yBACD,MAAM;iBACd,IAAI,CACf,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAC3C,OAAO,GAAG,cAAc,CACzB,GAAG;QACF,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;KACnC;YACO,MAAM,GAAG,MAAM;cACb,OAAO;eACN,OAAO;eACP,OAAO;sBACA,MAAM;iBACX,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI;qBACpD,OAAO;mBACT,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI;eACpC,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI;kBAC1B,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI;iBAC3B,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;cAC1B,MAAM,IAAI;aACX,MAAM,IAAI;cACT,MAAM,IAAI;eACT,eAAe,CAAC,WAAW,CAAC;YAC/B,eAAe,CAAC,WAAW,CAAC;uDA2uBpC,CAAC"}
@@ -409,7 +409,7 @@ const SgInputOTPBase = React.forwardRef(function SgInputOTPBase(props, ref) {
409
409
  const isDisabled = enabled === false || Boolean(inputDisabled);
410
410
  const isReadOnly = readOnly || Boolean(inputReadOnly);
411
411
  const mergedSlotClass = [
412
- "h-12 w-11 rounded-md border bg-white px-0 text-center text-base font-medium shadow-sm outline-none transition-all",
412
+ "h-12 w-11 border bg-white px-0 text-center text-base font-medium shadow-sm outline-none transition-all",
413
413
  hasError
414
414
  ? "border-[hsl(var(--destructive))] focus:border-[hsl(var(--destructive))] focus:ring-2 focus:ring-[hsl(var(--destructive)/0.25)]"
415
415
  : "border-border focus:border-[hsl(var(--primary))] focus:ring-2 focus:ring-[hsl(var(--primary)/0.25)]",
@@ -448,7 +448,14 @@ const SgInputOTPBase = React.forwardRef(function SgInputOTPBase(props, ref) {
448
448
  const computedPattern = token.slotKind === "digit"
449
449
  ? "[0-9]*"
450
450
  : slotPattern ?? "[A-Za-z0-9]*";
451
- return (_createElement("input", { ...restSlotInputProps, key: `${id}-slot-${slotIndex}`, id: `${id}-slot-${slotIndex}`, type: "text", value: slotValue, maxLength: 1, autoComplete: slotIndex === 0 ? slotAutoComplete ?? "one-time-code" : "off", autoFocus: slotIndex === 0 ? slotAutoFocus : false, inputMode: computedInputMode, pattern: computedPattern, className: mergedSlotClass, disabled: isDisabled, readOnly: isReadOnly, "aria-label": labelText ? `${labelText} ${slotIndex + 1}` : `OTP ${slotIndex + 1}`, ref: (node) => setSlotRef(slotIndex, node), onFocus: (event) => {
451
+ const slotShapeClass = slotCount === 1
452
+ ? "rounded-2xl"
453
+ : slotIndex === 0
454
+ ? "rounded-l-2xl rounded-r-md"
455
+ : slotIndex === slotCount - 1
456
+ ? "rounded-r-2xl rounded-l-md"
457
+ : "rounded-md";
458
+ return (_createElement("input", { ...restSlotInputProps, key: `${id}-slot-${slotIndex}`, id: `${id}-slot-${slotIndex}`, type: "text", value: slotValue, maxLength: 1, autoComplete: slotIndex === 0 ? slotAutoComplete ?? "one-time-code" : "off", autoFocus: slotIndex === 0 ? slotAutoFocus : false, inputMode: computedInputMode, pattern: computedPattern, className: `${mergedSlotClass} ${slotShapeClass}`, disabled: isDisabled, readOnly: isReadOnly, "aria-label": labelText ? `${labelText} ${slotIndex + 1}` : `OTP ${slotIndex + 1}`, ref: (node) => setSlotRef(slotIndex, node), onFocus: (event) => {
452
459
  event.currentTarget.select();
453
460
  onSlotInputFocus?.(event);
454
461
  }, onBlur: (event) => {
@@ -0,0 +1,37 @@
1
+ import React from "react";
2
+ import { UseFormRegister, FieldValues } from "react-hook-form";
3
+ import { type SgGroupBoxProps } from "../layout/SgGroupBox";
4
+ export type SgRadioGroupOrientation = "horizontal" | "vertical";
5
+ export type SgRadioGroupSelectionStyle = "radio" | "highlight";
6
+ export interface SgRadioGroupOption {
7
+ label: string;
8
+ value: string | number;
9
+ icon?: React.ReactNode;
10
+ disabled?: boolean;
11
+ }
12
+ export interface SgRadioGroupProps {
13
+ id?: string;
14
+ title?: string;
15
+ source: SgRadioGroupOption[];
16
+ value?: string | number;
17
+ orientation?: SgRadioGroupOrientation;
18
+ selectionStyle?: SgRadioGroupSelectionStyle;
19
+ iconOnly?: boolean;
20
+ showCancel?: boolean;
21
+ cancelLabel?: string;
22
+ disabled?: boolean;
23
+ readOnly?: boolean;
24
+ required?: boolean;
25
+ onChange?: (value: string | number | null) => void;
26
+ name?: string;
27
+ control?: any;
28
+ register?: UseFormRegister<FieldValues>;
29
+ error?: string;
30
+ className?: string;
31
+ optionClassName?: string;
32
+ groupBoxProps?: Omit<Partial<SgGroupBoxProps>, "children" | "title"> & {
33
+ title?: string;
34
+ };
35
+ }
36
+ export declare function SgRadioGroup(props: SgRadioGroupProps): import("react/jsx-runtime").JSX.Element;
37
+ //# sourceMappingURL=SgRadioGroup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgRadioGroup.d.ts","sourceRoot":"","sources":["../../src/inputs/SgRadioGroup.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,WAAW,EAAc,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGxE,MAAM,MAAM,uBAAuB,GAAG,YAAY,GAAG,UAAU,CAAC;AAChE,MAAM,MAAM,0BAA0B,GAAG,OAAO,GAAG,WAAW,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,cAAc,CAAC,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAGnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,QAAQ,CAAC,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3F;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,2CAsUpD"}
@@ -0,0 +1,139 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { Controller } from "react-hook-form";
5
+ import { SgGroupBox } from "../layout/SgGroupBox";
6
+ import { t, useComponentsI18n } from "../i18n";
7
+ export function SgRadioGroup(props) {
8
+ const i18n = useComponentsI18n();
9
+ const { id, title, source, value: controlledValue, orientation = "vertical", selectionStyle = "radio", iconOnly = false, showCancel = false, cancelLabel, disabled = false, readOnly = false, required = false, onChange, name, control, register, error, className = "", optionClassName = "", groupBoxProps = {} } = props;
10
+ const resolvedGroupBoxTitle = (() => {
11
+ const baseTitle = groupBoxProps.title ?? title ?? "";
12
+ if (!required || !baseTitle.trim())
13
+ return baseTitle;
14
+ return baseTitle.includes("*") ? baseTitle : `${baseTitle} *`;
15
+ })();
16
+ const [internalValue, setInternalValue] = React.useState(controlledValue ?? null);
17
+ const isControlled = controlledValue !== undefined;
18
+ const currentValue = isControlled ? controlledValue : internalValue;
19
+ const isHighlightSelection = selectionStyle === "highlight";
20
+ const handleChange = (newValue) => {
21
+ if (disabled || readOnly)
22
+ return;
23
+ if (!isControlled) {
24
+ setInternalValue(newValue);
25
+ }
26
+ onChange?.(newValue);
27
+ };
28
+ const renderRadioOption = (option, index, registration) => {
29
+ const isSelected = currentValue === option.value;
30
+ const isDisabled = disabled || option.disabled;
31
+ const inputClassName = isHighlightSelection
32
+ ? "sr-only"
33
+ : "w-4 h-4 text-[rgb(var(--sg-primary-500))] border-[rgb(var(--sg-border))] focus:ring-[rgb(var(--sg-ring))] focus:ring-2 cursor-pointer disabled:cursor-not-allowed";
34
+ if (isHighlightSelection) {
35
+ return (_jsxs("label", { className: `
36
+ flex items-center gap-2 border px-3 py-2 transition-all duration-150 select-none
37
+ ${orientation === "vertical" ? "w-full -mt-px rounded-none first:mt-0 first:rounded-t-md last:rounded-b-md" : "inline-flex rounded-md"}
38
+ ${iconOnly ? "justify-center" : ""}
39
+ ${isSelected
40
+ ? "relative z-10 translate-y-px rounded-md border-[rgb(var(--sg-primary-300))] bg-[rgb(var(--sg-primary-100))] ring-1 ring-[rgb(var(--sg-primary-200))] shadow-[inset_0_2px_4px_rgba(15,23,42,0.18),inset_0_1px_0_rgba(255,255,255,0.75),0_1px_2px_rgba(15,23,42,0.08)]"
41
+ : "border-[rgb(var(--sg-primary-200))] bg-[rgb(var(--sg-primary-50))] shadow-[0_2px_0_rgba(148,163,184,0.32),0_6px_10px_rgba(15,23,42,0.07)]"}
42
+ ${isDisabled
43
+ ? "cursor-not-allowed opacity-50"
44
+ : readOnly
45
+ ? "cursor-default"
46
+ : `cursor-pointer ${isSelected ? "hover:bg-[rgb(var(--sg-primary-200))]" : "hover:-translate-y-px hover:bg-[rgb(var(--sg-primary-100))] hover:shadow-[0_3px_0_rgba(148,163,184,0.32),0_10px_14px_rgba(15,23,42,0.1)]"}`}
47
+ focus-within:outline-none
48
+ focus-within:ring-2
49
+ focus-within:ring-[rgb(var(--sg-ring))]
50
+ ${optionClassName}
51
+ `, children: [_jsx("input", { type: "radio", ...(registration ? registration : { name: name || id }), value: option.value, checked: isSelected, disabled: isDisabled || readOnly, onChange: (event) => {
52
+ if (registration)
53
+ registration.onChange(event);
54
+ handleChange(option.value);
55
+ }, className: inputClassName }), option.icon ? (_jsx("span", { className: `
56
+ flex items-center justify-center transition-transform duration-150
57
+ ${isSelected ? "scale-110" : "scale-100"}
58
+ `, children: option.icon })) : null, !iconOnly ? (_jsx("span", { className: `
59
+ text-[rgb(var(--sg-text))] transition-[font-size] duration-150
60
+ ${isSelected ? "text-[15px] font-medium" : "text-sm"}
61
+ `, children: option.label })) : null] }, `${option.value}-${index}`));
62
+ }
63
+ return (_jsxs("label", { className: `
64
+ inline-flex items-center gap-2 cursor-pointer select-none
65
+ ${isDisabled ? "opacity-50 cursor-not-allowed" : ""}
66
+ ${readOnly ? "cursor-default" : ""}
67
+ ${optionClassName}
68
+ `, children: [_jsx("input", { type: "radio", ...(registration ? registration : { name: name || id }), value: option.value, checked: isSelected, disabled: isDisabled || readOnly, onChange: (event) => {
69
+ if (registration)
70
+ registration.onChange(event);
71
+ handleChange(option.value);
72
+ }, className: inputClassName }), option.icon && (_jsx("span", { className: "flex items-center justify-center", children: option.icon })), !iconOnly && (_jsx("span", { className: "text-sm text-[rgb(var(--sg-text))]", children: option.label }))] }, `${option.value}-${index}`));
73
+ };
74
+ const renderCancelOption = (registration) => {
75
+ if (!showCancel)
76
+ return null;
77
+ const isSelected = currentValue === null;
78
+ const inputClassName = isHighlightSelection
79
+ ? "sr-only"
80
+ : "w-4 h-4 text-[rgb(var(--sg-primary-500))] border-[rgb(var(--sg-border))] focus:ring-[rgb(var(--sg-ring))] focus:ring-2 cursor-pointer disabled:cursor-not-allowed";
81
+ if (isHighlightSelection) {
82
+ return (_jsxs("label", { className: `
83
+ flex items-center gap-2 border px-3 py-2 transition-all duration-150 select-none
84
+ ${orientation === "vertical" ? "w-full -mt-px rounded-none first:mt-0 first:rounded-t-md last:rounded-b-md" : "inline-flex rounded-md"}
85
+ ${iconOnly ? "justify-center" : ""}
86
+ ${isSelected
87
+ ? "relative z-10 translate-y-px rounded-md border-[rgb(var(--sg-primary-300))] bg-[rgb(var(--sg-primary-100))] ring-1 ring-[rgb(var(--sg-primary-200))] shadow-[inset_0_2px_4px_rgba(15,23,42,0.18),inset_0_1px_0_rgba(255,255,255,0.75),0_1px_2px_rgba(15,23,42,0.08)]"
88
+ : "border-[rgb(var(--sg-primary-200))] bg-[rgb(var(--sg-primary-50))] shadow-[0_2px_0_rgba(148,163,184,0.32),0_6px_10px_rgba(15,23,42,0.07)]"}
89
+ ${disabled
90
+ ? "cursor-not-allowed opacity-50"
91
+ : readOnly
92
+ ? "cursor-default"
93
+ : `cursor-pointer ${isSelected ? "hover:bg-[rgb(var(--sg-primary-200))]" : "hover:-translate-y-px hover:bg-[rgb(var(--sg-primary-100))] hover:shadow-[0_3px_0_rgba(148,163,184,0.32),0_10px_14px_rgba(15,23,42,0.1)]"}`}
94
+ focus-within:outline-none
95
+ focus-within:ring-2
96
+ focus-within:ring-[rgb(var(--sg-ring))]
97
+ ${optionClassName}
98
+ `, children: [_jsx("input", { type: "radio", ...(registration ? registration : { name: name || id }), value: "", checked: isSelected, disabled: disabled || readOnly, onChange: (event) => {
99
+ if (registration)
100
+ registration.onChange(event);
101
+ handleChange(null);
102
+ }, className: inputClassName }), !iconOnly ? (_jsx("span", { className: `
103
+ text-[rgb(var(--sg-text))] transition-[font-size] duration-150
104
+ ${isSelected ? "text-[15px] font-medium" : "text-sm"}
105
+ `, children: cancelLabel || t(i18n, "components.radiogroup.cancel") })) : null] }));
106
+ }
107
+ return (_jsxs("label", { className: `
108
+ inline-flex items-center gap-2 cursor-pointer select-none
109
+ ${disabled ? "opacity-50 cursor-not-allowed" : ""}
110
+ ${readOnly ? "cursor-default" : ""}
111
+ ${optionClassName}
112
+ `, children: [_jsx("input", { type: "radio", ...(registration ? registration : { name: name || id }), value: "", checked: isSelected, disabled: disabled || readOnly, onChange: (event) => {
113
+ if (registration)
114
+ registration.onChange(event);
115
+ handleChange(null);
116
+ }, className: inputClassName }), !iconOnly && (_jsx("span", { className: "text-sm text-[rgb(var(--sg-text))]", children: cancelLabel || t(i18n, "components.radiogroup.cancel") }))] }));
117
+ };
118
+ const content = (_jsxs("div", { className: className, children: [_jsx(SgGroupBox, { ...groupBoxProps, title: resolvedGroupBoxTitle || " ", className: groupBoxProps.className || "", children: _jsxs("div", { className: `
119
+ flex ${isHighlightSelection ? "gap-0" : "gap-4"}
120
+ ${orientation === "vertical" ? "flex-col" : "flex-row flex-wrap"}
121
+ `, role: "radiogroup", "aria-disabled": disabled || undefined, "aria-readonly": readOnly || undefined, "aria-required": required || undefined, children: [source.map((option, index) => renderRadioOption(option, index)), renderCancelOption()] }) }), error && (_jsx("div", { className: "mt-1 text-sm text-red-500", children: error }))] }));
122
+ // React Hook Form integration
123
+ if (control && name) {
124
+ return (_jsx(Controller, { name: name, control: control, rules: { required: required ? t(i18n, "components.inputs.required") : false }, render: ({ field, fieldState }) => (_jsx(SgRadioGroup, { ...props, value: field.value, onChange: (val) => {
125
+ field.onChange(val);
126
+ onChange?.(val);
127
+ }, error: fieldState.error?.message, control: undefined })) }));
128
+ }
129
+ if (register && name) {
130
+ const registration = register(name, {
131
+ required: required ? t(i18n, "components.inputs.required") : false
132
+ });
133
+ return (_jsxs("div", { className: className, children: [_jsx(SgGroupBox, { ...groupBoxProps, title: resolvedGroupBoxTitle || " ", children: _jsxs("div", { className: `
134
+ flex ${isHighlightSelection ? "gap-0" : "gap-4"}
135
+ ${orientation === "vertical" ? "flex-col" : "flex-row flex-wrap"}
136
+ `, role: "radiogroup", "aria-disabled": disabled || undefined, "aria-readonly": readOnly || undefined, "aria-required": required || undefined, children: [source.map((option, index) => renderRadioOption(option, index, registration)), renderCancelOption(registration)] }) }), error && (_jsx("div", { className: "mt-1 text-sm text-red-500", children: error }))] }));
137
+ }
138
+ return content;
139
+ }
@@ -0,0 +1,55 @@
1
+ import React from "react";
2
+ import type { FieldValues, UseFormRegister } from "react-hook-form";
3
+ export type SgRatingSize = "sm" | "md" | "lg" | "xl";
4
+ export interface SgRatingProps {
5
+ /** Unique identifier */
6
+ id?: string;
7
+ /** Label text */
8
+ label?: string;
9
+ /** Current rating value */
10
+ value?: number;
11
+ /** Number of stars */
12
+ stars?: number;
13
+ /** Enable half stars */
14
+ allowHalf?: boolean;
15
+ /** Show cancel button */
16
+ cancel?: boolean;
17
+ /** Disabled state */
18
+ disabled?: boolean;
19
+ /** Read only mode */
20
+ readOnly?: boolean;
21
+ /** Size of the stars */
22
+ size?: SgRatingSize;
23
+ /** Custom class name */
24
+ className?: string;
25
+ /** Custom icon for filled state */
26
+ onIcon?: React.ReactNode;
27
+ /** Custom icon for empty state */
28
+ offIcon?: React.ReactNode;
29
+ /** Custom icon for cancel button */
30
+ cancelIcon?: React.ReactNode;
31
+ /** Color for filled stars */
32
+ color?: string;
33
+ /** Color for empty stars */
34
+ emptyColor?: string;
35
+ /** Show tooltips with value */
36
+ showTooltip?: boolean;
37
+ /** Callback when value changes */
38
+ onChange?: (value: number) => void;
39
+ /** Callback when hovering over stars */
40
+ onHover?: (value: number | null) => void;
41
+ /** React Hook Form integration */
42
+ register?: UseFormRegister<FieldValues>;
43
+ /** React Hook Form field name */
44
+ name?: string;
45
+ /** React Hook Form control */
46
+ control?: any;
47
+ /** Error message */
48
+ error?: string;
49
+ /** Required field */
50
+ required?: boolean;
51
+ /** Required message */
52
+ requiredMessage?: string;
53
+ }
54
+ export declare function SgRating(props: SgRatingProps): import("react/jsx-runtime").JSX.Element;
55
+ //# sourceMappingURL=SgRating.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgRating.d.ts","sourceRoot":"","sources":["../../src/inputs/SgRating.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAGV,WAAW,EACX,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAIzB,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,wBAAwB;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yBAAyB;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wBAAwB;IACxB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,wBAAwB;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,kCAAkC;IAClC,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,oCAAoC;IACpC,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,wCAAwC;IACxC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,kCAAkC;IAClC,QAAQ,CAAC,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC;IACxC,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uBAAuB;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA8QD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,2CAkC5C"}