@seedgrid/fe-components 0.2.4

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 (270) hide show
  1. package/dist/blocked-email-domains.json +41 -0
  2. package/dist/buttons/SgButton.d.ts +43 -0
  3. package/dist/buttons/SgButton.d.ts.map +1 -0
  4. package/dist/buttons/SgButton.js +123 -0
  5. package/dist/buttons/SgFloatActionButton.d.ts +60 -0
  6. package/dist/buttons/SgFloatActionButton.d.ts.map +1 -0
  7. package/dist/buttons/SgFloatActionButton.js +532 -0
  8. package/dist/buttons/SgSpeedDial.d.ts +40 -0
  9. package/dist/buttons/SgSpeedDial.d.ts.map +1 -0
  10. package/dist/buttons/SgSpeedDial.js +149 -0
  11. package/dist/buttons/SgSplitButton.d.ts +32 -0
  12. package/dist/buttons/SgSplitButton.d.ts.map +1 -0
  13. package/dist/buttons/SgSplitButton.js +81 -0
  14. package/dist/clock/SgClock.d.ts +28 -0
  15. package/dist/clock/SgClock.d.ts.map +1 -0
  16. package/dist/clock/SgClock.js +280 -0
  17. package/dist/clock/SgTimeProvider.d.ts +13 -0
  18. package/dist/clock/SgTimeProvider.d.ts.map +1 -0
  19. package/dist/clock/SgTimeProvider.js +44 -0
  20. package/dist/clock/themes/SgClockThemePicker.d.ts +14 -0
  21. package/dist/clock/themes/SgClockThemePicker.d.ts.map +1 -0
  22. package/dist/clock/themes/SgClockThemePicker.js +71 -0
  23. package/dist/clock/themes/SgClockThemePreview.d.ts +7 -0
  24. package/dist/clock/themes/SgClockThemePreview.d.ts.map +1 -0
  25. package/dist/clock/themes/SgClockThemePreview.js +11 -0
  26. package/dist/clock/themes/builtins.d.ts +3 -0
  27. package/dist/clock/themes/builtins.d.ts.map +1 -0
  28. package/dist/clock/themes/builtins.js +241 -0
  29. package/dist/clock/themes/index.d.ts +9 -0
  30. package/dist/clock/themes/index.d.ts.map +1 -0
  31. package/dist/clock/themes/index.js +7 -0
  32. package/dist/clock/themes/provider.d.ts +19 -0
  33. package/dist/clock/themes/provider.d.ts.map +1 -0
  34. package/dist/clock/themes/provider.js +54 -0
  35. package/dist/clock/themes/registry.d.ts +9 -0
  36. package/dist/clock/themes/registry.d.ts.map +1 -0
  37. package/dist/clock/themes/registry.js +25 -0
  38. package/dist/clock/themes/renderTheme.d.ts +7 -0
  39. package/dist/clock/themes/renderTheme.d.ts.map +1 -0
  40. package/dist/clock/themes/renderTheme.js +41 -0
  41. package/dist/clock/themes/types.d.ts +21 -0
  42. package/dist/clock/themes/types.d.ts.map +1 -0
  43. package/dist/clock/themes/types.js +1 -0
  44. package/dist/clock/themes/urlThemeCache.d.ts +2 -0
  45. package/dist/clock/themes/urlThemeCache.d.ts.map +1 -0
  46. package/dist/clock/themes/urlThemeCache.js +11 -0
  47. package/dist/clock/themes/useDarkFlag.d.ts +2 -0
  48. package/dist/clock/themes/useDarkFlag.d.ts.map +1 -0
  49. package/dist/clock/themes/useDarkFlag.js +14 -0
  50. package/dist/commons/SgBadge.d.ts +51 -0
  51. package/dist/commons/SgBadge.d.ts.map +1 -0
  52. package/dist/commons/SgBadge.js +141 -0
  53. package/dist/commons/SgBadgeOverlay.d.ts +13 -0
  54. package/dist/commons/SgBadgeOverlay.d.ts.map +1 -0
  55. package/dist/commons/SgBadgeOverlay.js +20 -0
  56. package/dist/commons/SgButton.d.ts +39 -0
  57. package/dist/commons/SgButton.d.ts.map +1 -0
  58. package/dist/commons/SgButton.js +116 -0
  59. package/dist/commons/SgPopup.d.ts +42 -0
  60. package/dist/commons/SgPopup.d.ts.map +1 -0
  61. package/dist/commons/SgPopup.js +218 -0
  62. package/dist/commons/SgToast.d.ts +44 -0
  63. package/dist/commons/SgToast.d.ts.map +1 -0
  64. package/dist/commons/SgToast.js +97 -0
  65. package/dist/commons/SgToaster.d.ts +11 -0
  66. package/dist/commons/SgToaster.d.ts.map +1 -0
  67. package/dist/commons/SgToaster.js +85 -0
  68. package/dist/commons/common-passwords.d.ts +2 -0
  69. package/dist/commons/common-passwords.d.ts.map +1 -0
  70. package/dist/commons/common-passwords.js +167 -0
  71. package/dist/environment/SgEnvironmentProvider.d.ts +31 -0
  72. package/dist/environment/SgEnvironmentProvider.d.ts.map +1 -0
  73. package/dist/environment/SgEnvironmentProvider.js +120 -0
  74. package/dist/environment/persistence.d.ts +44 -0
  75. package/dist/environment/persistence.d.ts.map +1 -0
  76. package/dist/environment/persistence.js +149 -0
  77. package/dist/gadgets/clock/SgClock.d.ts +18 -0
  78. package/dist/gadgets/clock/SgClock.d.ts.map +1 -0
  79. package/dist/gadgets/clock/SgClock.js +407 -0
  80. package/dist/gadgets/clock/SgTimeProvider.d.ts +13 -0
  81. package/dist/gadgets/clock/SgTimeProvider.d.ts.map +1 -0
  82. package/dist/gadgets/clock/SgTimeProvider.js +44 -0
  83. package/dist/gadgets/clock/themes/SgClockThemePicker.d.ts +14 -0
  84. package/dist/gadgets/clock/themes/SgClockThemePicker.d.ts.map +1 -0
  85. package/dist/gadgets/clock/themes/SgClockThemePicker.js +71 -0
  86. package/dist/gadgets/clock/themes/SgClockThemePreview.d.ts +7 -0
  87. package/dist/gadgets/clock/themes/SgClockThemePreview.d.ts.map +1 -0
  88. package/dist/gadgets/clock/themes/SgClockThemePreview.js +11 -0
  89. package/dist/gadgets/clock/themes/builtins.d.ts +3 -0
  90. package/dist/gadgets/clock/themes/builtins.d.ts.map +1 -0
  91. package/dist/gadgets/clock/themes/builtins.js +241 -0
  92. package/dist/gadgets/clock/themes/index.d.ts +9 -0
  93. package/dist/gadgets/clock/themes/index.d.ts.map +1 -0
  94. package/dist/gadgets/clock/themes/index.js +7 -0
  95. package/dist/gadgets/clock/themes/provider.d.ts +19 -0
  96. package/dist/gadgets/clock/themes/provider.d.ts.map +1 -0
  97. package/dist/gadgets/clock/themes/provider.js +54 -0
  98. package/dist/gadgets/clock/themes/registry.d.ts +9 -0
  99. package/dist/gadgets/clock/themes/registry.d.ts.map +1 -0
  100. package/dist/gadgets/clock/themes/registry.js +25 -0
  101. package/dist/gadgets/clock/themes/renderTheme.d.ts +7 -0
  102. package/dist/gadgets/clock/themes/renderTheme.d.ts.map +1 -0
  103. package/dist/gadgets/clock/themes/renderTheme.js +41 -0
  104. package/dist/gadgets/clock/themes/types.d.ts +21 -0
  105. package/dist/gadgets/clock/themes/types.d.ts.map +1 -0
  106. package/dist/gadgets/clock/themes/types.js +1 -0
  107. package/dist/gadgets/clock/themes/urlThemeCache.d.ts +2 -0
  108. package/dist/gadgets/clock/themes/urlThemeCache.d.ts.map +1 -0
  109. package/dist/gadgets/clock/themes/urlThemeCache.js +11 -0
  110. package/dist/gadgets/clock/themes/useDarkFlag.d.ts +2 -0
  111. package/dist/gadgets/clock/themes/useDarkFlag.d.ts.map +1 -0
  112. package/dist/gadgets/clock/themes/useDarkFlag.js +14 -0
  113. package/dist/gadgets/flip-digit/SgFlipDigit.d.ts +23 -0
  114. package/dist/gadgets/flip-digit/SgFlipDigit.d.ts.map +1 -0
  115. package/dist/gadgets/flip-digit/SgFlipDigit.js +118 -0
  116. package/dist/gadgets/flip-digit/index.d.ts +3 -0
  117. package/dist/gadgets/flip-digit/index.d.ts.map +1 -0
  118. package/dist/gadgets/flip-digit/index.js +1 -0
  119. package/dist/i18n/en-US.json +76 -0
  120. package/dist/i18n/es.json +76 -0
  121. package/dist/i18n/index.d.ts +328 -0
  122. package/dist/i18n/index.d.ts.map +1 -0
  123. package/dist/i18n/index.js +87 -0
  124. package/dist/i18n/pt-BR.json +76 -0
  125. package/dist/i18n/pt-PT.json +76 -0
  126. package/dist/index.d.ts +88 -0
  127. package/dist/index.d.ts.map +1 -0
  128. package/dist/index.js +46 -0
  129. package/dist/inputs/FloatingInput.d.ts +13 -0
  130. package/dist/inputs/FloatingInput.d.ts.map +1 -0
  131. package/dist/inputs/FloatingInput.js +53 -0
  132. package/dist/inputs/FloatingSelect.d.ts +15 -0
  133. package/dist/inputs/FloatingSelect.d.ts.map +1 -0
  134. package/dist/inputs/FloatingSelect.js +52 -0
  135. package/dist/inputs/FloatingTextArea.d.ts +11 -0
  136. package/dist/inputs/FloatingTextArea.d.ts.map +1 -0
  137. package/dist/inputs/FloatingTextArea.js +34 -0
  138. package/dist/inputs/InputBirthDate.d.ts +13 -0
  139. package/dist/inputs/InputBirthDate.d.ts.map +1 -0
  140. package/dist/inputs/InputBirthDate.js +46 -0
  141. package/dist/inputs/InputDate.d.ts +8 -0
  142. package/dist/inputs/InputDate.d.ts.map +1 -0
  143. package/dist/inputs/InputDate.js +23 -0
  144. package/dist/inputs/InputEmail.d.ts +14 -0
  145. package/dist/inputs/InputEmail.d.ts.map +1 -0
  146. package/dist/inputs/InputEmail.js +43 -0
  147. package/dist/inputs/InputPassword.d.ts +12 -0
  148. package/dist/inputs/InputPassword.d.ts.map +1 -0
  149. package/dist/inputs/InputPassword.js +42 -0
  150. package/dist/inputs/MaskedInputs.d.ts +27 -0
  151. package/dist/inputs/MaskedInputs.d.ts.map +1 -0
  152. package/dist/inputs/MaskedInputs.js +161 -0
  153. package/dist/inputs/SgAutocomplete.d.ts +42 -0
  154. package/dist/inputs/SgAutocomplete.d.ts.map +1 -0
  155. package/dist/inputs/SgAutocomplete.js +241 -0
  156. package/dist/inputs/SgCurrencyEdit.d.ts +56 -0
  157. package/dist/inputs/SgCurrencyEdit.d.ts.map +1 -0
  158. package/dist/inputs/SgCurrencyEdit.js +496 -0
  159. package/dist/inputs/SgInputBirthDate.d.ts +13 -0
  160. package/dist/inputs/SgInputBirthDate.d.ts.map +1 -0
  161. package/dist/inputs/SgInputBirthDate.js +48 -0
  162. package/dist/inputs/SgInputCEP.d.ts +33 -0
  163. package/dist/inputs/SgInputCEP.d.ts.map +1 -0
  164. package/dist/inputs/SgInputCEP.js +117 -0
  165. package/dist/inputs/SgInputCNPJ.d.ts +20 -0
  166. package/dist/inputs/SgInputCNPJ.d.ts.map +1 -0
  167. package/dist/inputs/SgInputCNPJ.js +133 -0
  168. package/dist/inputs/SgInputCPF.d.ts +15 -0
  169. package/dist/inputs/SgInputCPF.d.ts.map +1 -0
  170. package/dist/inputs/SgInputCPF.js +70 -0
  171. package/dist/inputs/SgInputCPFCNPJ.d.ts +15 -0
  172. package/dist/inputs/SgInputCPFCNPJ.d.ts.map +1 -0
  173. package/dist/inputs/SgInputCPFCNPJ.js +92 -0
  174. package/dist/inputs/SgInputDate.d.ts +8 -0
  175. package/dist/inputs/SgInputDate.d.ts.map +1 -0
  176. package/dist/inputs/SgInputDate.js +120 -0
  177. package/dist/inputs/SgInputEmail.d.ts +16 -0
  178. package/dist/inputs/SgInputEmail.d.ts.map +1 -0
  179. package/dist/inputs/SgInputEmail.js +74 -0
  180. package/dist/inputs/SgInputFone.d.ts +15 -0
  181. package/dist/inputs/SgInputFone.d.ts.map +1 -0
  182. package/dist/inputs/SgInputFone.js +60 -0
  183. package/dist/inputs/SgInputMasked.d.ts +27 -0
  184. package/dist/inputs/SgInputMasked.d.ts.map +1 -0
  185. package/dist/inputs/SgInputMasked.js +161 -0
  186. package/dist/inputs/SgInputNumber.d.ts +49 -0
  187. package/dist/inputs/SgInputNumber.d.ts.map +1 -0
  188. package/dist/inputs/SgInputNumber.js +438 -0
  189. package/dist/inputs/SgInputPassword.d.ts +26 -0
  190. package/dist/inputs/SgInputPassword.d.ts.map +1 -0
  191. package/dist/inputs/SgInputPassword.js +278 -0
  192. package/dist/inputs/SgInputPhone.d.ts +15 -0
  193. package/dist/inputs/SgInputPhone.d.ts.map +1 -0
  194. package/dist/inputs/SgInputPhone.js +66 -0
  195. package/dist/inputs/SgInputPostalCode.d.ts +37 -0
  196. package/dist/inputs/SgInputPostalCode.d.ts.map +1 -0
  197. package/dist/inputs/SgInputPostalCode.js +193 -0
  198. package/dist/inputs/SgInputSelect.d.ts +16 -0
  199. package/dist/inputs/SgInputSelect.d.ts.map +1 -0
  200. package/dist/inputs/SgInputSelect.js +104 -0
  201. package/dist/inputs/SgInputText.d.ts +49 -0
  202. package/dist/inputs/SgInputText.d.ts.map +1 -0
  203. package/dist/inputs/SgInputText.js +336 -0
  204. package/dist/inputs/SgInputTextArea.d.ts +41 -0
  205. package/dist/inputs/SgInputTextArea.d.ts.map +1 -0
  206. package/dist/inputs/SgInputTextArea.js +216 -0
  207. package/dist/inputs/SgTextEditor.d.ts +27 -0
  208. package/dist/inputs/SgTextEditor.d.ts.map +1 -0
  209. package/dist/inputs/SgTextEditor.js +201 -0
  210. package/dist/integration/module.d.ts +39 -0
  211. package/dist/integration/module.d.ts.map +1 -0
  212. package/dist/integration/module.js +1 -0
  213. package/dist/layout/GroupBox.d.ts +10 -0
  214. package/dist/layout/GroupBox.d.ts.map +1 -0
  215. package/dist/layout/GroupBox.js +14 -0
  216. package/dist/layout/SgCard.d.ts +35 -0
  217. package/dist/layout/SgCard.d.ts.map +1 -0
  218. package/dist/layout/SgCard.js +106 -0
  219. package/dist/layout/SgDockLayout.d.ts +37 -0
  220. package/dist/layout/SgDockLayout.d.ts.map +1 -0
  221. package/dist/layout/SgDockLayout.js +101 -0
  222. package/dist/layout/SgDockZone.d.ts +12 -0
  223. package/dist/layout/SgDockZone.d.ts.map +1 -0
  224. package/dist/layout/SgDockZone.js +20 -0
  225. package/dist/layout/SgGrid.d.ts +18 -0
  226. package/dist/layout/SgGrid.d.ts.map +1 -0
  227. package/dist/layout/SgGrid.js +101 -0
  228. package/dist/layout/SgGroupBox.d.ts +10 -0
  229. package/dist/layout/SgGroupBox.d.ts.map +1 -0
  230. package/dist/layout/SgGroupBox.js +14 -0
  231. package/dist/layout/SgMainPanel.d.ts +11 -0
  232. package/dist/layout/SgMainPanel.d.ts.map +1 -0
  233. package/dist/layout/SgMainPanel.js +70 -0
  234. package/dist/layout/SgPanel.d.ts +22 -0
  235. package/dist/layout/SgPanel.d.ts.map +1 -0
  236. package/dist/layout/SgPanel.js +33 -0
  237. package/dist/layout/SgScreen.d.ts +11 -0
  238. package/dist/layout/SgScreen.d.ts.map +1 -0
  239. package/dist/layout/SgScreen.js +18 -0
  240. package/dist/layout/SgStack.d.ts +15 -0
  241. package/dist/layout/SgStack.d.ts.map +1 -0
  242. package/dist/layout/SgStack.js +32 -0
  243. package/dist/layout/SgToolBar.d.ts +46 -0
  244. package/dist/layout/SgToolBar.d.ts.map +1 -0
  245. package/dist/layout/SgToolBar.js +199 -0
  246. package/dist/layout/SgTreeView.d.ts +80 -0
  247. package/dist/layout/SgTreeView.d.ts.map +1 -0
  248. package/dist/layout/SgTreeView.js +338 -0
  249. package/dist/manifest.d.ts +3 -0
  250. package/dist/manifest.d.ts.map +1 -0
  251. package/dist/manifest.js +19 -0
  252. package/dist/masks.d.ts +14 -0
  253. package/dist/masks.d.ts.map +1 -0
  254. package/dist/masks.js +91 -0
  255. package/dist/overlay/SgDialog.d.ts +39 -0
  256. package/dist/overlay/SgDialog.d.ts.map +1 -0
  257. package/dist/overlay/SgDialog.js +177 -0
  258. package/dist/overlay/SgPopup.d.ts +42 -0
  259. package/dist/overlay/SgPopup.d.ts.map +1 -0
  260. package/dist/overlay/SgPopup.js +218 -0
  261. package/dist/rhf.d.ts +6 -0
  262. package/dist/rhf.d.ts.map +1 -0
  263. package/dist/rhf.js +1 -0
  264. package/dist/validators.d.ts +27 -0
  265. package/dist/validators.d.ts.map +1 -0
  266. package/dist/validators.js +218 -0
  267. package/dist/wizard/SGWizard.d.ts +28 -0
  268. package/dist/wizard/SGWizard.d.ts.map +1 -0
  269. package/dist/wizard/SGWizard.js +124 -0
  270. package/package.json +53 -0
