From f64ad95a203b58b174e6b5dc5ae3ac739918c8b7 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Sat, 1 Mar 2025 15:25:28 -0700 Subject: [PATCH] update segments --- .../_LayoutHeaderAndSegment/UserSegments.tsx | 6 +- .../features/modal/AddTypeModal.tsx | 2 +- .../components/ui/segmented/AppSegmented.tsx | 72 ++++++++++++------- .../MetricStylingAppSegment.stories.tsx | 16 +++-- 4 files changed, 58 insertions(+), 38 deletions(-) diff --git a/web/src/app/app/(settings_layout)/settings/(permissions)/users/[userId]/_LayoutHeaderAndSegment/UserSegments.tsx b/web/src/app/app/(settings_layout)/settings/(permissions)/users/[userId]/_LayoutHeaderAndSegment/UserSegments.tsx index 4129286b6..454acff9f 100644 --- a/web/src/app/app/(settings_layout)/settings/(permissions)/users/[userId]/_LayoutHeaderAndSegment/UserSegments.tsx +++ b/web/src/app/app/(settings_layout)/settings/(permissions)/users/[userId]/_LayoutHeaderAndSegment/UserSegments.tsx @@ -30,10 +30,10 @@ export const UserSegments: React.FC<{ onSelectApp: (app: UserSegmentsApps) => void; userId: string; }> = React.memo(({ isAdmin, selectedApp, onSelectApp, userId }) => { - const onChange = useMemoizedFn((value: SegmentedItem) => { - onSelectApp(value as UserSegmentsApps); + const onChange = useMemoizedFn((value: SegmentedItem) => { + onSelectApp(value.value); }); - const options = useMemo( + const options: SegmentedItem[] = useMemo( () => [ { diff --git a/web/src/components/features/modal/AddTypeModal.tsx b/web/src/components/features/modal/AddTypeModal.tsx index 778f6f988..e56a7a4dd 100644 --- a/web/src/components/features/modal/AddTypeModal.tsx +++ b/web/src/components/features/modal/AddTypeModal.tsx @@ -351,7 +351,7 @@ const ModalContent: React.FC<{ onClose }) => { const onSetSelectedFiltersPreflight = useMemoizedFn((value: SegmentedItem) => { - onSetSelectedFilter(value as string); + onSetSelectedFilter(value.value as string); }); return ( diff --git a/web/src/components/ui/segmented/AppSegmented.tsx b/web/src/components/ui/segmented/AppSegmented.tsx index 221fd78c6..1f4ff1548 100644 --- a/web/src/components/ui/segmented/AppSegmented.tsx +++ b/web/src/components/ui/segmented/AppSegmented.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import * as Tabs from '@radix-ui/react-tabs'; import { motion } from 'framer-motion'; import { cn } from '@/lib/classMerge'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useLayoutEffect } from 'react'; import { cva } from 'class-variance-authority'; import { useMemoizedFn } from 'ahooks'; @@ -102,6 +102,7 @@ export const AppSegmented: AppSegmentedComponent = React.forwardRef( width: 0, transform: 'translateX(0)' }); + const [isMeasured, setIsMeasured] = useState(false); const height = size === 'default' ? 'h-[28px]' : 'h-[50px]'; @@ -109,17 +110,31 @@ export const AppSegmented: AppSegmentedComponent = React.forwardRef( if (value !== undefined && value !== selectedValue) { setSelectedValue(value); } - }, [value]); + }, [value, selectedValue]); - useEffect(() => { - const selectedTab = tabRefs.current.get(selectedValue); - if (selectedTab) { - const { offsetWidth, offsetLeft } = selectedTab; - setGliderStyle({ - width: offsetWidth, - transform: `translateX(${offsetLeft}px)` - }); - } + // Use useLayoutEffect to measure before paint + useLayoutEffect(() => { + const updateGliderStyle = () => { + const selectedTab = tabRefs.current.get(selectedValue); + if (selectedTab) { + const { offsetWidth, offsetLeft } = selectedTab; + if (offsetWidth > 0) { + setGliderStyle({ + width: offsetWidth, + transform: `translateX(${offsetLeft}px)` + }); + setIsMeasured(true); + } + } + }; + + // Run immediately + updateGliderStyle(); + + // Also run after a short delay to ensure DOM is fully rendered + const timeoutId = setTimeout(updateGliderStyle, 25); + + return () => clearTimeout(timeoutId); }, [selectedValue]); const handleTabClick = useMemoizedFn((value: string) => { @@ -136,19 +151,24 @@ export const AppSegmented: AppSegmentedComponent = React.forwardRef( value={selectedValue} onValueChange={handleTabClick} className={cn(segmentedVariants({ block, type }), height, className)}> - + {isMeasured && ( + + )} @@ -178,9 +198,7 @@ interface SegmentedTriggerProps { tabRefs: React.MutableRefObject>; } -function SegmentedTriggerComponent( - props: SegmentedTriggerProps -): JSX.Element { +function SegmentedTriggerComponent(props: SegmentedTriggerProps) { const { item, selectedValue, size, block, tabRefs } = props; return ( = { } }, decorators: [ - (Story) => ( -
- -
- ) + (Story) => { + return ( +
+ +
+ ); + } ] }; @@ -49,8 +51,8 @@ const handleSetSegment = action('setSegment'); export const Default: Story = { args: { - segment: MetricStylingAppSegments.VISUALIZE, - setSegment: handleSetSegment, + // segment: MetricStylingAppSegments.VISUALIZE, + // setSegment: handleSetSegment, selectedChartType: ChartType.Line, className: '' }