mirror of https://github.com/buster-so/buster.git
update some segmented content
This commit is contained in:
parent
f64ad95a20
commit
e945c7f843
|
@ -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 (
|
||||
|
|
|
@ -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' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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} />;
|
||||
|
|
|
@ -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} />;
|
||||
|
|
|
@ -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} />;
|
||||
|
|
Loading…
Reference in New Issue