@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.
- package/dist/buttons/SgFloatActionButton.d.ts.map +1 -1
- package/dist/buttons/SgFloatActionButton.js +168 -38
- package/dist/commons/SgAvatar.d.ts +66 -0
- package/dist/commons/SgAvatar.d.ts.map +1 -0
- package/dist/commons/SgAvatar.js +136 -0
- package/dist/commons/SgSkeleton.d.ts +16 -0
- package/dist/commons/SgSkeleton.d.ts.map +1 -0
- package/dist/commons/SgSkeleton.js +58 -0
- package/dist/commons/SgToaster.d.ts +9 -0
- package/dist/commons/SgToaster.d.ts.map +1 -1
- package/dist/commons/SgToaster.js +86 -17
- package/dist/digits/discard-digit/SgDiscardDigit.d.ts +39 -0
- package/dist/digits/discard-digit/SgDiscardDigit.d.ts.map +1 -0
- package/dist/digits/discard-digit/SgDiscardDigit.js +303 -0
- package/dist/digits/discard-digit/index.d.ts +3 -0
- package/dist/digits/discard-digit/index.d.ts.map +1 -0
- package/dist/digits/discard-digit/index.js +1 -0
- package/dist/digits/fade-digit/SgFadeDigit.d.ts +27 -0
- package/dist/digits/fade-digit/SgFadeDigit.d.ts.map +1 -0
- package/dist/digits/fade-digit/SgFadeDigit.js +85 -0
- package/dist/digits/fade-digit/index.d.ts +3 -0
- package/dist/digits/fade-digit/index.d.ts.map +1 -0
- package/dist/digits/fade-digit/index.js +1 -0
- package/dist/digits/flip-digit/SgFlipDigit.d.ts +27 -0
- package/dist/digits/flip-digit/SgFlipDigit.d.ts.map +1 -0
- package/dist/digits/flip-digit/SgFlipDigit.js +70 -0
- package/dist/digits/flip-digit/index.d.ts.map +1 -0
- package/dist/digits/matrix-digit/SgMatrixDigit.d.ts +32 -0
- package/dist/digits/matrix-digit/SgMatrixDigit.d.ts.map +1 -0
- package/dist/digits/matrix-digit/SgMatrixDigit.js +86 -0
- package/dist/digits/matrix-digit/index.d.ts +3 -0
- package/dist/digits/matrix-digit/index.d.ts.map +1 -0
- package/dist/digits/matrix-digit/index.js +1 -0
- package/dist/digits/neon-digit/SgNeonDigit.d.ts +37 -0
- package/dist/digits/neon-digit/SgNeonDigit.d.ts.map +1 -0
- package/dist/digits/neon-digit/SgNeonDigit.js +59 -0
- package/dist/digits/neon-digit/index.d.ts +3 -0
- package/dist/digits/neon-digit/index.d.ts.map +1 -0
- package/dist/digits/neon-digit/index.js +1 -0
- package/dist/digits/roller3d-digit/SgRoller3DDigit.d.ts +37 -0
- package/dist/digits/roller3d-digit/SgRoller3DDigit.d.ts.map +1 -0
- package/dist/digits/roller3d-digit/SgRoller3DDigit.js +47 -0
- package/dist/digits/roller3d-digit/index.d.ts +3 -0
- package/dist/digits/roller3d-digit/index.d.ts.map +1 -0
- package/dist/digits/roller3d-digit/index.js +1 -0
- package/dist/environment/SgEnvironmentProvider.d.ts +1 -0
- package/dist/environment/SgEnvironmentProvider.d.ts.map +1 -1
- package/dist/environment/SgEnvironmentProvider.js +51 -12
- package/dist/gadgets/clock/SgClock.d.ts +3 -1
- package/dist/gadgets/clock/SgClock.d.ts.map +1 -1
- package/dist/gadgets/clock/SgClock.js +111 -180
- package/dist/gadgets/clock/SgTimeProvider.d.ts +1 -0
- package/dist/gadgets/clock/SgTimeProvider.d.ts.map +1 -1
- package/dist/gadgets/clock/SgTimeProvider.js +11 -4
- package/dist/gadgets/gauge/SgLinearGauge.d.ts +59 -0
- package/dist/gadgets/gauge/SgLinearGauge.d.ts.map +1 -0
- package/dist/gadgets/gauge/SgLinearGauge.js +258 -0
- package/dist/gadgets/gauge/SgRadialGauge.d.ts +73 -0
- package/dist/gadgets/gauge/SgRadialGauge.d.ts.map +1 -0
- package/dist/gadgets/gauge/SgRadialGauge.js +311 -0
- package/dist/gadgets/gauge/index.d.ts +5 -0
- package/dist/gadgets/gauge/index.d.ts.map +1 -0
- package/dist/gadgets/gauge/index.js +2 -0
- package/dist/gadgets/qr-code/SgQRCode.d.ts +25 -0
- package/dist/gadgets/qr-code/SgQRCode.d.ts.map +1 -0
- package/dist/gadgets/qr-code/SgQRCode.js +75 -0
- package/dist/gadgets/qr-code/index.d.ts +3 -0
- package/dist/gadgets/qr-code/index.d.ts.map +1 -0
- package/dist/gadgets/qr-code/index.js +1 -0
- package/dist/gadgets/string-animator/SgStringAnimator.d.ts +91 -0
- package/dist/gadgets/string-animator/SgStringAnimator.d.ts.map +1 -0
- package/dist/gadgets/string-animator/SgStringAnimator.js +145 -0
- package/dist/gadgets/string-animator/index.d.ts +3 -0
- package/dist/gadgets/string-animator/index.d.ts.map +1 -0
- package/dist/gadgets/string-animator/index.js +1 -0
- package/dist/i18n/en-US.json +9 -1
- package/dist/i18n/es.json +55 -47
- package/dist/i18n/index.d.ts +32 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/pt-BR.json +9 -1
- package/dist/i18n/pt-PT.json +9 -1
- package/dist/index.d.ts +53 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -1
- package/dist/inputs/SgAutocomplete.js +21 -5
- package/dist/inputs/SgCombobox.d.ts +26 -0
- package/dist/inputs/SgCombobox.d.ts.map +1 -0
- package/dist/inputs/SgCombobox.js +354 -0
- package/dist/inputs/SgInputOTP.d.ts.map +1 -1
- package/dist/inputs/SgInputOTP.js +9 -2
- package/dist/inputs/SgRadioGroup.d.ts +37 -0
- package/dist/inputs/SgRadioGroup.d.ts.map +1 -0
- package/dist/inputs/SgRadioGroup.js +139 -0
- package/dist/inputs/SgRating.d.ts +55 -0
- package/dist/inputs/SgRating.d.ts.map +1 -0
- package/dist/inputs/SgRating.js +135 -0
- package/dist/inputs/SgSlider.d.ts +20 -0
- package/dist/inputs/SgSlider.d.ts.map +1 -0
- package/dist/inputs/SgSlider.js +40 -0
- package/dist/inputs/SgStepperInput.d.ts +22 -0
- package/dist/inputs/SgStepperInput.d.ts.map +1 -0
- package/dist/inputs/SgStepperInput.js +51 -0
- package/dist/inputs/SgTextEditor.d.ts +1 -0
- package/dist/inputs/SgTextEditor.d.ts.map +1 -1
- package/dist/inputs/SgTextEditor.js +19 -3
- package/dist/inputs/SgToggleSwitch.d.ts +36 -0
- package/dist/inputs/SgToggleSwitch.d.ts.map +1 -0
- package/dist/inputs/SgToggleSwitch.js +174 -0
- package/dist/layout/SgAccordion.d.ts +39 -0
- package/dist/layout/SgAccordion.d.ts.map +1 -0
- package/dist/layout/SgAccordion.js +116 -0
- package/dist/layout/SgBreadcrumb.d.ts +33 -0
- package/dist/layout/SgBreadcrumb.d.ts.map +1 -0
- package/dist/layout/SgBreadcrumb.js +121 -0
- package/dist/layout/SgCarousel.d.ts +43 -0
- package/dist/layout/SgCarousel.d.ts.map +1 -0
- package/dist/layout/SgCarousel.js +166 -0
- package/dist/layout/SgDockLayout.d.ts +14 -0
- package/dist/layout/SgDockLayout.d.ts.map +1 -1
- package/dist/layout/SgDockLayout.js +145 -13
- package/dist/layout/SgDockScreen.d.ts +15 -0
- package/dist/layout/SgDockScreen.d.ts.map +1 -0
- package/dist/layout/SgDockScreen.js +13 -0
- package/dist/layout/SgDockZone.d.ts.map +1 -1
- package/dist/layout/SgDockZone.js +36 -2
- package/dist/layout/SgExpandablePanel.d.ts +50 -0
- package/dist/layout/SgExpandablePanel.d.ts.map +1 -0
- package/dist/layout/SgExpandablePanel.js +302 -0
- package/dist/layout/SgMainPanel.d.ts.map +1 -1
- package/dist/layout/SgMainPanel.js +36 -14
- package/dist/layout/SgMenu.d.ts +91 -0
- package/dist/layout/SgMenu.d.ts.map +1 -0
- package/dist/layout/SgMenu.js +939 -0
- package/dist/layout/SgPageControl.d.ts +49 -0
- package/dist/layout/SgPageControl.d.ts.map +1 -0
- package/dist/layout/SgPageControl.js +152 -0
- package/dist/layout/SgPanel.d.ts.map +1 -1
- package/dist/layout/SgPanel.js +10 -1
- package/dist/layout/SgScreen.d.ts +2 -0
- package/dist/layout/SgScreen.d.ts.map +1 -1
- package/dist/layout/SgScreen.js +4 -2
- package/dist/layout/SgToolBar.d.ts +9 -3
- package/dist/layout/SgToolBar.d.ts.map +1 -1
- package/dist/layout/SgToolBar.js +461 -55
- package/dist/menus/SgDockMenu.d.ts +62 -0
- package/dist/menus/SgDockMenu.d.ts.map +1 -0
- package/dist/menus/SgDockMenu.js +480 -0
- package/dist/others/SgPlayground.js +73 -73
- package/package.json +72 -57
- package/dist/gadgets/flip-digit/SgFlipDigit.d.ts +0 -23
- package/dist/gadgets/flip-digit/SgFlipDigit.d.ts.map +0 -1
- package/dist/gadgets/flip-digit/SgFlipDigit.js +0 -118
- package/dist/gadgets/flip-digit/index.d.ts.map +0 -1
- /package/dist/{gadgets → digits}/flip-digit/index.d.ts +0 -0
- /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
|
+
}
|
|
@@ -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;
|
|
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
|
-
|
|
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:
|
|
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"}
|