@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,135 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { Controller } from "react-hook-form";
5
+ import { t, useComponentsI18n } from "../i18n";
6
+ const STAR_SIZES = {
7
+ sm: 16,
8
+ md: 24,
9
+ lg: 32,
10
+ xl: 40
11
+ };
12
+ function StarIcon({ size, filled = false }) {
13
+ return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 24 24", fill: filled ? "currentColor" : "none", stroke: "currentColor", strokeWidth: filled ? 0 : 2, strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polygon", { points: "12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" }) }));
14
+ }
15
+ function CancelIcon({ size }) {
16
+ return (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "m15 9-6 6" }), _jsx("path", { d: "m9 9 6 6" })] }));
17
+ }
18
+ function ErrorText(props) {
19
+ if (!props.message)
20
+ return null;
21
+ return _jsx("p", { className: "text-xs text-red-600", children: props.message });
22
+ }
23
+ function SgRatingBase(props) {
24
+ const i18n = useComponentsI18n();
25
+ const { id, label, value = 0, stars = 5, allowHalf = false, cancel = true, disabled = false, readOnly = false, size = "md", className = "", onIcon, offIcon, cancelIcon, color = "hsl(var(--primary))", emptyColor = "hsl(var(--muted-foreground))", showTooltip = false, onChange, onHover, error, required = false, requiredMessage } = props;
26
+ const [hoverValue, setHoverValue] = React.useState(null);
27
+ const [internalValue, setInternalValue] = React.useState(value);
28
+ const [internalError, setInternalError] = React.useState(null);
29
+ React.useEffect(() => {
30
+ setInternalValue(value);
31
+ }, [value]);
32
+ const handleStarClick = (starIndex, isHalf) => {
33
+ if (disabled || readOnly)
34
+ return;
35
+ const newValue = allowHalf && isHalf ? starIndex + 0.5 : starIndex + 1;
36
+ setInternalValue(newValue);
37
+ onChange?.(newValue);
38
+ // Validation
39
+ if (required && newValue === 0) {
40
+ const message = requiredMessage ?? t(i18n, "components.rating.required");
41
+ setInternalError(message);
42
+ }
43
+ else {
44
+ setInternalError(null);
45
+ }
46
+ };
47
+ const handleStarHover = (starIndex, isHalf) => {
48
+ if (disabled || readOnly)
49
+ return;
50
+ const newHoverValue = allowHalf && isHalf ? starIndex + 0.5 : starIndex + 1;
51
+ setHoverValue(newHoverValue);
52
+ onHover?.(newHoverValue);
53
+ };
54
+ const handleMouseLeave = () => {
55
+ setHoverValue(null);
56
+ onHover?.(null);
57
+ };
58
+ const handleCancel = () => {
59
+ if (disabled || readOnly)
60
+ return;
61
+ setInternalValue(0);
62
+ onChange?.(0);
63
+ if (required) {
64
+ const message = requiredMessage ?? t(i18n, "components.rating.required");
65
+ setInternalError(message);
66
+ }
67
+ else {
68
+ setInternalError(null);
69
+ }
70
+ };
71
+ const displayValue = hoverValue ?? internalValue;
72
+ const starSize = STAR_SIZES[size];
73
+ const isInteractive = !disabled && !readOnly;
74
+ const getStarFillPercentage = (starIndex) => {
75
+ const starValue = starIndex + 1;
76
+ if (displayValue >= starValue)
77
+ return 100;
78
+ if (displayValue > starIndex && displayValue < starValue) {
79
+ return (displayValue - starIndex) * 100;
80
+ }
81
+ return 0;
82
+ };
83
+ return (_jsxs("div", { className: `sg-rating ${className}`, children: [label && (_jsxs("label", { htmlFor: id, className: "mb-2 block text-sm font-medium", children: [label, required && _jsx("span", { className: "ml-1 text-red-600", children: "*" })] })), _jsxs("div", { className: "flex items-center gap-1", onMouseLeave: handleMouseLeave, style: {
84
+ opacity: disabled ? 0.5 : 1,
85
+ cursor: isInteractive ? "pointer" : "default"
86
+ }, children: [cancel && (_jsx("button", { type: "button", onClick: handleCancel, disabled: disabled || readOnly, className: "mr-1 transition-opacity hover:opacity-80", style: {
87
+ color: emptyColor,
88
+ cursor: isInteractive ? "pointer" : "default"
89
+ }, title: t(i18n, "components.rating.cancel"), children: cancelIcon ?? _jsx(CancelIcon, { size: starSize }) })), Array.from({ length: stars }).map((_, index) => {
90
+ const fillPercentage = getStarFillPercentage(index);
91
+ const tooltipValue = showTooltip ? (hoverValue ?? internalValue) : null;
92
+ return (_jsxs("div", { className: "relative inline-block", style: { position: "relative" }, children: [showTooltip && hoverValue !== null && Math.floor(hoverValue - 0.5) === index && (_jsx("div", { className: "absolute -top-8 left-1/2 -translate-x-1/2 rounded bg-black/80 px-2 py-1 text-xs text-white", style: { whiteSpace: "nowrap", zIndex: 10 }, children: tooltipValue })), _jsxs("div", { className: "relative inline-flex", style: { width: starSize, height: starSize }, children: [_jsx("div", { style: { color: emptyColor, position: "absolute", top: 0, left: 0 }, children: offIcon ?? _jsx(StarIcon, { size: starSize, filled: false }) }), _jsx("div", { style: {
93
+ color: color,
94
+ position: "absolute",
95
+ top: 0,
96
+ left: 0,
97
+ clipPath: `inset(0 ${100 - fillPercentage}% 0 0)`,
98
+ transition: "clip-path 0.2s ease"
99
+ }, children: onIcon ?? _jsx(StarIcon, { size: starSize, filled: true }) }), allowHalf ? (_jsxs(_Fragment, { children: [_jsx("div", { style: {
100
+ position: "absolute",
101
+ top: 0,
102
+ left: 0,
103
+ width: "50%",
104
+ height: "100%",
105
+ cursor: isInteractive ? "pointer" : "default"
106
+ }, onClick: () => handleStarClick(index, true), onMouseEnter: () => handleStarHover(index, true) }), _jsx("div", { style: {
107
+ position: "absolute",
108
+ top: 0,
109
+ right: 0,
110
+ width: "50%",
111
+ height: "100%",
112
+ cursor: isInteractive ? "pointer" : "default"
113
+ }, onClick: () => handleStarClick(index, false), onMouseEnter: () => handleStarHover(index, false) })] })) : (_jsx("div", { style: {
114
+ position: "absolute",
115
+ top: 0,
116
+ left: 0,
117
+ width: "100%",
118
+ height: "100%",
119
+ cursor: isInteractive ? "pointer" : "default"
120
+ }, onClick: () => handleStarClick(index, false), onMouseEnter: () => handleStarHover(index, false) }))] })] }, index));
121
+ })] }), _jsx(ErrorText, { message: error ?? internalError ?? undefined })] }));
122
+ }
123
+ export function SgRating(props) {
124
+ const { control, name, register, ...rest } = props;
125
+ if (name && register) {
126
+ return _jsx(SgRatingBase, { ...rest });
127
+ }
128
+ if (control && name) {
129
+ return (_jsx(Controller, { name: name, control: control, render: ({ field, fieldState }) => (_jsx(SgRatingBase, { ...rest, value: field.value ?? 0, onChange: (value) => {
130
+ field.onChange(value);
131
+ rest.onChange?.(value);
132
+ }, error: rest.error ?? fieldState.error?.message })) }));
133
+ }
134
+ return _jsx(SgRatingBase, { ...rest });
135
+ }
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ export type SgSliderProps = {
3
+ id: string;
4
+ minValue: number;
5
+ maxValue: number;
6
+ value?: number;
7
+ defaultValue?: number;
8
+ step?: number;
9
+ disabled?: boolean;
10
+ onChange?: (value: number) => void;
11
+ ariaLabel?: string;
12
+ className?: string;
13
+ width?: number | string;
14
+ inputProps?: Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "id" | "min" | "max" | "step" | "value" | "defaultValue" | "onChange" | "disabled" | "aria-label">;
15
+ };
16
+ export declare function SgSlider(props: Readonly<SgSliderProps>): import("react/jsx-runtime").JSX.Element;
17
+ export declare namespace SgSlider {
18
+ var displayName: string;
19
+ }
20
+ //# sourceMappingURL=SgSlider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgSlider.d.ts","sourceRoot":"","sources":["../../src/inputs/SgSlider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAiB/B,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,IAAI,CACf,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EACzC,MAAM,GACN,IAAI,GACJ,KAAK,GACL,KAAK,GACL,MAAM,GACN,OAAO,GACP,cAAc,GACd,UAAU,GACV,UAAU,GACV,YAAY,CACf,CAAC;CACH,CAAC;AAEF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,2CA0DtD;yBA1De,QAAQ"}
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ function cn(...parts) {
5
+ return parts.filter(Boolean).join(" ");
6
+ }
7
+ function toCssSize(value) {
8
+ if (value === undefined || value === null)
9
+ return undefined;
10
+ if (typeof value === "number")
11
+ return `${value}px`;
12
+ const trimmed = value.trim();
13
+ return trimmed.length > 0 ? trimmed : undefined;
14
+ }
15
+ function clamp(value, min, max) {
16
+ return Math.max(min, Math.min(max, value));
17
+ }
18
+ export function SgSlider(props) {
19
+ const { id, minValue, maxValue, value, defaultValue, step = 1, disabled = false, onChange, ariaLabel, className, width, inputProps } = props;
20
+ const safeMin = Number.isFinite(minValue) ? minValue : 0;
21
+ const rawMax = Number.isFinite(maxValue) ? maxValue : safeMin;
22
+ const safeMax = Math.max(safeMin, rawMax);
23
+ const safeStep = Number.isFinite(step) && step > 0 ? step : 1;
24
+ const isControlled = value !== undefined;
25
+ const [internalValue, setInternalValue] = React.useState(() => clamp(defaultValue ?? safeMin, safeMin, safeMax));
26
+ React.useEffect(() => {
27
+ if (isControlled)
28
+ return;
29
+ setInternalValue((prev) => clamp(prev, safeMin, safeMax));
30
+ }, [isControlled, safeMin, safeMax]);
31
+ const currentValue = clamp(isControlled ? value : internalValue, safeMin, safeMax);
32
+ return (_jsx("input", { id: id, type: "range", min: safeMin, max: safeMax, step: safeStep, value: currentValue, disabled: disabled, "aria-label": ariaLabel, onChange: (event) => {
33
+ const nextRaw = Number(event.currentTarget.value);
34
+ const next = clamp(Number.isFinite(nextRaw) ? nextRaw : safeMin, safeMin, safeMax);
35
+ if (!isControlled)
36
+ setInternalValue(next);
37
+ onChange?.(next);
38
+ }, className: cn("h-5 w-full cursor-pointer", disabled ? "cursor-not-allowed opacity-60" : "", className), style: { width: toCssSize(width) }, ...inputProps }));
39
+ }
40
+ SgSlider.displayName = "SgSlider";
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ export type SgStepperInputProps = {
3
+ id: string;
4
+ minValue: number;
5
+ maxValue: number;
6
+ step?: number;
7
+ value?: number;
8
+ defaultValue?: number;
9
+ disabled?: boolean;
10
+ readOnly?: boolean;
11
+ onChange?: (value: number) => void;
12
+ ariaLabel?: string;
13
+ className?: string;
14
+ inputClassName?: string;
15
+ width?: number | string;
16
+ inputProps?: Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "id" | "min" | "max" | "step" | "value" | "defaultValue" | "disabled" | "readOnly" | "onChange" | "aria-label">;
17
+ };
18
+ export declare function SgStepperInput(props: Readonly<SgStepperInputProps>): import("react/jsx-runtime").JSX.Element;
19
+ export declare namespace SgStepperInput {
20
+ var displayName: string;
21
+ }
22
+ //# sourceMappingURL=SgStepperInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgStepperInput.d.ts","sourceRoot":"","sources":["../../src/inputs/SgStepperInput.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAiB/B,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,IAAI,CACf,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EACzC,MAAM,GACN,IAAI,GACJ,KAAK,GACL,KAAK,GACL,MAAM,GACN,OAAO,GACP,cAAc,GACd,UAAU,GACV,UAAU,GACV,UAAU,GACV,YAAY,CACf,CAAC;CACH,CAAC;AAEF,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,mBAAmB,CAAC,2CAiHlE;yBAjHe,cAAc"}
@@ -0,0 +1,51 @@
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 toCssSize(value) {
8
+ if (value === undefined || value === null)
9
+ return undefined;
10
+ if (typeof value === "number")
11
+ return `${value}px`;
12
+ const trimmed = value.trim();
13
+ return trimmed.length > 0 ? trimmed : undefined;
14
+ }
15
+ function clamp(value, min, max) {
16
+ return Math.max(min, Math.min(max, value));
17
+ }
18
+ export function SgStepperInput(props) {
19
+ const { id, minValue, maxValue, step = 1, value, defaultValue, disabled = false, readOnly = false, onChange, ariaLabel, className, inputClassName, width, inputProps } = props;
20
+ const safeMin = Number.isFinite(minValue) ? minValue : 0;
21
+ const rawMax = Number.isFinite(maxValue) ? maxValue : safeMin;
22
+ const safeMax = Math.max(safeMin, rawMax);
23
+ const safeStep = Number.isFinite(step) && step > 0 ? step : 1;
24
+ const isControlled = value !== undefined;
25
+ const [internalValue, setInternalValue] = React.useState(() => clamp(defaultValue ?? safeMin, safeMin, safeMax));
26
+ React.useEffect(() => {
27
+ if (isControlled)
28
+ return;
29
+ setInternalValue((prev) => clamp(prev, safeMin, safeMax));
30
+ }, [isControlled, safeMin, safeMax]);
31
+ const currentValue = clamp(isControlled ? value : internalValue, safeMin, safeMax);
32
+ const emitValue = React.useCallback((nextRaw) => {
33
+ const next = clamp(nextRaw, safeMin, safeMax);
34
+ if (!isControlled)
35
+ setInternalValue(next);
36
+ onChange?.(next);
37
+ }, [isControlled, onChange, safeMax, safeMin]);
38
+ const canDecrease = !disabled && !readOnly && currentValue > safeMin;
39
+ const canIncrease = !disabled && !readOnly && currentValue < safeMax;
40
+ return (_jsxs("div", { className: cn("inline-flex h-10 overflow-hidden rounded-md border border-border bg-background", disabled ? "opacity-60" : "", className), style: { width: toCssSize(width) }, children: [_jsx("input", { id: id, type: "number", min: safeMin, max: safeMax, step: safeStep, value: currentValue, disabled: disabled, readOnly: readOnly, "aria-label": ariaLabel, onChange: (event) => {
41
+ const inputNext = Number(event.currentTarget.value);
42
+ if (!Number.isFinite(inputNext))
43
+ return;
44
+ emitValue(inputNext);
45
+ }, className: cn("min-w-0 flex-1 border-0 bg-transparent px-3 text-sm outline-none", "focus:ring-0 [appearance:textfield]", "[&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none", inputClassName), ...inputProps }), _jsxs("div", { className: "flex w-7 flex-col border-l border-border", children: [_jsx("button", { type: "button", "aria-label": "Increase value", disabled: !canIncrease, onClick: () => emitValue(currentValue + safeStep), className: cn("inline-flex h-1/2 items-center justify-center text-foreground", "border-b border-border text-[10px]", canIncrease ? "hover:bg-muted" : "cursor-not-allowed opacity-40"), children: _jsx(StepArrow, { direction: "up" }) }), _jsx("button", { type: "button", "aria-label": "Decrease value", disabled: !canDecrease, onClick: () => emitValue(currentValue - safeStep), className: cn("inline-flex h-1/2 items-center justify-center text-foreground text-[10px]", canDecrease ? "hover:bg-muted" : "cursor-not-allowed opacity-40"), children: _jsx(StepArrow, { direction: "down" }) })] })] }));
46
+ }
47
+ SgStepperInput.displayName = "SgStepperInput";
48
+ function StepArrow(props) {
49
+ const rotate = props.direction === "up" ? 0 : 180;
50
+ return (_jsx("svg", { viewBox: "0 0 24 24", className: "size-3", style: { transform: `rotate(${rotate}deg)` }, "aria-hidden": "true", children: _jsx("path", { d: "M12 8l5 8H7z", fill: "currentColor" }) }));
51
+ }
@@ -16,6 +16,7 @@ export type SgTextEditorProps = {
16
16
  height?: number;
17
17
  placeholder?: string;
18
18
  disabled?: boolean;
19
+ borderRadius?: number | string;
19
20
  showCssEditor?: boolean;
20
21
  cssEditorLabel?: string;
21
22
  className?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"SgTextEditor.d.ts","sourceRoot":"","sources":["../../src/inputs/SgTextEditor.tsx"],"names":[],"mappings":"AAgBA,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAE1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC1D,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAE9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA2DF,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,iBAAiB,CAAC,2CAgY9D;yBAhYe,YAAY"}
