@marcoschwartz/lite-ui 0.27.3 → 0.27.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3757,17 +3757,64 @@ var SegmentedControl = ({
3757
3757
  }) => {
3758
3758
  const groupId = (0, import_react20.useId)();
3759
3759
  const groupName = name || `segmented-control-${groupId}`;
3760
+ const containerRef = (0, import_react20.useRef)(null);
3761
+ const indicatorRef = (0, import_react20.useRef)(null);
3760
3762
  const options = (0, import_react20.useMemo)(() => normalizeData(data), [data]);
3761
3763
  const [internalValue, setInternalValue] = (0, import_react20.useState)(
3762
3764
  defaultValue || options[0]?.value || ""
3763
3765
  );
3764
3766
  const isControlled = controlledValue !== void 0;
3765
3767
  const currentValue = isControlled ? controlledValue : internalValue;
3766
- const [hasInteracted, setHasInteracted] = (0, import_react20.useState)(false);
3768
+ const [initialized, setInitialized] = (0, import_react20.useState)(false);
3767
3769
  const activeIndex = options.findIndex((opt) => opt.value === currentValue);
3770
+ const updateIndicator = (0, import_react20.useCallback)((animate = true) => {
3771
+ if (!containerRef.current || !indicatorRef.current) return;
3772
+ const container = containerRef.current;
3773
+ const indicator = indicatorRef.current;
3774
+ if (activeIndex === -1) {
3775
+ indicator.style.display = "none";
3776
+ return;
3777
+ }
3778
+ const buttons = container.querySelectorAll("[data-segment-button]");
3779
+ const activeButton = buttons[activeIndex];
3780
+ if (!activeButton) return;
3781
+ const containerRect = container.getBoundingClientRect();
3782
+ const buttonRect = activeButton.getBoundingClientRect();
3783
+ if (!animate) {
3784
+ indicator.style.transitionDuration = "0ms";
3785
+ } else {
3786
+ indicator.style.transitionDuration = `${transitionDuration}ms`;
3787
+ }
3788
+ indicator.style.display = "block";
3789
+ if (orientation === "horizontal") {
3790
+ indicator.style.width = `${buttonRect.width}px`;
3791
+ indicator.style.height = `calc(100% - 8px)`;
3792
+ indicator.style.transform = `translateX(${buttonRect.left - containerRect.left - 4}px)`;
3793
+ } else {
3794
+ indicator.style.width = `calc(100% - 8px)`;
3795
+ indicator.style.height = `${buttonRect.height}px`;
3796
+ indicator.style.transform = `translateY(${buttonRect.top - containerRect.top - 4}px)`;
3797
+ }
3798
+ }, [activeIndex, orientation, transitionDuration]);
3799
+ (0, import_react20.useEffect)(() => {
3800
+ const timer = setTimeout(() => {
3801
+ updateIndicator(false);
3802
+ setInitialized(true);
3803
+ }, 20);
3804
+ return () => clearTimeout(timer);
3805
+ }, []);
3806
+ (0, import_react20.useEffect)(() => {
3807
+ if (initialized) {
3808
+ updateIndicator(true);
3809
+ }
3810
+ }, [currentValue, initialized, updateIndicator]);
3811
+ (0, import_react20.useEffect)(() => {
3812
+ const handleResize = () => updateIndicator(false);
3813
+ window.addEventListener("resize", handleResize);
3814
+ return () => window.removeEventListener("resize", handleResize);
3815
+ }, [updateIndicator]);
3768
3816
  const handleChange = (0, import_react20.useCallback)((newValue) => {
3769
3817
  if (disabled) return;
3770
- setHasInteracted(true);
3771
3818
  if (!isControlled) {
3772
3819
  setInternalValue(newValue);
3773
3820
  }
@@ -3803,20 +3850,6 @@ var SegmentedControl = ({
3803
3850
  }, [options, currentValue, orientation, handleChange]);
3804
3851
  const sizeStyles2 = sizeConfig[size];
3805
3852
  const radiusStyle = radiusConfig[radius];
3806
- const totalOptions = options.length;
3807
- const indicatorStyle = activeIndex >= 0 ? orientation === "horizontal" ? {
3808
- width: `calc(${100 / totalOptions}% - ${totalOptions > 1 ? 0 : 0}px)`,
3809
- height: "calc(100% - 8px)",
3810
- left: `calc(${activeIndex / totalOptions * 100}% + 4px)`,
3811
- top: "4px",
3812
- transition: hasInteracted ? `left ${transitionDuration}ms ease, width ${transitionDuration}ms ease` : "none"
3813
- } : {
3814
- width: "calc(100% - 8px)",
3815
- height: `calc(${100 / totalOptions}% - ${totalOptions > 1 ? 0 : 0}px)`,
3816
- left: "4px",
3817
- top: `calc(${activeIndex / totalOptions * 100}% + 4px)`,
3818
- transition: hasInteracted ? `top ${transitionDuration}ms ease, height ${transitionDuration}ms ease` : "none"
3819
- } : { display: "none" };
3820
3853
  const containerClasses = [
3821
3854
  "relative inline-flex p-1",
3822
3855
  "bg-[hsl(var(--muted))]",
@@ -3829,6 +3862,7 @@ var SegmentedControl = ({
3829
3862
  return /* @__PURE__ */ (0, import_jsx_runtime99.jsxs)(
3830
3863
  "div",
3831
3864
  {
3865
+ ref: containerRef,
3832
3866
  role: "radiogroup",
3833
3867
  "aria-label": ariaLabel || "Segmented control",
3834
3868
  className: containerClasses,
@@ -3836,17 +3870,23 @@ var SegmentedControl = ({
3836
3870
  /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(
3837
3871
  "div",
3838
3872
  {
3873
+ ref: indicatorRef,
3839
3874
  className: [
3840
- "absolute z-0",
3875
+ "absolute z-0 top-1 left-1",
3841
3876
  radiusStyle,
3842
3877
  colorConfig[color],
3843
- "shadow-sm"
3878
+ "shadow-sm",
3879
+ "transition-transform",
3880
+ initialized ? "opacity-100" : "opacity-0"
3844
3881
  ].join(" "),
3845
- style: indicatorStyle,
3882
+ style: {
3883
+ transitionProperty: "transform, width, height, opacity",
3884
+ transitionTimingFunction: "ease"
3885
+ },
3846
3886
  "aria-hidden": "true"
3847
3887
  }
3848
3888
  ),
3849
- options.map((option, index) => {
3889
+ options.map((option) => {
3850
3890
  const isActive = option.value === currentValue;
3851
3891
  const isDisabled = disabled || option.disabled;
3852
3892
  const buttonClasses = [