@@ -0,0 +1,149 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { SgButton } from "./SgButton";
5
+ /* ── helpers ── */
6
+ function cn(...parts) {
7
+ return parts.filter(Boolean).join(" ");
8
+ }
9
+ /* ── constants ── */
10
+ const ITEM_GAP = { sm: 40, md: 48, lg: 56 };
11
+ const DEFAULT_RADIUS = { sm: 100, md: 120, lg: 150 };
12
+ const DIRECTION_START_ANGLE = {
13
+ up: 270,
14
+ down: 90,
15
+ left: 180,
16
+ right: 0
17
+ };
18
+ const DEFAULT_TOOLTIP = {
19
+ up: "left",
20
+ down: "right",
21
+ left: "top",
22
+ right: "bottom"
23
+ };
24
+ /* ── positioning ── */
25
+ function degToRad(deg) {
26
+ return (deg * Math.PI) / 180;
27
+ }
28
+ function computeItemPositions(items, type, direction, size, radius) {
29
+ const count = items.length;
30
+ if (count === 0)
31
+ return [];
32
+ if (type === "linear") {
33
+ const gap = ITEM_GAP[size];
34
+ return items.map((_, i) => {
35
+ const dist = gap * (i + 1);
36
+ switch (direction) {
37
+ case "up":
38
+ return { x: 0, y: -dist };
39
+ case "down":
40
+ return { x: 0, y: dist };
41
+ case "left":
42
+ return { x: -dist, y: 0 };
43
+ case "right":
44
+ return { x: dist, y: 0 };
45
+ }
46
+ });
47
+ }
48
+ const r = radius ?? DEFAULT_RADIUS[size];
49
+ const centerAngle = DIRECTION_START_ANGLE[direction];
50
+ let totalArc;
51
+ switch (type) {
52
+ case "circle":
53
+ totalArc = 360;
54
+ break;
55
+ case "semi-circle":
56
+ totalArc = 180;
57
+ break;
58
+ case "quarter-circle":
59
+ totalArc = 90;
60
+ break;
61
+ default:
62
+ totalArc = 360;
63
+ }
64
+ const step = type === "circle" ? totalArc / count : totalArc / Math.max(count - 1, 1);
65
+ const startAngle = type === "circle" ? centerAngle : centerAngle - totalArc / 2;
66
+ return items.map((_, i) => {
67
+ const angle = startAngle + step * i;
68
+ const rad = degToRad(angle);
69
+ return {
70
+ x: Math.round(r * Math.cos(rad)),
71
+ y: Math.round(r * Math.sin(rad))
72
+ };
73
+ });
74
+ }
75
+ /* ── default icons ── */
76
+ function PlusIcon() {
77
+ return (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M5 12h14" }), _jsx("path", { d: "M12 5v14" })] }));
78
+ }
79
+ /* ── tooltip ── */
80
+ const TOOLTIP_POS = {
81
+ top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
82
+ bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
83
+ left: "right-full top-1/2 -translate-y-1/2 mr-2",
84
+ right: "left-full top-1/2 -translate-y-1/2 ml-2"
85
+ };
86
+ /* ── component ── */
87
+ export function SgSpeedDial(props) {
88
+ const { items, direction = "up", type = "linear", severity = "primary", appearance = "solid", size = "md", icon, activeIcon, radius, disabled = false, className, style, transitionDelay = 30, mask = false, tooltipPosition, onOpen, onClose } = props;
89
+ const [open, setOpen] = React.useState(false);
90
+ const containerRef = React.useRef(null);
91
+ const toggle = React.useCallback(() => {
92
+ if (disabled)
93
+ return;
94
+ setOpen((prev) => {
95
+ const next = !prev;
96
+ if (next)
97
+ onOpen?.();
98
+ else
99
+ onClose?.();
100
+ return next;
101
+ });
102
+ }, [disabled, onOpen, onClose]);
103
+ const close = React.useCallback(() => {
104
+ if (open) {
105
+ setOpen(false);
106
+ onClose?.();
107
+ }
108
+ }, [open, onClose]);
109
+ // click-outside
110
+ React.useEffect(() => {
111
+ if (!open)
112
+ return;
113
+ function handleClick(e) {
114
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
115
+ close();
116
+ }
117
+ }
118
+ function handleEscape(e) {
119
+ if (e.key === "Escape")
120
+ close();
121
+ }
122
+ document.addEventListener("mousedown", handleClick);
123
+ document.addEventListener("keydown", handleEscape);
124
+ return () => {
125
+ document.removeEventListener("mousedown", handleClick);
126
+ document.removeEventListener("keydown", handleEscape);
127
+ };
128
+ }, [open, close]);
129
+ const positions = computeItemPositions(items, type, direction, size, radius);
130
+ const tipPos = tooltipPosition ?? DEFAULT_TOOLTIP[direction];
131
+ const triggerIcon = icon ?? _jsx(PlusIcon, {});
132
+ const triggerActiveIcon = activeIcon ?? triggerIcon;
133
+ return (_jsxs(_Fragment, { children: [mask ? (_jsx("div", { className: cn("fixed inset-0 bg-black/50 transition-opacity duration-300 z-40", open ? "opacity-100" : "opacity-0 pointer-events-none"), onClick: close })) : null, _jsxs("div", { ref: containerRef, className: cn("relative inline-flex items-center justify-center", mask && open ? "z-50" : "z-auto", className), style: style, children: [items.map((item, i) => {
134
+ const pos = positions[i] ?? { x: 0, y: 0 };
135
+ const itemSeverity = item.severity ?? "plain";
136
+ return (_jsx("div", { className: "absolute", style: {
137
+ transform: open
138
+ ? `translate(${pos.x}px, ${pos.y}px) scale(1)`
139
+ : "translate(0, 0) scale(0)",
140
+ opacity: open ? 1 : 0,
141
+ transition: `transform 200ms ease, opacity 200ms ease`,
142
+ transitionDelay: open ? `${i * transitionDelay}ms` : `${(items.length - 1 - i) * transitionDelay}ms`
143
+ }, children: _jsxs("div", { className: "relative group", children: [_jsx(SgButton, { severity: itemSeverity, appearance: itemSeverity === "plain" ? "solid" : appearance, size: size, shape: "rounded", disabled: item.disabled, className: item.className, onClick: () => {
144
+ item.onClick?.();
145
+ close();
146
+ }, "aria-label": item.label, leftIcon: item.icon }), item.label ? (_jsx("span", { className: cn("absolute whitespace-nowrap rounded bg-foreground/90 px-2 py-1 text-xs text-background", "opacity-0 group-hover:opacity-100 transition-opacity duration-150 pointer-events-none", TOOLTIP_POS[tipPos]), children: item.label })) : null] }) }, i));
147
+ }), _jsx(SgButton, { severity: severity, appearance: appearance, size: size, shape: "rounded", disabled: disabled, onClick: toggle, "aria-label": open ? "Close" : "Open", "aria-expanded": open, leftIcon: _jsx("span", { className: "inline-flex transition-transform duration-300", style: { transform: open ? "rotate(45deg)" : "rotate(0deg)" }, children: open ? triggerActiveIcon : triggerIcon }) })] })] }));
148
+ }
149
+ SgSpeedDial.displayName = "SgSpeedDial";
@@ -0,0 +1,32 @@
1
+ import * as React from "react";
2
+ import type { SgButtonCustomColors } from "./SgButton";
3
+ type Severity = "primary" | "secondary" | "success" | "info" | "warning" | "help" | "danger";
4
+ type Appearance = "solid" | "outline" | "ghost";
5
+ type Size = "sm" | "md" | "lg";
6
+ type Shape = "default" | "rounded" | "square";
7
+ type Elevation = "none" | "sm" | "md";
8
+ export type SgSplitButtonItem = {
9
+ label: string;
10
+ icon?: React.ReactNode;
11
+ onClick?: () => void;
12
+ disabled?: boolean;
13
+ separator?: boolean;
14
+ };
15
+ export type SgSplitButtonProps = {
16
+ label?: string;
17
+ leftIcon?: React.ReactNode;
18
+ severity?: Severity;
19
+ appearance?: Appearance;
20
+ size?: Size;
21
+ shape?: Shape;
22
+ elevation?: Elevation;
23
+ disabled?: boolean;
24
+ loading?: boolean;
25
+ onClick?: () => void;
26
+ items: SgSplitButtonItem[];
27
+ customColors?: SgButtonCustomColors;
28
+ className?: string;
29
+ };
30
+ export declare const SgSplitButton: React.ForwardRefExoticComponent<SgSplitButtonProps & React.RefAttributes<HTMLDivElement>>;
31
+ export {};
32
+ //# sourceMappingURL=SgSplitButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgSplitButton.d.ts","sourceRoot":"","sources":["../../src/buttons/SgSplitButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,KAAK,QAAQ,GACT,SAAS,GACT,WAAW,GACX,SAAS,GACT,MAAM,GACN,SAAS,GACT,MAAM,GACN,QAAQ,CAAC;AAEb,KAAK,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAEhD,KAAK,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/B,KAAK,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE9C,KAAK,SAAS,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AAEtC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAmBF,eAAO,MAAM,aAAa,2FAkKzB,CAAC"}
@@ -0,0 +1,81 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { SgButton, resolveButtonColors } from "./SgButton";
5
+ function ChevronDown({ className }) {
6
+ return (_jsx("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m6 9 6 6 6-6" }) }));
7
+ }
8
+ export const SgSplitButton = React.forwardRef(({ label, leftIcon, severity = "primary", appearance = "solid", size = "md", shape = "default", elevation = "none", disabled = false, loading = false, onClick, items, customColors, className }, ref) => {
9
+ const [open, setOpen] = React.useState(false);
10
+ const containerRef = React.useRef(null);
11
+ const menuRef = React.useRef(null);
12
+ React.useImperativeHandle(ref, () => containerRef.current);
13
+ React.useEffect(() => {
14
+ if (!open)
15
+ return;
16
+ function handleClickOutside(e) {
17
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
18
+ setOpen(false);
19
+ }
20
+ }
21
+ function handleEscape(e) {
22
+ if (e.key === "Escape")
23
+ setOpen(false);
24
+ }
25
+ document.addEventListener("mousedown", handleClickOutside);
26
+ document.addEventListener("keydown", handleEscape);
27
+ return () => {
28
+ document.removeEventListener("mousedown", handleClickOutside);
29
+ document.removeEventListener("keydown", handleEscape);
30
+ };
31
+ }, [open]);
32
+ const toggle = () => {
33
+ if (!disabled && !loading)
34
+ setOpen((prev) => !prev);
35
+ };
36
+ const handleItemClick = (item) => {
37
+ if (item.disabled)
38
+ return;
39
+ item.onClick?.();
40
+ setOpen(false);
41
+ };
42
+ const colors = resolveButtonColors(severity, customColors);
43
+ const toneKey = severity === "danger"
44
+ ? "error"
45
+ : severity === "help"
46
+ ? "tertiary"
47
+ : severity;
48
+ const tone100 = `var(--sg-${toneKey}-100, var(--sg-primary-100))`;
49
+ const tone200 = `var(--sg-${toneKey}-200, ${tone100})`;
50
+ const tone300 = `var(--sg-${toneKey}-300, ${tone200})`;
51
+ const menuBg = `rgb(${tone100})`;
52
+ const menuFg = colors.bg;
53
+ const menuHoverBg = `rgb(${tone200})`;
54
+ const menuBorder = `rgb(${tone200})`;
55
+ const dividerBorderClass = appearance === "solid"
56
+ ? "border-l border-[rgb(var(--sg-border))]"
57
+ : appearance === "outline"
58
+ ? "border-l border-[var(--sg-btn-border,currentColor)]"
59
+ : "border-l border-current/20";
60
+ const sizeClasses = {
61
+ sm: { menu: "min-w-[140px]", item: "px-3 py-1.5", text: "text-sm", iconSize: "size-3.5" },
62
+ md: { menu: "min-w-[160px]", item: "px-4 py-2", text: "text-sm", iconSize: "size-4" },
63
+ lg: { menu: "min-w-[180px]", item: "px-5 py-2.5", text: "text-base", iconSize: "size-5" }
64
+ };
65
+ const s = sizeClasses[size];
66
+ return (_jsxs("div", { ref: containerRef, className: `relative inline-flex ${className ?? ""}`, children: [_jsx(SgButton, { severity: severity, appearance: appearance, size: size, shape: shape, elevation: elevation, disabled: disabled, loading: loading, leftIcon: leftIcon, onClick: onClick, customColors: customColors, className: "rounded-r-none", children: label }), _jsx(SgButton, { severity: severity, appearance: appearance, size: size, shape: shape, elevation: elevation, disabled: disabled || loading, leftIcon: _jsx(ChevronDown, { className: "size-4" }), onClick: toggle, customColors: customColors, className: `rounded-l-none ${dividerBorderClass}`, "aria-haspopup": "true", "aria-expanded": open }), open && (_jsx("div", { ref: menuRef, role: "menu", className: `absolute right-0 top-full z-50 mt-1 ${s.menu} overflow-hidden rounded-lg shadow-lg`, style: {
67
+ backgroundColor: menuBg,
68
+ color: menuFg,
69
+ border: `1px solid ${menuBorder}`
70
+ }, children: items.map((item, i) => (_jsxs(React.Fragment, { children: [item.separator && i > 0 ? (_jsx("div", { className: "my-0.5", style: {
71
+ borderTop: `1px solid ${appearance === "solid" ? `rgb(${tone300})` : "rgb(var(--sg-text)/0.2)"}`
72
+ } })) : null, _jsxs("button", { role: "menuitem", type: "button", disabled: item.disabled, className: `flex w-full items-center gap-2 ${s.item} ${s.text} transition-colors disabled:opacity-50 disabled:cursor-not-allowed`, style: {
73
+ backgroundColor: "transparent"
74
+ }, onMouseEnter: (e) => {
75
+ if (!item.disabled)
76
+ (e.currentTarget.style.backgroundColor = menuHoverBg);
77
+ }, onMouseLeave: (e) => {
78
+ e.currentTarget.style.backgroundColor = "transparent";
79
+ }, onClick: () => handleItemClick(item), children: [item.icon ? _jsx("span", { className: `shrink-0 ${s.iconSize}`, children: item.icon }) : null, _jsx("span", { children: item.label })] })] }, i))) }))] }));
80
+ });
81
+ SgSplitButton.displayName = "SgSplitButton";
@@ -0,0 +1,28 @@
1
+ import * as React from "react";
2
+ import type { SgClockTheme } from "./themes/types";
3
+ export type SgClockProps = {
4
+ variant?: "digital" | "analog" | "instrument";
5
+ size?: "sm" | "md" | "lg" | number;
6
+ timezone?: string;
7
+ locale?: string;
8
+ format?: "12h" | "24h";
9
+ showSeconds?: boolean;
10
+ digitalStyle?: "default" | "segment";
11
+ secondHandMode?: "step" | "smooth";
12
+ instrumentMode?: "hours" | "minutes" | "seconds";
13
+ instrumentWindow?: number;
14
+ instrumentRange?: {
15
+ start: number;
16
+ end: number;
17
+ };
18
+ instrumentStep?: number;
19
+ instrumentStyle?: "outlined" | "soft";
20
+ instrumentHighlight?: boolean;
21
+ instrumentSmooth?: boolean;
22
+ themeId?: string;
23
+ theme?: SgClockTheme;
24
+ className?: string;
25
+ centerOverlay?: React.ReactNode;
26
+ };
27
+ export declare function SgClock(props: SgClockProps): import("react/jsx-runtime").JSX.Element;
28
+ //# sourceMappingURL=SgClock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgClock.d.ts","sourceRoot":"","sources":["../../src/clock/SgClock.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAenD,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC9C,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IACrC,cAAc,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACnC,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;IACjD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACtC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACjC,CAAC;AA+bF,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,2CAqE1C"}
@@ -0,0 +1,280 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useSgTime } from "./SgTimeProvider";
5
+ import { useSgClockThemeResolver } from "./themes/provider";
6
+ import { ThemeLayer, resolveTheme } from "./themes/renderTheme";
7
+ import { useDarkFlag } from "./themes/useDarkFlag";
8
+ import { getTheme } from "./themes/registry";
9
+ function cn(...parts) {
10
+ return parts.filter(Boolean).join(" ");
11
+ }
12
+ function useSecondTick() {
13
+ const [, setTick] = React.useState(0);
14
+ React.useEffect(() => {
15
+ const id = window.setInterval(() => setTick((v) => v + 1), 1000);
16
+ return () => window.clearInterval(id);
17
+ }, []);
18
+ }
19
+ function getHmsForTimezone(date, locale, timeZone) {
20
+ const parts = new Intl.DateTimeFormat(locale, {
21
+ timeZone,
22
+ hour: "2-digit",
23
+ minute: "2-digit",
24
+ second: "2-digit",
25
+ hour12: false
26
+ }).formatToParts(date);
27
+ const get = (type) => {
28
+ const p = parts.find((x) => x.type === type)?.value ?? "0";
29
+ const n = Number.parseInt(p, 10);
30
+ return Number.isFinite(n) ? n : 0;
31
+ };
32
+ return { h: get("hour"), m: get("minute"), s: get("second") };
33
+ }
34
+ function sizeToClass(size) {
35
+ if (typeof size === "number")
36
+ return "";
37
+ if (size === "sm")
38
+ return "text-sm";
39
+ if (size === "lg")
40
+ return "text-2xl";
41
+ return "text-base";
42
+ }
43
+ function digitalSizeToNumber(size) {
44
+ if (typeof size === "number")
45
+ return size;
46
+ if (size === "sm")
47
+ return 12;
48
+ if (size === "lg")
49
+ return 28;
50
+ return 16;
51
+ }
52
+ const SEGMENTS = {
53
+ "0": [
54
+ { x: 1, y: 0, w: 4, h: 1 },
55
+ { x: 5, y: 1, w: 1, h: 4 },
56
+ { x: 5, y: 5, w: 1, h: 4 },
57
+ { x: 1, y: 9, w: 4, h: 1 },
58
+ { x: 0, y: 5, w: 1, h: 4 },
59
+ { x: 0, y: 1, w: 1, h: 4 }
60
+ ],
61
+ "1": [
62
+ { x: 5, y: 1, w: 1, h: 4 },
63
+ { x: 5, y: 5, w: 1, h: 4 }
64
+ ],
65
+ "2": [
66
+ { x: 1, y: 0, w: 4, h: 1 },
67
+ { x: 5, y: 1, w: 1, h: 4 },
68
+ { x: 1, y: 4.5, w: 4, h: 1 },
69
+ { x: 0, y: 5, w: 1, h: 4 },
70
+ { x: 1, y: 9, w: 4, h: 1 }
71
+ ],
72
+ "3": [
73
+ { x: 1, y: 0, w: 4, h: 1 },
74
+ { x: 5, y: 1, w: 1, h: 4 },
75
+ { x: 1, y: 4.5, w: 4, h: 1 },
76
+ { x: 5, y: 5, w: 1, h: 4 },
77
+ { x: 1, y: 9, w: 4, h: 1 }
78
+ ],
79
+ "4": [
80
+ { x: 0, y: 1, w: 1, h: 4 },
81
+ { x: 5, y: 1, w: 1, h: 4 },
82
+ { x: 1, y: 4.5, w: 4, h: 1 },
83
+ { x: 5, y: 5, w: 1, h: 4 }
84
+ ],
85
+ "5": [
86
+ { x: 1, y: 0, w: 4, h: 1 },
87
+ { x: 0, y: 1, w: 1, h: 4 },
88
+ { x: 1, y: 4.5, w: 4, h: 1 },
89
+ { x: 5, y: 5, w: 1, h: 4 },
90
+ { x: 1, y: 9, w: 4, h: 1 }
91
+ ],
92
+ "6": [
93
+ { x: 1, y: 0, w: 4, h: 1 },
94
+ { x: 0, y: 1, w: 1, h: 4 },
95
+ { x: 1, y: 4.5, w: 4, h: 1 },
96
+ { x: 0, y: 5, w: 1, h: 4 },
97
+ { x: 5, y: 5, w: 1, h: 4 },
98
+ { x: 1, y: 9, w: 4, h: 1 }
99
+ ],
100
+ "7": [
101
+ { x: 1, y: 0, w: 4, h: 1 },
102
+ { x: 5, y: 1, w: 1, h: 4 },
103
+ { x: 5, y: 5, w: 1, h: 4 }
104
+ ],
105
+ "8": [
106
+ { x: 1, y: 0, w: 4, h: 1 },
107
+ { x: 5, y: 1, w: 1, h: 4 },
108
+ { x: 5, y: 5, w: 1, h: 4 },
109
+ { x: 1, y: 9, w: 4, h: 1 },
110
+ { x: 0, y: 5, w: 1, h: 4 },
111
+ { x: 0, y: 1, w: 1, h: 4 },
112
+ { x: 1, y: 4.5, w: 4, h: 1 }
113
+ ],
114
+ "9": [
115
+ { x: 1, y: 0, w: 4, h: 1 },
116
+ { x: 5, y: 1, w: 1, h: 4 },
117
+ { x: 5, y: 5, w: 1, h: 4 },
118
+ { x: 1, y: 9, w: 4, h: 1 },
119
+ { x: 0, y: 1, w: 1, h: 4 },
120
+ { x: 1, y: 4.5, w: 4, h: 1 }
121
+ ]
122
+ };
123
+ function round(n) {
124
+ return Math.round(n * 1000) / 1000;
125
+ }
126
+ function renderSegmentDigit(digit, x, y, scale, keyPrefix) {
127
+ const segs = SEGMENTS[digit] ?? [];
128
+ return segs.map((s, idx) => (_jsx("rect", { x: round(x + s.x * scale), y: round(y + s.y * scale), width: round(s.w * scale), height: round(s.h * scale), className: "fill-neutral-800 dark:fill-neutral-200", rx: round(0.3 * scale) }, `${keyPrefix}-${idx}`)));
129
+ }
130
+ function renderSegmentChar(ch, x, y, scale, keyPrefix) {
131
+ if (ch === ":") {
132
+ return (_jsxs("g", { children: [_jsx("rect", { x: round(x + 2.2 * scale), y: round(y + 2 * scale), width: round(1 * scale), height: round(1 * scale), className: "fill-neutral-800 dark:fill-neutral-200" }), _jsx("rect", { x: round(x + 2.2 * scale), y: round(y + 7 * scale), width: round(1 * scale), height: round(1 * scale), className: "fill-neutral-800 dark:fill-neutral-200" })] }, keyPrefix));
133
+ }
134
+ if (!SEGMENTS[ch])
135
+ return null;
136
+ return _jsx("g", { children: renderSegmentDigit(ch, x, y, scale, keyPrefix) }, keyPrefix);
137
+ }
138
+ function renderSegmentText(text, sizePx) {
139
+ const baseScale = sizePx / 16;
140
+ const digitW = 6 * baseScale;
141
+ const digitH = 10 * baseScale;
142
+ const gap = 1.4 * baseScale;
143
+ const colonW = 4 * baseScale;
144
+ let width = 0;
145
+ for (const ch of text) {
146
+ width += ch === ":" ? colonW : digitW;
147
+ width += gap;
148
+ }
149
+ width -= gap;
150
+ const height = digitH;
151
+ let cursor = 0;
152
+ const nodes = [];
153
+ text.split("").forEach((ch, idx) => {
154
+ const w = ch === ":" ? colonW : digitW;
155
+ nodes.push(renderSegmentChar(ch, cursor, 0, baseScale, `seg-${idx}`));
156
+ cursor += w + gap;
157
+ });
158
+ return { width, height, nodes };
159
+ }
160
+ function wrapRange(value, start, end) {
161
+ const size = end - start + 1;
162
+ const v = ((value - start) % size + size) % size;
163
+ return start + v;
164
+ }
165
+ function getTimeValue(date, locale, timeZone, mode, smooth) {
166
+ const { h, m, s } = getHmsForTimezone(date, locale, timeZone);
167
+ const ms = date.getMilliseconds();
168
+ if (mode === "hours") {
169
+ if (!smooth)
170
+ return { base: h, frac: 0 };
171
+ const value = h + m / 60 + s / 3600;
172
+ return { base: Math.floor(value), frac: value - Math.floor(value) };
173
+ }
174
+ if (mode === "minutes") {
175
+ if (!smooth)
176
+ return { base: m, frac: 0 };
177
+ const value = m + s / 60;
178
+ return { base: Math.floor(value), frac: value - Math.floor(value) };
179
+ }
180
+ if (!smooth)
181
+ return { base: s, frac: 0 };
182
+ const value = s + ms / 1000;
183
+ return { base: Math.floor(value), frac: value - Math.floor(value) };
184
+ }
185
+ function InstrumentClock({ size = "md", locale = "pt-BR", timezone, instrumentWindow = 12, instrumentRange, instrumentStep = 1, instrumentStyle = "outlined", instrumentHighlight = true, instrumentSmooth = true, className }) {
186
+ const { tick, nowMs } = useSgTime();
187
+ void tick;
188
+ useSecondTick();
189
+ const date = new Date(nowMs());
190
+ const windowSize = instrumentWindow % 2 === 0 ? instrumentWindow + 1 : instrumentWindow;
191
+ const centerIndex = Math.floor(windowSize / 2);
192
+ const cellWidth = typeof size === "number" ? size : size === "sm" ? 36 : size === "lg" ? 64 : 48;
193
+ const height = Math.round(cellWidth * 0.9);
194
+ const pad = instrumentStyle === "soft" ? "bg-muted/40" : "bg-background";
195
+ const border = instrumentStyle === "outlined" ? "border border-border" : "";
196
+ const lastPosRef = React.useRef({
197
+ hours: null,
198
+ minutes: null,
199
+ seconds: null
200
+ });
201
+ const renderLine = (mode, range) => {
202
+ const { base, frac } = getTimeValue(date, locale, timezone, mode, instrumentSmooth);
203
+ const values = Array.from({ length: windowSize }).map((_, idx) => {
204
+ const offset = (idx - centerIndex) * instrumentStep;
205
+ const value = wrapRange(base + offset, range.start, range.end);
206
+ return value;
207
+ });
208
+ const pos = base + frac;
209
+ const lastPos = lastPosRef.current[mode] ?? null;
210
+ if (lastPos === null) {
211
+ lastPosRef.current[mode] = pos;
212
+ }
213
+ else if (instrumentSmooth) {
214
+ const rangeSize = range.end - range.start + 1;
215
+ const delta = pos - lastPos;
216
+ if (delta < -rangeSize / 2) {
217
+ lastPosRef.current[mode] = lastPos - rangeSize;
218
+ }
219
+ }
220
+ const translateX = ((lastPosRef.current[mode] ?? pos) - pos) * cellWidth;
221
+ lastPosRef.current[mode] = pos;
222
+ return (_jsxs("div", { className: cn("relative overflow-hidden rounded-lg", pad, border), style: { height }, children: [_jsx("div", { className: "flex items-center", style: {
223
+ transform: `translateX(${translateX}px)`,
224
+ transition: instrumentSmooth ? "transform 60ms linear" : "none"
225
+ }, children: values.map((v, idx) => {
226
+ const isCenter = idx === centerIndex;
227
+ return (_jsx("div", { className: cn("flex items-center justify-center font-mono tabular-nums text-muted-foreground", isCenter && instrumentHighlight ? "text-foreground font-semibold" : ""), style: { width: cellWidth, height }, children: String(v).padStart(2, "0") }, `${mode}-${v}-${idx}`));
228
+ }) }), _jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: _jsxs("div", { className: "relative", children: [_jsx("div", { className: "absolute -top-2 left-1/2 h-2 w-0.5 -translate-x-1/2 bg-foreground" }), _jsx("div", { className: "absolute -bottom-2 left-1/2 h-2 w-0.5 -translate-x-1/2 bg-foreground" }), _jsx("div", { className: "h-full w-0.5 bg-foreground", style: { height } })] }) })] }));
229
+ };
230
+ return (_jsxs("div", { className: cn("relative w-full max-w-full space-y-3", className), children: [renderLine("hours", instrumentRange ?? { start: 0, end: 23 }), renderLine("minutes", instrumentRange ?? { start: 0, end: 59 }), renderLine("seconds", instrumentRange ?? { start: 0, end: 59 })] }));
231
+ }
232
+ function AnalogClock({ size = 280, timezone, locale = "pt-BR", showSeconds = true, secondHandMode = "step", themeId = "classic", theme, className, centerOverlay }) {
233
+ const { tick, nowMs } = useSgTime();
234
+ void tick;
235
+ useSecondTick();
236
+ const resolver = useSgClockThemeResolver();
237
+ const dark = useDarkFlag();
238
+ const date = new Date(nowMs());
239
+ const { h, m, s } = getHmsForTimezone(date, locale, timezone);
240
+ const ms = date.getMilliseconds();
241
+ const sec = secondHandMode === "smooth" ? s + ms / 1000 : s;
242
+ const secDeg = (sec / 60) * 360;
243
+ const minDeg = ((m + sec / 60) / 60) * 360;
244
+ const hourDeg = (((h % 12) + m / 60 + sec / 3600) / 12) * 360;
245
+ const themeObj = theme ??
246
+ (resolver ? resolveTheme(resolver, themeId, "classic") : getTheme(themeId) ?? getTheme("classic"));
247
+ return (_jsx("div", { className: className, children: _jsxs("svg", { width: size, height: size, viewBox: "0 0 100 100", className: "block", "aria-label": "Analog clock", children: [_jsx("g", { id: "theme", children: themeObj ? _jsx(ThemeLayer, { theme: themeObj, args: { size, dark } }) : null }), _jsxs("g", { id: "hands", children: [_jsx("g", { transform: `rotate(${hourDeg} 50 50)`, children: _jsx("line", { x1: "50", y1: "50", x2: "50", y2: "28", className: "stroke-neutral-800 dark:stroke-neutral-200", strokeWidth: "2.6", strokeLinecap: "round" }) }), _jsx("g", { transform: `rotate(${minDeg} 50 50)`, children: _jsx("line", { x1: "50", y1: "50", x2: "50", y2: "18", className: "stroke-neutral-800 dark:stroke-neutral-200", strokeWidth: "1.7", strokeLinecap: "round" }) }), showSeconds && (_jsx("g", { transform: `rotate(${secDeg} 50 50)`, children: _jsx("line", { x1: "50", y1: "54", x2: "50", y2: "14", className: "stroke-rose-500", strokeWidth: "0.9", strokeLinecap: "round" }) }))] }), _jsx("circle", { cx: "50", cy: "50", r: "2.2", className: "fill-neutral-800 dark:fill-neutral-200" }), showSeconds ? _jsx("circle", { cx: "50", cy: "50", r: "1.1", className: "fill-rose-500" }) : null, centerOverlay ? (_jsx("foreignObject", { x: "35", y: "35", width: "30", height: "30", children: _jsx("div", { className: "flex h-full w-full items-center justify-center", children: centerOverlay }) })) : null] }) }));
248
+ }
249
+ function DigitalClock({ timezone, locale = "pt-BR", format = "24h", showSeconds = true, size = "md", digitalStyle = "default", className }) {
250
+ const { tick, nowMs } = useSgTime();
251
+ void tick;
252
+ useSecondTick();
253
+ const d = new Date(nowMs());
254
+ const text = new Intl.DateTimeFormat(locale, {
255
+ timeZone: timezone,
256
+ hour: "2-digit",
257
+ minute: "2-digit",
258
+ second: showSeconds ? "2-digit" : undefined,
259
+ hour12: format === "12h"
260
+ }).format(d);
261
+ const classSize = sizeToClass(size);
262
+ const fontSize = typeof size === "number" ? { fontSize: `${size}px`, lineHeight: 1 } : undefined;
263
+ const sizePx = digitalSizeToNumber(size);
264
+ if (digitalStyle === "segment") {
265
+ const seg = renderSegmentText(text, sizePx);
266
+ return (_jsx("svg", { width: seg.width, height: seg.height, viewBox: `0 0 ${seg.width} ${seg.height}`, className: cn("block", className), "aria-label": "Digital clock", children: seg.nodes }));
267
+ }
268
+ return (_jsx("div", { className: cn("font-mono tabular-nums", classSize, className), style: fontSize, children: text }));
269
+ }
270
+ export function SgClock(props) {
271
+ const { variant = "digital", size = "md", timezone, locale = "pt-BR", format = "24h", showSeconds = true, digitalStyle = "default", secondHandMode = "step", instrumentMode = "minutes", instrumentWindow = 12, instrumentRange, instrumentStep = 1, instrumentStyle = "outlined", instrumentHighlight = true, instrumentSmooth = true, themeId = "classic", theme, className, centerOverlay } = props;
272
+ if (variant === "analog") {
273
+ const analogSize = typeof size === "number" ? size : size === "sm" ? 140 : size === "lg" ? 320 : 240;
274
+ return (_jsx(AnalogClock, { size: analogSize, themeId: themeId, theme: theme, timezone: timezone, locale: locale, showSeconds: showSeconds, secondHandMode: secondHandMode, className: className, centerOverlay: centerOverlay }));
275
+ }
276
+ if (variant === "instrument") {
277
+ return (_jsx(InstrumentClock, { size: size, locale: locale, timezone: timezone, instrumentMode: instrumentMode, instrumentWindow: instrumentWindow, instrumentRange: instrumentRange, instrumentStep: instrumentStep, instrumentStyle: instrumentStyle, instrumentHighlight: instrumentHighlight, instrumentSmooth: instrumentSmooth, className: className }));
278
+ }
279
+ return (_jsx(DigitalClock, { timezone: timezone, locale: locale, format: format, showSeconds: showSeconds, size: size, digitalStyle: digitalStyle, className: className }));
280
+ }
@@ -0,0 +1,13 @@
1
+ import * as React from "react";
2
+ export type SgTimeContextValue = {
3
+ serverStartMs: number;
4
+ perfStartMs: number;
5
+ tick: number;
6
+ nowMs: () => number;
7
+ };
8
+ export declare function useSgTime(): SgTimeContextValue;
9
+ export declare function SgTimeProvider({ initialServerTime, children }: {
10
+ initialServerTime: string;
11
+ children: React.ReactNode;
12
+ }): import("react/jsx-runtime").JSX.Element;
13
+ //# sourceMappingURL=SgTimeProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgTimeProvider.d.ts","sourceRoot":"","sources":["../../src/clock/SgTimeProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,MAAM,CAAC;CACrB,CAAC;AAIF,wBAAgB,SAAS,uBAIxB;AAED,wBAAgB,cAAc,CAAC,EAC7B,iBAAiB,EACjB,QAAQ,EACT,EAAE;IACD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,2CAyCA"}
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ const SgTimeContext = React.createContext(null);
5
+ export function useSgTime() {
6
+ const ctx = React.useContext(SgTimeContext);
7
+ if (!ctx)
8
+ throw new Error("useSgTime must be used within <SgTimeProvider />");
9
+ return ctx;
10
+ }
11
+ export function SgTimeProvider({ initialServerTime, children }) {
12
+ const serverStartMsRef = React.useRef(Date.parse(initialServerTime));
13
+ const perfStartMsRef = React.useRef(0);
14
+ const [tick, setTick] = React.useState(0);
15
+ const [hydrated, setHydrated] = React.useState(false);
16
+ React.useEffect(() => {
17
+ perfStartMsRef.current = performance.now();
18
+ setHydrated(true);
19
+ const alignDelay = 1000 - (Date.now() % 1000);
20
+ let intervalId = null;
21
+ const timeoutId = window.setTimeout(() => {
22
+ setTick((x) => x + 1);
23
+ intervalId = window.setInterval(() => setTick((x) => x + 1), 1000);
24
+ }, alignDelay);
25
+ return () => {
26
+ window.clearTimeout(timeoutId);
27
+ if (intervalId)
28
+ window.clearInterval(intervalId);
29
+ };
30
+ }, []);
31
+ const nowMs = React.useCallback(() => {
32
+ if (!hydrated)
33
+ return serverStartMsRef.current;
34
+ const delta = performance.now() - perfStartMsRef.current;
35
+ return serverStartMsRef.current + delta;
36
+ }, [hydrated]);
37
+ const value = React.useMemo(() => ({
38
+ serverStartMs: serverStartMsRef.current,
39
+ perfStartMs: perfStartMsRef.current,
40
+ tick,
41
+ nowMs
42
+ }), [tick, nowMs]);
43
+ return _jsx(SgTimeContext.Provider, { value: value, children: children });
44
+ }
@@ -0,0 +1,14 @@
1
+ import type { SgClockTheme } from "./types";
2
+ export type SgClockThemePickerProps = {
3
+ value: string;
4
+ onChange: (id: string) => void;
5
+ label?: string;
6
+ placeholder?: string;
7
+ className?: string;
8
+ filter?: (theme: SgClockTheme) => boolean;
9
+ previewSize?: number;
10
+ searchable?: boolean;
11
+ fallbackThemeId?: string;
12
+ };
13
+ export declare function SgClockThemePicker({ value, onChange, label, placeholder, className, filter, previewSize, searchable, fallbackThemeId }: SgClockThemePickerProps): import("react/jsx-runtime").JSX.Element;
14
+ //# sourceMappingURL=SgClockThemePicker.d.ts.map