1
+ {"version":3,"file":"SgTextEditor.d.ts","sourceRoot":"","sources":["../../src/inputs/SgTextEditor.tsx"],"names":[],"mappings":"AAgBA,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAE1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC1D,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAE9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAE/B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA2DF,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,iBAAiB,CAAC,2CAoZ9D;yBApZe,YAAY"}
@@ -68,7 +68,12 @@ function canRun(editor, fn) {
68
68
  }
69
69
  }
70
70
  export function SgTextEditor(props) {
71
- const { id, valueHtml, defaultValueHtml, onChangeHtml, cssText = "", onCssTextChange, fileName, onSave, onLoad, height = 320, placeholder = "Type here...", disabled, showCssEditor = false, cssEditorLabel = "Embedded CSS", className } = props;
71
+ const { id, valueHtml, defaultValueHtml, onChangeHtml, cssText = "", onCssTextChange, fileName, onSave, onLoad, height = 320, placeholder = "Type here...", disabled, borderRadius, showCssEditor = false, cssEditorLabel = "Embedded CSS", className } = props;
72
+ const resolvedBorderRadius = React.useMemo(() => {
73
+ if (borderRadius === undefined)
74
+ return undefined;
75
+ return typeof borderRadius === "number" ? `${borderRadius}px` : borderRadius;
76
+ }, [borderRadius]);
72
77
  const isControlled = typeof valueHtml === "string";
73
78
  const editor = useEditor({
74
79
  editable: !disabled,
@@ -136,7 +141,18 @@ export function SgTextEditor(props) {
136
141
  fn();
137
142
  };
138
143
  const active = (name, attrs) => !!editor?.isActive(name, attrs);
139
- return (_jsxs("div", { className: cn("w-full", className), children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 rounded-t-lg border border-b-0 bg-background p-2", children: [_jsxs("select", { className: "h-9 rounded-md border px-2 text-sm bg-background", disabled: !editor || disabled, value: active("heading", { level: 1 })
144
+ const toolbarStyle = resolvedBorderRadius
145
+ ? { borderTopLeftRadius: resolvedBorderRadius, borderTopRightRadius: resolvedBorderRadius }
146
+ : undefined;
147
+ const editorContainerStyle = { height };
148
+ if (resolvedBorderRadius) {
149
+ editorContainerStyle.borderBottomLeftRadius = resolvedBorderRadius;
150
+ editorContainerStyle.borderBottomRightRadius = resolvedBorderRadius;
151
+ }
152
+ const cssTextareaStyle = resolvedBorderRadius
153
+ ? { borderRadius: resolvedBorderRadius }
154
+ : undefined;
155
+ return (_jsxs("div", { className: cn("w-full", className), children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 rounded-t-lg border border-b-0 bg-background p-2", style: toolbarStyle, children: [_jsxs("select", { className: "h-9 rounded-md border px-2 text-sm bg-background", disabled: !editor || disabled, value: active("heading", { level: 1 })
140
156
  ? "h1"
141
157
  : active("heading", { level: 2 })
142
158
  ? "h2"
@@ -192,7 +208,7 @@ export function SgTextEditor(props) {
192
208
  return;
193
209
  void loadFromHtmlFile(f);
194
210
  e.currentTarget.value = "";
195
- } })] })] })] }), _jsx("div", { className: "rounded-b-lg border bg-background", style: { height }, children: _jsx("div", { className: "h-full overflow-auto p-3", children: _jsx(EditorContent, { editor: editor }) }) }), showCssEditor ? (_jsxs("div", { className: "mt-3", children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-foreground", children: cssEditorLabel }), _jsx("textarea", { value: cssText, onChange: (e) => onCssTextChange?.(e.target.value), className: "min-h-[160px] w-full rounded-lg border p-2 font-mono text-xs" }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "CSS is embedded inside the saved HTML document." })] })) : null] }));
211
+ } })] })] })] }), _jsx("div", { className: "rounded-b-lg border bg-background", style: editorContainerStyle, children: _jsx("div", { className: "h-full overflow-auto p-3", children: _jsx(EditorContent, { editor: editor }) }) }), showCssEditor ? (_jsxs("div", { className: "mt-3", children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-foreground", children: cssEditorLabel }), _jsx("textarea", { value: cssText, onChange: (e) => onCssTextChange?.(e.target.value), className: "min-h-[160px] w-full rounded-lg border p-2 font-mono text-xs", style: cssTextareaStyle }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "CSS is embedded inside the saved HTML document." })] })) : null] }));
196
212
  }
197
213
  function ToolbarButton(props) {
198
214
  const { label, text, active, disabled, onClick } = props;
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+ import type { FieldValues, RegisterOptions, UseFormRegister } from "react-hook-form";
3
+ import type { RhfFieldProps } from "../rhf";
4
+ export type SgToggleSwitchProps = {
5
+ id: string;
6
+ label?: string;
7
+ description?: string;
8
+ error?: string;
9
+ className?: string;
10
+ labelClassName?: string;
11
+ switchClassName?: string;
12
+ trackClassName?: string;
13
+ thumbClassName?: string;
14
+ onIcon?: React.ReactNode;
15
+ offIcon?: React.ReactNode;
16
+ checked?: boolean;
17
+ defaultChecked?: boolean;
18
+ width?: number | string;
19
+ enabled?: boolean;
20
+ readOnly?: boolean;
21
+ required?: boolean;
22
+ requiredMessage?: string;
23
+ validateOnBlur?: boolean;
24
+ validation?: (checked: boolean) => string | null;
25
+ onValidation?: (message: string | null) => void;
26
+ onChange?: (checked: boolean) => void;
27
+ inputProps?: React.InputHTMLAttributes<HTMLInputElement> & {
28
+ ref?: React.Ref<HTMLInputElement>;
29
+ };
30
+ register?: UseFormRegister<FieldValues>;
31
+ rules?: RegisterOptions<FieldValues>;
32
+ } & RhfFieldProps;
33
+ export declare function SgToggleSwitch(props: Readonly<SgToggleSwitchProps>): import("react/jsx-runtime").JSX.Element;
34
+ export declare const SgSwitch: typeof SgToggleSwitch;
35
+ export type SgSwitchProps = SgToggleSwitchProps;
36
+ //# sourceMappingURL=SgToggleSwitch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgToggleSwitch.d.ts","sourceRoot":"","sources":["../../src/inputs/SgToggleSwitch.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;AAuC5C,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,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,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;IACjD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,GAAG;QACzD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;KACnC,CAAC;IACF,QAAQ,CAAC,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC;IACxC,KAAK,CAAC,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC;CACtC,GAAG,aAAa,CAAC;AAsNlB,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,mBAAmB,CAAC,2CAmDlE;AAED,eAAO,MAAM,QAAQ,uBAAiB,CAAC;AACvC,MAAM,MAAM,aAAa,GAAG,mBAAmB,CAAC"}
@@ -0,0 +1,174 @@
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 { t, useComponentsI18n } from "../i18n";
6
+ function cn(...parts) {
7
+ return parts.filter(Boolean).join(" ");
8
+ }
9
+ function toBoolean(value, fallback = false) {
10
+ if (typeof value === "boolean")
11
+ return value;
12
+ if (typeof value === "number")
13
+ return value !== 0;
14
+ if (typeof value === "string") {
15
+ const normalized = value.trim().toLowerCase();
16
+ return normalized === "true" || normalized === "1" || normalized === "on" || normalized === "yes";
17
+ }
18
+ return fallback;
19
+ }
20
+ function mergeRefs(...refs) {
21
+ return (node) => {
22
+ for (const ref of refs) {
23
+ if (!ref)
24
+ continue;
25
+ if (typeof ref === "function") {
26
+ ref(node);
27
+ }
28
+ else if (typeof ref === "object" && "current" in ref) {
29
+ ref.current = node;
30
+ }
31
+ }
32
+ };
33
+ }
34
+ function ErrorText(props) {
35
+ if (!props.message)
36
+ return null;
37
+ return (_jsx("p", { id: props.id, "data-sg-error": true, className: "text-xs text-red-600", children: props.message }));
38
+ }
39
+ function mergeInputPropsWithField(inputProps, field) {
40
+ return {
41
+ ...inputProps,
42
+ checked: toBoolean(field.value, false),
43
+ onChange: (event) => {
44
+ field.onChange(event.currentTarget.checked);
45
+ inputProps?.onChange?.(event);
46
+ },
47
+ onBlur: (event) => {
48
+ field.onBlur();
49
+ inputProps?.onBlur?.(event);
50
+ },
51
+ ref: (node) => {
52
+ field.ref(node);
53
+ const ref = inputProps?.ref;
54
+ if (!ref)
55
+ return;
56
+ if (typeof ref === "function") {
57
+ ref(node);
58
+ }
59
+ else if (ref && typeof ref === "object" && "current" in ref) {
60
+ ref.current = node;
61
+ }
62
+ }
63
+ };
64
+ }
65
+ function SgToggleSwitchBase(props) {
66
+ const i18n = useComponentsI18n();
67
+ const inputProps = props.inputProps ?? {};
68
+ const { checked: inputChecked, defaultChecked: inputDefaultChecked, onChange: inputOnChange, onBlur: inputOnBlur, onClick: inputOnClick, disabled: inputDisabled, readOnly: inputReadOnly, className: inputClassName, ref: inputRef, style: inputStyle, ...restInputProps } = inputProps;
69
+ const isControlled = props.checked !== undefined || inputChecked !== undefined;
70
+ const [internalChecked, setInternalChecked] = React.useState(() => toBoolean(props.defaultChecked ?? inputDefaultChecked, false));
71
+ const [internalError, setInternalError] = React.useState(null);
72
+ const [hasInteracted, setHasInteracted] = React.useState(false);
73
+ const inputNodeRef = React.useRef(null);
74
+ const errorId = `${props.id}-error`;
75
+ const checked = isControlled ? toBoolean(props.checked ?? inputChecked, false) : internalChecked;
76
+ const isReadOnly = Boolean(props.readOnly ?? inputReadOnly);
77
+ const isDisabled = props.enabled === false || Boolean(inputDisabled);
78
+ const setRefs = React.useMemo(() => mergeRefs((node) => {
79
+ inputNodeRef.current = node;
80
+ }, inputRef), [inputRef]);
81
+ React.useEffect(() => {
82
+ if (isControlled)
83
+ return;
84
+ const domChecked = inputNodeRef.current?.checked ?? false;
85
+ setInternalChecked((prev) => (prev === domChecked ? prev : domChecked));
86
+ }, [isControlled]);
87
+ const runValidation = React.useCallback((nextChecked) => {
88
+ if ((props.required ?? false) && !nextChecked) {
89
+ const message = props.requiredMessage ?? t(i18n, "components.inputs.required");
90
+ setInternalError(message);
91
+ props.onValidation?.(message);
92
+ return;
93
+ }
94
+ if (props.validation) {
95
+ const message = props.validation(nextChecked);
96
+ setInternalError(message);
97
+ props.onValidation?.(message ?? null);
98
+ return;
99
+ }
100
+ setInternalError(null);
101
+ props.onValidation?.(null);
102
+ }, [i18n, props]);
103
+ const handleChange = (event) => {
104
+ if (isReadOnly) {
105
+ event.preventDefault();
106
+ event.currentTarget.checked = checked;
107
+ return;
108
+ }
109
+ const nextChecked = event.currentTarget.checked;
110
+ if (!isControlled)
111
+ setInternalChecked(nextChecked);
112
+ setHasInteracted(true);
113
+ if (props.validateOnBlur === false) {
114
+ runValidation(nextChecked);
115
+ }
116
+ inputOnChange?.(event);
117
+ props.onChange?.(nextChecked);
118
+ };
119
+ const handleBlur = (event) => {
120
+ if ((props.validateOnBlur ?? true) || hasInteracted) {
121
+ runValidation(event.currentTarget.checked);
122
+ }
123
+ inputOnBlur?.(event);
124
+ };
125
+ const handleClick = (event) => {
126
+ if (isReadOnly) {
127
+ event.preventDefault();
128
+ return;
129
+ }
130
+ inputOnClick?.(event);
131
+ };
132
+ const hasError = Boolean(props.error ?? internalError);
133
+ const resolvedBorderRadius = 9999;
134
+ const switchWidth = props.width
135
+ ? typeof props.width === "number"
136
+ ? `${props.width}px`
137
+ : props.width
138
+ : undefined;
139
+ return (_jsxs("div", { className: cn("space-y-1", props.className), style: switchWidth ? { width: switchWidth } : undefined, children: [_jsxs("label", { htmlFor: props.id, className: cn("inline-flex items-start gap-3 select-none", isDisabled
140
+ ? "cursor-not-allowed opacity-70"
141
+ : isReadOnly
142
+ ? "cursor-default"
143
+ : "cursor-pointer"), children: [_jsx("input", { id: props.id, type: "checkbox", role: "switch", "aria-invalid": hasError, "aria-describedby": hasError ? errorId : undefined, "aria-label": props.label ? undefined : props.id, checked: isControlled ? checked : undefined, defaultChecked: isControlled ? undefined : toBoolean(props.defaultChecked ?? inputDefaultChecked, false), disabled: isDisabled, readOnly: isReadOnly, ...restInputProps, className: cn("peer sr-only", inputClassName), style: inputStyle, ref: setRefs, onChange: handleChange, onBlur: handleBlur, onClick: handleClick }), _jsxs("span", { className: cn("relative inline-flex h-6 w-11 items-center", props.switchClassName), children: [_jsx("span", { "aria-hidden": "true", className: cn("absolute inset-0 rounded-full border transition-colors duration-200", checked
144
+ ? hasError
145
+ ? "border-[hsl(var(--destructive))] bg-[hsl(var(--destructive)/0.2)]"
146
+ : "border-[hsl(var(--primary))] bg-[hsl(var(--primary))]"
147
+ : hasError
148
+ ? "border-[hsl(var(--destructive))] bg-[hsl(var(--destructive)/0.05)]"
149
+ : "border-border bg-muted", "peer-focus-visible:ring-2 peer-focus-visible:ring-[hsl(var(--primary)/0.25)]", hasError ? "peer-focus-visible:ring-[hsl(var(--destructive)/0.25)]" : undefined, props.trackClassName), style: { borderRadius: resolvedBorderRadius } }), _jsx("span", { "aria-hidden": "true", className: cn("pointer-events-none relative z-10 inline-flex size-5 items-center justify-center rounded-full bg-white text-[11px] text-foreground/70 shadow-sm transition-transform duration-200", checked ? "translate-x-5" : "translate-x-0", props.thumbClassName), children: checked ? props.onIcon ?? null : props.offIcon ?? null })] }), props.label ? (_jsxs("span", { className: cn("pt-0.5 text-sm text-foreground", props.labelClassName), children: [props.label, props.required ? _jsx("span", { className: "ml-1 text-[hsl(var(--destructive))]", children: "*" }) : null, props.description ? (_jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: props.description })) : null] })) : null] }), _jsx(ErrorText, { id: errorId, message: props.error ?? internalError ?? undefined })] }));
150
+ }
151
+ export function SgToggleSwitch(props) {
152
+ const { control, name, register, rules, ...rest } = props;
153
+ if (name && register) {
154
+ const reg = register(name, rules);
155
+ return (_jsx(SgToggleSwitchBase, { ...rest, inputProps: {
156
+ ...rest.inputProps,
157
+ name,
158
+ onChange: (event) => {
159
+ reg.onChange(event);
160
+ rest.inputProps?.onChange?.(event);
161
+ },
162
+ onBlur: (event) => {
163
+ reg.onBlur(event);
164
+ rest.inputProps?.onBlur?.(event);
165
+ },
166
+ ref: mergeRefs(reg.ref, rest.inputProps?.ref)
167
+ } }));
168
+ }
169
+ if (control && name) {
170
+ return (_jsx(Controller, { name: name, control: control, render: ({ field, fieldState }) => (_jsx(SgToggleSwitchBase, { ...rest, error: rest.error ?? fieldState.error?.message, inputProps: mergeInputPropsWithField(rest.inputProps, field) })) }));
171
+ }
172
+ return _jsx(SgToggleSwitchBase, { ...rest });
173
+ }
174
+ export const SgSwitch = SgToggleSwitch;
@@ -0,0 +1,39 @@
1
+ import * as React from "react";
2
+ export type SgAccordionOrientation = "vertical" | "horizontal";
3
+ export type SgAccordionItem = {
4
+ id?: string | number;
5
+ title: React.ReactNode;
6
+ content: React.ReactNode;
7
+ disabled?: boolean;
8
+ icon?: React.ReactNode;
9
+ end?: React.ReactNode;
10
+ className?: string;
11
+ headerClassName?: string;
12
+ headerBackgroundColor?: string;
13
+ contentClassName?: string;
14
+ };
15
+ export type SgAccordionProps = Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> & {
16
+ id?: string;
17
+ items: SgAccordionItem[];
18
+ orientation?: SgAccordionOrientation;
19
+ multiple?: boolean;
20
+ collapsible?: boolean;
21
+ activeIndex?: number | number[];
22
+ defaultActiveIndex?: number | number[];
23
+ defaultOpenFirst?: boolean;
24
+ onActiveIndexChange?: (indexes: number[]) => void;
25
+ onItemToggle?: (index: number, isOpen: boolean) => void;
26
+ panelClassName?: string;
27
+ headerClassName?: string;
28
+ headerBackgroundColor?: string;
29
+ contentClassName?: string;
30
+ animationDuration?: number;
31
+ horizontalHeaderWidth?: number | string;
32
+ horizontalMinHeight?: number | string;
33
+ keepMounted?: boolean;
34
+ };
35
+ export declare function SgAccordion(props: Readonly<SgAccordionProps>): import("react/jsx-runtime").JSX.Element;
36
+ export declare namespace SgAccordion {
37
+ var displayName: string;
38
+ }
39
+ //# sourceMappingURL=SgAccordion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgAccordion.d.ts","sourceRoot":"","sources":["../../src/layout/SgAccordion.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAsD/B,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,YAAY,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,GAAG;IACtF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAChC,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAClD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qBAAqB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAKF,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,2CAgN5D;yBAhNe,WAAW"}