update some segmented content

This commit is contained in:
Nate Kelley 2025-03-01 16:06:16 -07:00
parent f64ad95a20
commit e945c7f843
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
13 changed files with 99 additions and 82 deletions

View File

@ -1,4 +1,4 @@
import { AppSegmented, AppSegmentedProps } from '@/components/ui';
import { AppSegmented } from '@/components/ui/segmented';
import React, { useMemo } from 'react';
import { CopyLinkButton } from './CopyLinkButton';
import { ShareAssetType } from '@/api/asset_interfaces';
@ -23,7 +23,7 @@ export const ShareMenuTopBar: React.FC<{
({ assetType, onCopyLink, selectedOptions, onChangeSelectedOption, permission }) => {
const isOwner = permission === ShareRole.OWNER;
const options: AppSegmentedProps['options'] = useMemo(() => {
const options: SegmentedItem<ShareMenuTopBarOptions>[] = useMemo(() => {
return [
{
value: ShareMenuTopBarOptions.Share,
@ -45,8 +45,8 @@ export const ShareMenuTopBar: React.FC<{
.map((o) => ({ ...o, show: undefined }));
}, [assetType, isOwner]);
const onChange = useMemoizedFn((v: SegmentedItem) => {
onChangeSelectedOption(v as ShareMenuTopBarOptions);
const onChange = useMemoizedFn((v: SegmentedItem<ShareMenuTopBarOptions>) => {
onChangeSelectedOption(v.value);
});
return (

View File

@ -110,3 +110,13 @@ export const CustomStyling: Story = {
className: 'bg-blue-100 [&_[data-state=active]]:text-blue-700'
}
};
export const WithOnlyIcons: Story = {
args: {
options: [
{ value: 'tab1', icon: <HouseModern />, tooltip: 'Tooltip 1' },
{ value: 'tab2', icon: <Grid />, tooltip: 'Tooltip 2' },
{ value: 'tab3', icon: <BottleChampagne />, tooltip: 'Tooltip 3' }
]
}
};

View File

@ -7,15 +7,17 @@ import { cn } from '@/lib/classMerge';
import { useEffect, useState, useLayoutEffect } from 'react';
import { cva } from 'class-variance-authority';
import { useMemoizedFn } from 'ahooks';
import { Tooltip } from '../tooltip/Tooltip';
export interface SegmentedItem<T extends string = string> {
value: T;
label: React.ReactNode;
label?: React.ReactNode;
icon?: React.ReactNode;
disabled?: boolean;
tooltip?: string;
}
interface AppSegmentedProps<T extends string = string> {
export interface AppSegmentedProps<T extends string = string> {
options: SegmentedItem<T>[];
value?: T;
onChange?: (value: SegmentedItem<T>) => void;
@ -200,26 +202,37 @@ interface SegmentedTriggerProps<T extends string = string> {
function SegmentedTriggerComponent<T extends string = string>(props: SegmentedTriggerProps<T>) {
const { item, selectedValue, size, block, tabRefs } = props;
const NodeWrapper = item.tooltip
? ({ children }: { children: React.ReactNode }) => (
<div className="flex items-center gap-x-1">{children}</div>
)
: React.Fragment;
return (
<Tabs.Trigger
key={item.value}
value={item.value}
disabled={item.disabled}
ref={(el) => {
if (el) tabRefs.current.set(item.value, el);
}}
className={cn(
'focus-visible:ring-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
triggerVariants({
size,
block,
disabled: item.disabled,
selected: selectedValue === item.value
})
)}>
{item.icon && <span className={cn('flex items-center text-sm')}>{item.icon}</span>}
<span className={cn('text-sm')}>{item.label}</span>
</Tabs.Trigger>
<Tooltip title={item.tooltip || ''} sideOffset={10}>
<Tabs.Trigger
key={item.value}
value={item.value}
disabled={item.disabled}
ref={(el) => {
if (el) tabRefs.current.set(item.value, el);
}}
className={cn(
'focus-visible:ring-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
triggerVariants({
size,
block,
disabled: item.disabled,
selected: selectedValue === item.value
})
)}>
<>
{item.icon && <span className={cn('flex items-center text-sm')}>{item.icon}</span>}
{item.label && <span className={cn('text-sm')}>{item.label}</span>}
</>
</Tabs.Trigger>
</Tooltip>
);
}

View File

@ -9,7 +9,7 @@ import { KeyboardShortcutPill } from '../pills/KeyboardShortcutPills';
import omit from 'lodash/omit';
export interface TooltipProps
extends Pick<React.ComponentProps<typeof TooltipContentBase>, 'align' | 'side'>,
extends Pick<React.ComponentProps<typeof TooltipContentBase>, 'align' | 'side' | 'sideOffset'>,
Pick<React.ComponentProps<typeof TooltipProvider>, 'delayDuration' | 'skipDelayDuration'> {
children: React.ReactNode;
title: string;
@ -18,14 +18,24 @@ export interface TooltipProps
}
export const Tooltip = React.memo<TooltipProps>(
({ children, title, shortcut, delayDuration = 0, skipDelayDuration, align, side, open }) => {
({
children,
title,
sideOffset,
shortcut,
delayDuration = 0,
skipDelayDuration,
align,
side,
open
}) => {
if (!title && !shortcut?.length) return children;
return (
<TooltipProvider delayDuration={delayDuration} skipDelayDuration={skipDelayDuration}>
<TooltipBase open={open}>
<TooltipTrigger asChild>{children}</TooltipTrigger>
<TooltipContentBase align={align} side={side}>
<TooltipContentBase align={align} side={side} sideOffset={sideOffset}>
<TooltipContent title={title} shortcut={shortcut} />
</TooltipContentBase>
</TooltipBase>

View File

@ -98,7 +98,7 @@ export const CollectionListHeader: React.FC<{
});
CollectionListHeader.displayName = 'CollectionListHeader';
const filters = [
const filters: SegmentedItem<string>[] = [
{
label: 'All',
value: JSON.stringify({})
@ -125,7 +125,7 @@ const CollectionFilters: React.FC<{
const onChangeFilter = useMemoizedFn((v: SegmentedItem) => {
let parsedValue;
try {
parsedValue = JSON.parse(v as string);
parsedValue = JSON.parse(v.value as string);
} catch (error) {
console.error('error', error);
}

View File

@ -7,8 +7,9 @@ import Link from 'next/link';
import { BusterRoutes, createBusterRoute } from '@/routes';
import { useBusterDashboardContextSelector } from '@/context/Dashboards';
import { DashboardsListEmitPayload } from '@/api/buster_socket/dashboards';
import { AppMaterialIcons, AppSegmented } from '@/components/ui';
import { AppSegmented, SegmentedItem } from '@/components/ui/segmented';
import { useMemoizedFn } from 'ahooks';
import { Plus } from '@/components/ui/icons';
export const DashboardHeader: React.FC<{
dashboardFilters: {
@ -64,7 +65,7 @@ export const DashboardHeader: React.FC<{
<div className="flex items-center">
<Button
type="default"
icon={<AppMaterialIcons icon="add" />}
icon={<Plus />}
loading={isCreatingDashboard}
onClick={onClickNewDashboardButton}>
New Dashboard
@ -81,7 +82,7 @@ const DashboardFilters: React.FC<{
Omit<DashboardsListEmitPayload['payload'], 'page_token' | 'page_size'>
>;
}> = ({ onChangeFilter, activeFilters }) => {
const filters = [
const filters: SegmentedItem<string>[] = [
{
label: 'All ',
value: JSON.stringify({})
@ -110,7 +111,7 @@ const DashboardFilters: React.FC<{
options={filters}
value={selectedFilter?.value}
onChange={(v) => {
const parsedValue = JSON.parse(v as string) as {
const parsedValue = JSON.parse(v.value) as {
shared_with_me?: boolean;
only_my_dashboards?: boolean;
};

View File

@ -1,10 +1,10 @@
'use client';
import React, { useMemo, useState } from 'react';
import React, { useMemo } from 'react';
import { Breadcrumb, Button } from 'antd';
import Link from 'next/link';
import { BusterRoutes, createBusterRoute } from '@/routes';
import { AppMaterialIcons, AppSegmented, AppTooltip } from '@/components/ui';
import { AppMaterialIcons, AppSegmented, AppTooltip, SegmentedItem } from '@/components/ui';
import { NewDatasetModal } from '@/components/features/modal/NewDatasetModal';
import { AppContentHeader } from '@/components/ui/layouts/AppContentHeader_Old';
import { useIndividualDataset } from '@/context/Datasets';
@ -90,7 +90,7 @@ const DatasetFilters: React.FC<{
datasetFilter: 'all' | 'published' | 'drafts';
setDatasetFilter: (filter: 'all' | 'published' | 'drafts') => void;
}> = ({ datasetFilter, setDatasetFilter }) => {
const options: { label: string; value: 'all' | 'published' | 'drafts' }[] = useMemo(
const options: SegmentedItem<'all' | 'published' | 'drafts'>[] = useMemo(
() => [
{ label: 'All', value: 'all' },
{ label: 'Published', value: 'published' },
@ -104,7 +104,7 @@ const DatasetFilters: React.FC<{
options={options}
value={datasetFilter}
onChange={(value) => {
setDatasetFilter(value as 'all' | 'published' | 'drafts');
setDatasetFilter(value.value);
}}
/>
);

View File

@ -1,9 +1,9 @@
import { IBusterMetricChartConfig } from '@/api/asset_interfaces';
import { type IBusterMetricChartConfig } from '@/api/asset_interfaces';
import React, { useMemo } from 'react';
import { LabelAndInput } from '../../../Common/LabelAndInput';
import { AppMaterialIcons, AppSegmented, AppTooltip } from '@/components/ui';
import { ChartType, ColumnSettings } from '@/components/ui/charts/interfaces';
import { useEditAppSegmented } from './useEditAppSegmented';
import { AppMaterialIcons, AppSegmented, AppTooltip, SegmentedItem } from '@/components/ui';
import { ChartType, type ColumnSettings } from '@/components/ui/charts/interfaces';
import { useMemoizedFn } from 'ahooks';
const options = [
{
@ -26,7 +26,7 @@ const options = [
value: 'dot',
tooltip: 'Dot'
}
].map(({ tooltip, icon, ...option }) => ({
].map<SegmentedItem<string>>(({ tooltip, icon, ...option }) => ({
...option,
icon: (
<AppTooltip title={tooltip}>
@ -46,24 +46,16 @@ export const EditDisplayAs: React.FC<{
return options.find((option) => option.value === columnVisualization)?.value || 'bar';
}, [columnVisualization]);
const { onClick } = useEditAppSegmented({
onClick: (value) => {
onUpdateColumnSettingConfig({
columnVisualization: value as Required<ColumnSettings>['columnVisualization']
});
}
const onChange = useMemoizedFn((value: SegmentedItem<string>) => {
onUpdateColumnSettingConfig({
columnVisualization: value.value as Required<ColumnSettings>['columnVisualization']
});
});
return (
<LabelAndInput label="Display as">
<div className="flex justify-end">
<AppSegmented
options={options}
block={false}
bordered={false}
value={selectedOption}
onClick={onClick}
/>
<AppSegmented options={options} block={false} value={selectedOption} onChange={onChange} />
</div>
</LabelAndInput>
);

View File

@ -1,11 +1,11 @@
import type { IColumnLabelFormat } from '@/components/ui/charts/interfaces/columnLabelInterfaces';
import React, { useMemo } from 'react';
import { LabelAndInput } from '../../../Common/LabelAndInput';
import { AppSegmented } from '@/components/ui';
import { AppSegmented, type SegmentedItem } from '@/components/ui/segmented';
import { ColumnTypeIcon } from '../config';
import { useEditAppSegmented } from './useEditAppSegmented';
import { createStyles } from 'antd-style';
import { isDateColumnType, isNumericColumnType } from '@/lib';
import { useMemoizedFn } from 'ahooks';
export const EditLabelStyle: React.FC<{
onUpdateColumnConfig: (columnLabelFormat: Partial<IColumnLabelFormat>) => void;
@ -33,12 +33,10 @@ export const EditLabelStyle: React.FC<{
}));
}, [enabledOptions]);
const { onClick } = useEditAppSegmented({
onClick: (value) => {
onUpdateColumnConfig({
style: value as IColumnLabelFormat['style']
});
}
const onChange = useMemoizedFn((value: SegmentedItem<string>) => {
onUpdateColumnConfig({
style: value.value as IColumnLabelFormat['style']
});
});
if (enabledOptions.length === 0) return null;
@ -46,7 +44,7 @@ export const EditLabelStyle: React.FC<{
return (
<LabelAndInput label="Style">
<div className="flex items-center justify-end">
<AppSegmented bordered={false} options={options} value={style} onClick={onClick} />
<AppSegmented options={options} value={style} onChange={onChange} />
</div>
</LabelAndInput>
);

View File

@ -2,7 +2,6 @@ import React, { useMemo } from 'react';
import { LabelAndInput } from '../../../Common/LabelAndInput';
import { BusterChartConfigProps, ColumnSettings } from '@/components/ui/charts';
import { AppMaterialIcons, AppSegmented, AppTooltip } from '@/components/ui';
import { useEditAppSegmented } from './useEditAppSegmented';
import { ENABLED_DOTS_ON_LINE_SIZE } from '@/api/asset_interfaces';
import { useMemoizedFn } from 'ahooks';
import { type SegmentedItem } from '@/components/ui/segmented';
@ -106,12 +105,8 @@ export const EditLineStyle: React.FC<{
methodRecord[lineValue]();
});
const onChangeValue = useMemoizedFn((value: SegmentedItem) => {
if (value) onClickValue(value as string);
});
const { onClick } = useEditAppSegmented({
onClick: onClickValue
const onChange = useMemoizedFn((value: SegmentedItem) => {
if (value?.value) onClickValue(value.value);
});
return (
@ -120,10 +115,8 @@ export const EditLineStyle: React.FC<{
<AppSegmented
options={shownOptions}
block={false}
bordered={false}
value={selectedOption}
onClick={onClick}
// onChange={onChangeValue}
onChange={onChange}
/>
</div>
</LabelAndInput>

View File

@ -6,7 +6,7 @@ import { useChatLayoutContextSelector } from '../../ChatLayoutContext';
import { useMemoizedFn } from 'ahooks';
import { type SegmentedItem } from '@/components/ui/segmented';
const segmentOptions: { label: string; value: DashboardFileView }[] = [
const segmentOptions: SegmentedItem<FileView>[] = [
{ label: 'Dashboard', value: 'dashboard' },
{ label: 'File', value: 'file' }
];
@ -15,8 +15,8 @@ export const DashboardContainerHeaderSegment: React.FC<FileContainerSegmentProps
({ selectedFileView }) => {
const onSetFileView = useChatLayoutContextSelector((x) => x.onSetFileView);
const onChange = useMemoizedFn((fileView: SegmentedItem) => {
onSetFileView({ fileView: fileView as FileView });
const onChange = useMemoizedFn((fileView: SegmentedItem<FileView>) => {
onSetFileView({ fileView: fileView.value });
});
return <AppSegmented options={segmentOptions} value={selectedFileView} onChange={onChange} />;

View File

@ -16,8 +16,8 @@ export const MetricContainerHeaderSegment: React.FC<FileContainerSegmentProps> =
({ selectedFileView }) => {
const onSetFileView = useChatLayoutContextSelector((x) => x.onSetFileView);
const onChange = useMemoizedFn((fileView: SegmentedItem) => {
onSetFileView({ fileView: fileView as FileView });
const onChange = useMemoizedFn((fileView: SegmentedItem<FileView>) => {
onSetFileView({ fileView: fileView.value });
});
return <AppSegmented options={segmentOptions} value={selectedFileView} onChange={onChange} />;

View File

@ -14,8 +14,8 @@ export const ReasoningContainerHeaderSegment: React.FC<FileContainerSegmentProps
({ selectedFileView }) => {
const onSetFileView = useChatLayoutContextSelector((x) => x.onSetFileView);
const onChange = useMemoizedFn((fileView: SegmentedItem) => {
onSetFileView({ fileView: fileView as FileView });
const onChange = useMemoizedFn((fileView: SegmentedItem<FileView>) => {
onSetFileView({ fileView: fileView.value });
});
return <AppSegmented options={segmentOptions} value={selectedFileView} onChange={onChange} />;