mirror of https://github.com/buster-so/buster.git
Edit metric title types
This commit is contained in:
parent
58c8c1f4ab
commit
0bd385799f
|
@ -8,14 +8,15 @@ import {
|
|||
SelectTrigger,
|
||||
SelectValue
|
||||
} from './SelectBase';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
interface SelectItemGroup {
|
||||
interface SelectItemGroup<T = string> {
|
||||
label: string;
|
||||
items: SelectItem[];
|
||||
items: SelectItem<T>[];
|
||||
}
|
||||
|
||||
export interface SelectItem {
|
||||
value: string;
|
||||
export interface SelectItem<T = string> {
|
||||
value: T;
|
||||
label: string; //this will be used in the select item text
|
||||
secondaryLabel?: string;
|
||||
icon?: React.ReactNode;
|
||||
|
@ -23,10 +24,10 @@ export interface SelectItem {
|
|||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface SelectProps {
|
||||
items: SelectItem[] | SelectItemGroup[];
|
||||
export interface SelectProps<T = string> {
|
||||
items: SelectItem<T>[] | SelectItemGroup[];
|
||||
disabled?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
onChange: (value: T) => void;
|
||||
placeholder?: string;
|
||||
value?: string;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
|
@ -35,44 +36,49 @@ export interface SelectProps {
|
|||
className?: string;
|
||||
}
|
||||
|
||||
export const Select: React.FC<SelectProps> = React.memo(
|
||||
({
|
||||
items,
|
||||
showIndex,
|
||||
disabled,
|
||||
onChange,
|
||||
placeholder,
|
||||
value,
|
||||
onOpenChange,
|
||||
open,
|
||||
className = ''
|
||||
}) => {
|
||||
return (
|
||||
<SelectBase
|
||||
disabled={disabled}
|
||||
onOpenChange={onOpenChange}
|
||||
open={open}
|
||||
onValueChange={onChange}>
|
||||
<SelectTrigger className={className}>
|
||||
<SelectValue placeholder={placeholder} defaultValue={value} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{items.map((item, index) => (
|
||||
<SelectItemSelector key={index} item={item} index={index} showIndex={showIndex} />
|
||||
))}
|
||||
</SelectContent>
|
||||
</SelectBase>
|
||||
);
|
||||
}
|
||||
);
|
||||
const _Select = <T,>({
|
||||
items,
|
||||
showIndex,
|
||||
disabled,
|
||||
onChange,
|
||||
placeholder,
|
||||
value,
|
||||
onOpenChange,
|
||||
open,
|
||||
className = ''
|
||||
}: SelectProps<T>) => {
|
||||
const onValueChange = useMemoizedFn((value: string) => {
|
||||
onChange(value as T);
|
||||
});
|
||||
return (
|
||||
<SelectBase
|
||||
disabled={disabled}
|
||||
onOpenChange={onOpenChange}
|
||||
open={open}
|
||||
onValueChange={onValueChange}>
|
||||
<SelectTrigger className={className}>
|
||||
<SelectValue placeholder={placeholder} defaultValue={value} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{items.map((item, index) => (
|
||||
<SelectItemSelector key={index} item={item} index={index} showIndex={showIndex} />
|
||||
))}
|
||||
</SelectContent>
|
||||
</SelectBase>
|
||||
);
|
||||
};
|
||||
_Select.displayName = 'Select';
|
||||
export const Select = React.memo(_Select) as typeof _Select;
|
||||
|
||||
Select.displayName = 'Select';
|
||||
|
||||
const SelectItemSelector: React.FC<{
|
||||
item: SelectItem | SelectItemGroup;
|
||||
const SelectItemSelector = <T,>({
|
||||
item,
|
||||
index,
|
||||
showIndex
|
||||
}: {
|
||||
item: SelectItem<T> | SelectItemGroup;
|
||||
index: number;
|
||||
showIndex?: boolean;
|
||||
}> = React.memo(({ item, index, showIndex }) => {
|
||||
}) => {
|
||||
const isGroup = typeof item === 'object' && 'items' in item;
|
||||
|
||||
if (isGroup) {
|
||||
|
@ -101,7 +107,7 @@ const SelectItemSelector: React.FC<{
|
|||
{label}
|
||||
</SelectItem>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
SelectItemSelector.displayName = 'SelectItemSelector';
|
||||
const SelectItemSecondaryText: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from './AppSwitch';
|
|
@ -1,10 +1,11 @@
|
|||
import { AppMaterialIcons, AppPopover } from '@/components/ui';
|
||||
import { Button } from 'antd';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import React, { useMemo } from 'react';
|
||||
import { SelectAxisContainerId } from '../config';
|
||||
import { SelectAxisSettingContent } from './SelectAxisSettingContent';
|
||||
import { useSelectAxisContextSelector } from '../useSelectAxisContext';
|
||||
import { zoneIdToAxisSettingContent } from './config';
|
||||
import { Popover } from '@/components/ui/tooltip/Popover';
|
||||
import { Sliders3 } from '@/components/ui/icons';
|
||||
|
||||
export const SelectAxisSettingsButton: React.FC<{
|
||||
zoneId: SelectAxisContainerId;
|
||||
|
@ -20,14 +21,13 @@ export const SelectAxisSettingsButton: React.FC<{
|
|||
if (!canUseAxisSetting) return null;
|
||||
|
||||
return (
|
||||
<AppPopover
|
||||
<Popover
|
||||
content={<SelectAxisSettingContent zoneId={zoneId} />}
|
||||
trigger="click"
|
||||
destroyTooltipOnHide
|
||||
performant
|
||||
placement="leftBottom">
|
||||
<Button type="text" icon={<AppMaterialIcons icon="tune" />} />
|
||||
</AppPopover>
|
||||
align="end"
|
||||
side="left">
|
||||
<Button variant="ghost" prefix={<Sliders3 />} />
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
SelectAxisSettingsButton.displayName = 'SelectAxisSettingsButton';
|
||||
|
|
|
@ -1,375 +0,0 @@
|
|||
import type { IBusterMetricChartConfig, ColumnMetaData } from '@/api/asset_interfaces';
|
||||
import { AppPopover, AppMaterialIcons } from '@/components/ui';
|
||||
import type { IColumnLabelFormat, DerivedMetricTitle } from '@/components/ui/charts';
|
||||
import { formatLabel, isNumericColumnType, isNumericColumnStyle } from '@/lib';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Input, Button, Divider, Switch, Select } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import last from 'lodash/last';
|
||||
import React, { useMemo } from 'react';
|
||||
import { LabelAndInput } from '../../Common';
|
||||
import { AGGREGATE_OPTIONS } from './EditMetricType';
|
||||
import { Text } from '@/components/ui';
|
||||
import { createColumnFieldOptions } from './helpers';
|
||||
|
||||
const DEFAULT_METRIC_HEADER: Required<IBusterMetricChartConfig['metricHeader']> = {
|
||||
columnId: '',
|
||||
useValue: false,
|
||||
aggregate: 'sum'
|
||||
};
|
||||
|
||||
type NonNullableHeader =
|
||||
| NonNullable<IBusterMetricChartConfig['metricHeader']>
|
||||
| NonNullable<IBusterMetricChartConfig['metricSubHeader']>;
|
||||
|
||||
export const DerivedTitleInput: React.FC<{
|
||||
type: 'header' | 'subHeader';
|
||||
header: IBusterMetricChartConfig['metricHeader'] | IBusterMetricChartConfig['metricSubHeader'];
|
||||
columnLabelFormat: IColumnLabelFormat;
|
||||
columnLabelFormats: IBusterMetricChartConfig['columnLabelFormats'];
|
||||
metricColumnId: IBusterMetricChartConfig['metricColumnId'];
|
||||
columnMetadata: ColumnMetaData[];
|
||||
onUpdateHeaderConfig: (newMetricHeader: IBusterMetricChartConfig['metricHeader']) => void;
|
||||
}> = React.memo(
|
||||
({
|
||||
type,
|
||||
header: headerProp,
|
||||
columnLabelFormats,
|
||||
columnLabelFormat,
|
||||
metricColumnId,
|
||||
columnMetadata,
|
||||
onUpdateHeaderConfig
|
||||
}) => {
|
||||
const header = useMemo(() => {
|
||||
const isStringHeader = typeof headerProp === 'string';
|
||||
|
||||
if (isStringHeader) return headerProp;
|
||||
|
||||
if (headerProp === null) {
|
||||
return {
|
||||
useValue: false,
|
||||
columnId: metricColumnId,
|
||||
aggregate: 'sum'
|
||||
} as DerivedMetricTitle;
|
||||
}
|
||||
|
||||
return headerProp;
|
||||
}, [headerProp]);
|
||||
|
||||
const isStringHeader = typeof header === 'string';
|
||||
|
||||
const onUpdateHeader = useMemoizedFn(
|
||||
(
|
||||
newHeader:
|
||||
| Partial<IBusterMetricChartConfig['metricHeader']>
|
||||
| Partial<IBusterMetricChartConfig['metricSubHeader']>
|
||||
) => {
|
||||
if (typeof newHeader === 'string') {
|
||||
return onUpdateHeaderConfig(newHeader);
|
||||
} else {
|
||||
if (typeof header === 'string') {
|
||||
return onUpdateHeaderConfig({
|
||||
...DEFAULT_METRIC_HEADER,
|
||||
columnId: metricColumnId,
|
||||
...newHeader
|
||||
});
|
||||
} else {
|
||||
return onUpdateHeaderConfig({ ...DEFAULT_METRIC_HEADER, ...header, ...newHeader });
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const value = useMemo(() => {
|
||||
if (isStringHeader) {
|
||||
return header;
|
||||
}
|
||||
}, [isStringHeader, header]);
|
||||
|
||||
const placeholder = useMemo(() => {
|
||||
if (isStringHeader) {
|
||||
return 'Type or link a value';
|
||||
}
|
||||
const { useValue, columnId, aggregate } = header;
|
||||
const columnLabelFormat = columnLabelFormats[columnId];
|
||||
let label = formatLabel(columnId, columnLabelFormat, true);
|
||||
if (useValue && aggregate) {
|
||||
const aggregateLabel =
|
||||
AGGREGATE_OPTIONS.find(({ value }) => value === aggregate)?.label || '';
|
||||
label = `${label} (${aggregateLabel})`;
|
||||
}
|
||||
return label;
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<Input
|
||||
className="w-full"
|
||||
value={value}
|
||||
disabled={false}
|
||||
placeholder={placeholder}
|
||||
onChange={(e) => {
|
||||
onUpdateHeader(e.target.value);
|
||||
}}
|
||||
suffix={
|
||||
<DerivedTitleSuffix
|
||||
columnLabelFormat={columnLabelFormat}
|
||||
header={header}
|
||||
columnMetadata={columnMetadata}
|
||||
metricColumnId={metricColumnId}
|
||||
columnLabelFormats={columnLabelFormats}
|
||||
type={type}
|
||||
onUpdateHeader={onUpdateHeader}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
DerivedTitleInput.displayName = 'DerivedTitleInput';
|
||||
|
||||
const DerivedTitleSuffix: React.FC<{
|
||||
columnLabelFormat: IColumnLabelFormat;
|
||||
columnLabelFormats: IBusterMetricChartConfig['columnLabelFormats'];
|
||||
metricColumnId: IBusterMetricChartConfig['metricColumnId'];
|
||||
columnMetadata: ColumnMetaData[];
|
||||
header: NonNullableHeader;
|
||||
onUpdateHeader: OnUpdateHeaderType;
|
||||
type: 'header' | 'subHeader';
|
||||
}> = ({
|
||||
columnLabelFormat,
|
||||
columnLabelFormats,
|
||||
header,
|
||||
onUpdateHeader,
|
||||
metricColumnId,
|
||||
columnMetadata,
|
||||
type
|
||||
}) => {
|
||||
const isStringHeader = typeof header === 'string';
|
||||
const buttonIcon = isStringHeader ? 'link_off' : 'link';
|
||||
|
||||
return (
|
||||
<div className="flex" onClick={(e) => e.stopPropagation()}>
|
||||
<AppPopover
|
||||
placement="topLeft"
|
||||
trigger="click"
|
||||
destroyTooltipOnHide
|
||||
content={
|
||||
<DerivedTitleSuffixContent
|
||||
columnLabelFormat={columnLabelFormat}
|
||||
header={header}
|
||||
metricColumnId={metricColumnId}
|
||||
columnMetadata={columnMetadata}
|
||||
columnLabelFormats={columnLabelFormats}
|
||||
onUpdateHeader={onUpdateHeader}
|
||||
type={type}
|
||||
/>
|
||||
}>
|
||||
<Button className="h-[18px]!" type="text" icon={<AppMaterialIcons icon={buttonIcon} />} />
|
||||
</AppPopover>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DerivedTitleSuffixContent: React.FC<{
|
||||
type: 'header' | 'subHeader';
|
||||
columnLabelFormat: IColumnLabelFormat;
|
||||
metricColumnId: IBusterMetricChartConfig['metricColumnId'];
|
||||
columnMetadata: ColumnMetaData[];
|
||||
columnLabelFormats: IBusterMetricChartConfig['columnLabelFormats'];
|
||||
header: NonNullableHeader;
|
||||
onUpdateHeader: OnUpdateHeaderType;
|
||||
}> = ({ type, columnLabelFormats, header, onUpdateHeader, columnMetadata }) => {
|
||||
const isStringHeader = typeof header === 'string';
|
||||
const headerColumnId = typeof header === 'object' ? header.columnId : '';
|
||||
const headerUseValue = typeof header === 'object' && header.useValue;
|
||||
|
||||
const ComponentsLoop: {
|
||||
key: string;
|
||||
enabled: boolean;
|
||||
Component: React.ReactNode;
|
||||
}[] = [
|
||||
{
|
||||
key: 'link',
|
||||
enabled: true,
|
||||
Component: (
|
||||
<ToggleHeaderLink isStringHeader={isStringHeader} onUpdateHeader={onUpdateHeader} />
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'columnId',
|
||||
enabled: !isStringHeader,
|
||||
Component: (
|
||||
<DerivedTitleColumnId
|
||||
headerColumnId={headerColumnId}
|
||||
onUpdateHeader={onUpdateHeader}
|
||||
columnMetadata={columnMetadata}
|
||||
columnLabelFormats={columnLabelFormats}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'useValue',
|
||||
enabled: !isStringHeader,
|
||||
Component: <ToggleUseColumnValue onUpdateHeader={onUpdateHeader} useValue={headerUseValue} />
|
||||
},
|
||||
|
||||
{
|
||||
key: 'aggregate',
|
||||
enabled: !isStringHeader && headerUseValue,
|
||||
Component: (
|
||||
<DerivedTitleAggregate
|
||||
onUpdateHeader={onUpdateHeader}
|
||||
aggregate={typeof header === 'object' && header.aggregate ? header.aggregate : 'sum'}
|
||||
columnLabelFormat={columnLabelFormats[headerColumnId]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex w-[285px] max-w-[285px] flex-col">
|
||||
<DerivedTitleSuffixContentHeader type={type} />
|
||||
|
||||
<Divider />
|
||||
|
||||
<div className="flex flex-col space-y-2 p-3">
|
||||
{ComponentsLoop.map(({ enabled, key, Component }) => {
|
||||
if (!enabled) return null;
|
||||
return <React.Fragment key={key}>{Component}</React.Fragment>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DerivedTitleSuffixContentHeader: React.FC<{
|
||||
type: 'header' | 'subHeader';
|
||||
}> = React.memo(
|
||||
({ type }) => {
|
||||
const title = type === 'header' ? 'Header settings' : 'Sub-header settings';
|
||||
|
||||
return (
|
||||
<div className="p-3">
|
||||
<Text>{title}</Text>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
() => true
|
||||
);
|
||||
DerivedTitleSuffixContentHeader.displayName = 'DerivedTitleSuffixContentHeader';
|
||||
|
||||
const ToggleUseColumnValue: React.FC<{
|
||||
onUpdateHeader: OnUpdateHeaderType;
|
||||
useValue: boolean;
|
||||
}> = React.memo(({ onUpdateHeader, useValue }) => {
|
||||
return (
|
||||
<LabelAndInput label="Use column value">
|
||||
<div className="flex justify-end">
|
||||
<Switch checked={useValue} onChange={(v) => onUpdateHeader({ useValue: v })} />
|
||||
</div>
|
||||
</LabelAndInput>
|
||||
);
|
||||
});
|
||||
ToggleUseColumnValue.displayName = 'ToggleUseColumnValue';
|
||||
|
||||
const ToggleHeaderLink: React.FC<{
|
||||
isStringHeader: boolean;
|
||||
onUpdateHeader: OnUpdateHeaderType;
|
||||
}> = React.memo(({ isStringHeader, onUpdateHeader }) => {
|
||||
return (
|
||||
<LabelAndInput label="Use column">
|
||||
<div className="flex justify-end">
|
||||
<Switch
|
||||
checked={!isStringHeader}
|
||||
onChange={(v) => {
|
||||
onUpdateHeader(v ? {} : '');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</LabelAndInput>
|
||||
);
|
||||
});
|
||||
ToggleHeaderLink.displayName = 'ToggleHeaderLink';
|
||||
|
||||
const DerivedTitleColumnId: React.FC<{
|
||||
headerColumnId: IBusterMetricChartConfig['metricColumnId'];
|
||||
onUpdateHeader: OnUpdateHeaderType;
|
||||
columnMetadata: ColumnMetaData[];
|
||||
columnLabelFormats: IBusterMetricChartConfig['columnLabelFormats'];
|
||||
}> = React.memo(({ headerColumnId, onUpdateHeader, columnMetadata, columnLabelFormats }) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
const columnOptions = useMemo(() => {
|
||||
return createColumnFieldOptions(columnMetadata, columnLabelFormats, styles.icon);
|
||||
}, [columnMetadata, columnLabelFormats, styles.icon]);
|
||||
|
||||
const selectedColumn = useMemo(() => {
|
||||
return columnOptions.find((option) => option.value === headerColumnId);
|
||||
}, [headerColumnId, columnOptions]);
|
||||
|
||||
const onChangeSelect = useMemoizedFn((v: string) => {
|
||||
const columnLabelFormat = columnLabelFormats[v];
|
||||
const isNumberColumn = isNumericColumnType(columnLabelFormat?.columnType);
|
||||
const isNumericStyle = isNumericColumnStyle(columnLabelFormat?.style);
|
||||
const newHeader: Partial<IBusterMetricChartConfig['metricHeader']> = { columnId: v };
|
||||
if (!isNumberColumn || !isNumericStyle) {
|
||||
newHeader.aggregate = 'first';
|
||||
}
|
||||
onUpdateHeader(newHeader);
|
||||
});
|
||||
|
||||
return (
|
||||
<LabelAndInput label="Column ID">
|
||||
<Select
|
||||
className="w-full overflow-hidden"
|
||||
options={columnOptions}
|
||||
value={selectedColumn?.value}
|
||||
onChange={onChangeSelect}
|
||||
/>
|
||||
</LabelAndInput>
|
||||
);
|
||||
});
|
||||
DerivedTitleColumnId.displayName = 'DerivedTitleColumnId';
|
||||
|
||||
const DerivedTitleAggregate: React.FC<{
|
||||
onUpdateHeader: OnUpdateHeaderType;
|
||||
aggregate: Required<DerivedMetricTitle>['aggregate'];
|
||||
columnLabelFormat: IColumnLabelFormat;
|
||||
}> = React.memo(({ onUpdateHeader, aggregate, columnLabelFormat }) => {
|
||||
const isNumberColumn = isNumericColumnType(columnLabelFormat?.columnType);
|
||||
const isNumericStyle = isNumericColumnStyle(columnLabelFormat?.style);
|
||||
const disableOptions = !isNumberColumn || !isNumericStyle;
|
||||
|
||||
const selectedOption = useMemo(() => {
|
||||
if (!disableOptions) {
|
||||
return AGGREGATE_OPTIONS.find((option) => option.value === aggregate)?.value;
|
||||
}
|
||||
return last(AGGREGATE_OPTIONS)?.value;
|
||||
}, [aggregate, disableOptions]);
|
||||
|
||||
return (
|
||||
<LabelAndInput label="Aggregate">
|
||||
<Select
|
||||
options={AGGREGATE_OPTIONS}
|
||||
value={selectedOption}
|
||||
disabled={disableOptions}
|
||||
onChange={(v) =>
|
||||
onUpdateHeader({ aggregate: v as Required<DerivedMetricTitle>['aggregate'] })
|
||||
}
|
||||
/>
|
||||
</LabelAndInput>
|
||||
);
|
||||
});
|
||||
DerivedTitleAggregate.displayName = 'DerivedTitleAggregate';
|
||||
|
||||
type OnUpdateHeaderType = (
|
||||
header:
|
||||
| Partial<IBusterMetricChartConfig['metricHeader']>
|
||||
| Partial<IBusterMetricChartConfig['metricSubHeader']>
|
||||
) => void;
|
||||
|
||||
const useStyles = createStyles(({ token }) => ({
|
||||
icon: token.colorIcon
|
||||
}));
|
|
@ -1,5 +1,5 @@
|
|||
import { IBusterMetricChartConfig } from '@/api/asset_interfaces';
|
||||
import { isNumericColumnStyle, isNumericColumnType } from '@/lib';
|
||||
import { isNumericColumnStyle, isNumericColumnType } from '@/lib/messages';
|
||||
import React, { useMemo } from 'react';
|
||||
import { LabelAndInput } from '../../Common';
|
||||
import { Button, Select } from 'antd';
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
import React from 'react';
|
||||
import { LabelAndInput } from '../../Common';
|
||||
import type { ColumnMetaData, IBusterMetricChartConfig } from '@/api/asset_interfaces';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { DerivedTitleInput } from './EditDerivedHeader';
|
||||
|
||||
export const EditMetricSubHeader: React.FC<{
|
||||
metricSubHeader: IBusterMetricChartConfig['metricSubHeader'];
|
||||
columnLabelFormats: IBusterMetricChartConfig['columnLabelFormats'];
|
||||
metricColumnId: IBusterMetricChartConfig['metricColumnId'];
|
||||
columnMetadata: ColumnMetaData[];
|
||||
onUpdateChartConfig: (chartConfig: Partial<IBusterMetricChartConfig>) => void;
|
||||
}> = React.memo(
|
||||
({
|
||||
metricSubHeader,
|
||||
columnMetadata,
|
||||
columnLabelFormats,
|
||||
metricColumnId,
|
||||
onUpdateChartConfig
|
||||
}) => {
|
||||
const columnLabelFormat = columnLabelFormats[metricColumnId];
|
||||
|
||||
const onUpdateMetricHeader = useMemoizedFn(
|
||||
(newMetricSubHeader: IBusterMetricChartConfig['metricSubHeader']) => {
|
||||
onUpdateChartConfig({ metricSubHeader: newMetricSubHeader });
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<LabelAndInput label={'Sub-header'}>
|
||||
<DerivedTitleInput
|
||||
type="subHeader"
|
||||
header={metricSubHeader}
|
||||
columnLabelFormat={columnLabelFormat}
|
||||
metricColumnId={metricColumnId}
|
||||
columnMetadata={columnMetadata}
|
||||
columnLabelFormats={columnLabelFormats}
|
||||
onUpdateHeaderConfig={onUpdateMetricHeader}
|
||||
/>
|
||||
</LabelAndInput>
|
||||
);
|
||||
}
|
||||
);
|
||||
EditMetricSubHeader.displayName = 'EditMetricSubHeader';
|
|
@ -6,11 +6,9 @@ import last from 'lodash/last';
|
|||
import { useMemoizedFn } from 'ahooks';
|
||||
import { isNumericColumnStyle, isNumericColumnType } from '@/lib';
|
||||
import { ColumnLabelFormat } from '@/components/ui/charts';
|
||||
import { SelectItem } from '@/components/ui/select';
|
||||
|
||||
export const AGGREGATE_OPTIONS: {
|
||||
label: string;
|
||||
value: IBusterMetricChartConfig['metricValueAggregate'];
|
||||
}[] = [
|
||||
export const AGGREGATE_OPTIONS: SelectItem<IBusterMetricChartConfig['metricValueAggregate']>[] = [
|
||||
{ label: 'Sum', value: 'sum' },
|
||||
{ label: 'Average', value: 'average' },
|
||||
{ label: 'Median', value: 'median' },
|
||||
|
|
|
@ -1,24 +1,21 @@
|
|||
import { ColumnMetaData, IBusterMetricChartConfig } from '@/api/asset_interfaces';
|
||||
import { formatLabel } from '@/lib';
|
||||
import { ColumnTypeIcon } from '../SelectAxis/config';
|
||||
import { type SelectItem } from '@/components/ui/select';
|
||||
|
||||
export const createColumnFieldOptions = (
|
||||
columnMetadata: ColumnMetaData[],
|
||||
columnLabelFormats: IBusterMetricChartConfig['columnLabelFormats'],
|
||||
iconClass: string
|
||||
) => {
|
||||
return columnMetadata.map((column) => {
|
||||
): SelectItem[] => {
|
||||
return columnMetadata.map<SelectItem>((column) => {
|
||||
const labelFormat = columnLabelFormats[column.name];
|
||||
const formattedLabel = formatLabel(column.name, labelFormat, true);
|
||||
const Icon = ColumnTypeIcon[labelFormat.style];
|
||||
|
||||
return {
|
||||
label: (
|
||||
<div className="flex w-full items-center space-x-1.5 overflow-hidden">
|
||||
<div className={`${iconClass} flex`}>{Icon.icon}</div>
|
||||
<span className="truncate">{formattedLabel}</span>
|
||||
</div>
|
||||
),
|
||||
icon: Icon.icon,
|
||||
label: formattedLabel,
|
||||
value: column.name
|
||||
};
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue