Skip to content

Commit 273921e

Browse files
authored
Merge pull request #1870 from merico-dev/radar-customization
radar customization
2 parents f7bf421 + 6b44d70 commit 273921e

File tree

14 files changed

+170
-217
lines changed

14 files changed

+170
-217
lines changed

api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devtable/api",
3-
"version": "14.60.0",
3+
"version": "14.60.1",
44
"description": "",
55
"main": "index.js",
66
"scripts": {

dashboard/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devtable/dashboard",
3-
"version": "14.60.0",
3+
"version": "14.60.1",
44
"license": "Apache-2.0",
55
"repository": {
66
"url": "https://github.com/merico-dev/table"
@@ -44,7 +44,7 @@
4444
},
4545
"dependencies": {
4646
"@dnd-kit/core": "^6.1.0",
47-
"@dnd-kit/helpers": "^0.0.3",
47+
"@dnd-kit/helpers": "0.0.4",
4848
"@dnd-kit/react": "^0.0.4",
4949
"@dnd-kit/sortable": "^8.0.0",
5050
"@emotion/cache": "^11.11.0",
Lines changed: 42 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,58 @@
11
import { move } from '@dnd-kit/helpers';
22
import { DragDropProvider } from '@dnd-kit/react';
33
import { Group, Stack, Text } from '@mantine/core';
4-
import _ from 'lodash';
5-
import { forwardRef, useMemo } from 'react';
4+
import { ComponentProps, forwardRef, memo } from 'react';
5+
import { Control, FieldValues, useFieldArray } from 'react-hook-form';
66
import { useTranslation } from 'react-i18next';
7-
import { v4 as uuidv4 } from 'uuid';
87
import { NameColorMapRow } from './types';
98
import { RowEditor } from './row-editor';
109
import { AddARow } from './add-a-row';
1110
import { SelectPalette } from './select-palette';
1211

12+
type FieldWithId = NameColorMapRow & { id: string };
13+
1314
type Props = {
14-
value: NameColorMapRow[];
15-
onChange: (v: NameColorMapRow[]) => void;
16-
zIndex?: number;
15+
control: Control<$TSFixMe>;
16+
name: string;
1717
names: string[];
1818
};
1919

20-
export const NameColorMapEditor = forwardRef<HTMLDivElement, Props>(({ value, onChange, zIndex = 340, names }, ref) => {
21-
const { t } = useTranslation();
22-
const rows = useMemo(() => {
23-
return value.map((r) => ({
24-
id: uuidv4(),
25-
...r,
26-
}));
27-
}, [value]);
20+
export const NameColorMapEditor = memo(
21+
forwardRef<HTMLDivElement, Props>(({ control, name, names }, ref) => {
22+
const { t } = useTranslation();
23+
const { fields, append, remove, update, replace } = useFieldArray({
24+
control,
25+
name,
26+
});
27+
28+
const typedFields = fields as FieldWithId[];
2829

29-
const append = (v: NameColorMapRow) => {
30-
onChange([...value, v]);
31-
};
32-
const remove = (index: number) => {
33-
const newValue = [...value];
34-
newValue.splice(index, 1);
35-
onChange(newValue);
36-
};
37-
const getChangeHandler = (index: number) => (v: NameColorMapRow) => {
38-
const newValue = [...value];
39-
newValue[index] = v;
40-
onChange(newValue);
41-
};
30+
const handleAppend = (v: NameColorMapRow) => {
31+
append(v);
32+
};
4233

43-
const onDragEnd = (event: any) => {
44-
const { source, target } = event.operation;
45-
const newRows = move(rows, source, target);
46-
onChange(newRows.map((v) => _.omit(v, 'id')));
47-
};
34+
const onDragEnd: ComponentProps<typeof DragDropProvider>['onDragEnd'] = (event) => {
35+
const { source, target } = event.operation;
36+
if (!source || !target) return;
37+
const newFields = move(typedFields, source, target);
38+
replace(newFields.map((f) => ({ name: f.name, color: f.color })));
39+
};
4840

49-
return (
50-
<Stack ref={ref} pt={4}>
51-
<Group justify="space-between">
52-
<Text size="sm" fw="500" mb={-4}>
53-
{t('viz.pie_chart.color.map.label')}
54-
</Text>
55-
<SelectPalette value={value} onChange={onChange} />
56-
</Group>
57-
<DragDropProvider onDragEnd={onDragEnd}>
58-
{rows.map((r, index) => (
59-
<RowEditor
60-
key={r.id}
61-
row={r}
62-
handleChange={getChangeHandler(index)}
63-
handleRemove={() => remove(index)}
64-
index={index}
65-
names={names}
66-
/>
67-
))}
68-
</DragDropProvider>
69-
<AddARow append={append} />
70-
</Stack>
71-
);
72-
});
41+
return (
42+
<Stack ref={ref} pt={4}>
43+
<Group justify="space-between">
44+
<Text size="sm" fw="500" mb={-4}>
45+
{t('viz.pie_chart.color.map.label')}
46+
</Text>
47+
<SelectPalette fields={typedFields} replace={replace} />
48+
</Group>
49+
<DragDropProvider onDragEnd={onDragEnd}>
50+
{typedFields.map((field, index) => (
51+
<RowEditor key={field.id} field={field} update={update} remove={remove} index={index} names={names} />
52+
))}
53+
</DragDropProvider>
54+
<AddARow append={handleAppend} />
55+
</Stack>
56+
);
57+
}),
58+
);
Lines changed: 75 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,89 @@
11
import { useSortable } from '@dnd-kit/react/sortable';
2-
import { ActionIcon, Autocomplete, Badge, Center, CloseButton, ColorInput, Flex, Group } from '@mantine/core';
2+
import { ActionIcon, Autocomplete, Badge, Center, CloseButton, Flex, Group } from '@mantine/core';
3+
import { ColorPickerPopoverForViz } from '~/components/widgets/color-picker-popover/color-picker-popover-for-viz';
34
import { IconGripVertical } from '@tabler/icons-react';
45
import { useBoolean } from 'ahooks';
6+
import { isEqual } from 'lodash';
7+
import { memo } from 'react';
58
import { useTranslation } from 'react-i18next';
69
import { NameColorMapRow } from './types';
710

8-
type RowFieldItem = {
9-
id: string;
10-
} & NameColorMapRow;
11-
1211
type NameColorMapRowProps = {
13-
row: RowFieldItem;
14-
handleChange: (v: RowFieldItem) => void;
15-
handleRemove: () => void;
12+
field: NameColorMapRow & { id: string };
13+
update: (index: number, data: NameColorMapRow) => void;
14+
remove: (index: number) => void;
1615
index: number;
1716
names: string[];
1817
};
1918

20-
export const RowEditor = ({ row, index, handleChange, handleRemove, names }: NameColorMapRowProps) => {
21-
const { t } = useTranslation();
22-
const [touched, { setTrue: setTouched }] = useBoolean(false);
23-
const [hovering, { setTrue, setFalse }] = useBoolean(false);
24-
const { ref, handleRef } = useSortable({
25-
id: row.id,
26-
index,
27-
});
28-
29-
const changeName = (name: string) => {
30-
handleChange({
31-
...row,
32-
name,
19+
export const RowEditor = memo(
20+
({ field, index, update, remove, names }: NameColorMapRowProps) => {
21+
const { t } = useTranslation();
22+
const [touched, { setTrue: setTouched }] = useBoolean(false);
23+
const [hovering, { setTrue, setFalse }] = useBoolean(false);
24+
const { ref, handleRef } = useSortable({
25+
id: field.id,
26+
index,
3327
});
34-
};
3528

36-
const changeColor = (color: string) => {
37-
handleChange({
38-
...row,
39-
color,
40-
});
41-
};
29+
const changeName = (name: string) => {
30+
update(index, {
31+
name,
32+
color: field.color,
33+
});
34+
};
4235

43-
return (
44-
<Flex
45-
ref={ref}
46-
gap="sm"
47-
justify="flex-start"
48-
align="center"
49-
direction="row"
50-
wrap="nowrap"
51-
onMouseEnter={setTrue}
52-
onMouseLeave={setFalse}
53-
>
54-
<Center style={{ minWidth: '30px', maxWidth: '30px', flex: 0 }}>
55-
{hovering ? (
56-
<ActionIcon size="xs" ref={handleRef} variant="subtle">
57-
<IconGripVertical />
58-
</ActionIcon>
59-
) : (
60-
<Badge size="sm" variant="light">
61-
{index + 1}
62-
</Badge>
63-
)}
64-
</Center>
65-
<Group grow wrap="nowrap" style={{ flex: 1 }}>
66-
<Autocomplete
67-
size="xs"
68-
value={row.name}
69-
placeholder={t('viz.pie_chart.color.map.name')}
70-
onChange={changeName}
71-
onClick={setTouched}
72-
error={touched && !row.name}
73-
data={names}
74-
maxDropdownHeight={500}
75-
/>
76-
<ColorInput
77-
styles={{
78-
root: {
79-
flexGrow: 1,
80-
},
81-
}}
82-
popoverProps={{
83-
withinPortal: true,
84-
zIndex: 340,
85-
}}
86-
size="xs"
87-
value={row.color}
88-
onChange={changeColor}
89-
onClick={setTouched}
90-
error={touched && !row.color}
91-
/>
92-
</Group>
93-
<div style={{ minWidth: '40px', maxWidth: '40px', flex: 0 }}>
94-
<CloseButton onClick={handleRemove} size="sm" />
95-
</div>
96-
</Flex>
97-
);
98-
};
36+
const changeColor = (color: string) => {
37+
update(index, {
38+
name: field.name,
39+
color,
40+
});
41+
};
42+
43+
return (
44+
<Flex
45+
ref={ref}
46+
gap="sm"
47+
justify="flex-start"
48+
align="center"
49+
direction="row"
50+
wrap="nowrap"
51+
onMouseEnter={setTrue}
52+
onMouseLeave={setFalse}
53+
>
54+
<Center style={{ minWidth: '30px', maxWidth: '30px', flex: 0 }}>
55+
{hovering ? (
56+
<ActionIcon size="xs" ref={handleRef} variant="subtle">
57+
<IconGripVertical />
58+
</ActionIcon>
59+
) : (
60+
<Badge size="sm" variant="light">
61+
{index + 1}
62+
</Badge>
63+
)}
64+
</Center>
65+
<Group grow wrap="nowrap" style={{ flex: 1 }}>
66+
<Autocomplete
67+
size="xs"
68+
value={field.name}
69+
placeholder={t('viz.pie_chart.color.map.name')}
70+
onChange={changeName}
71+
onClick={setTouched}
72+
error={touched && !field.name}
73+
data={names}
74+
maxDropdownHeight={500}
75+
/>
76+
<ColorPickerPopoverForViz
77+
value={field.color}
78+
onChange={changeColor}
79+
label={t('viz.pie_chart.color.map.color')}
80+
/>
81+
</Group>
82+
<div style={{ minWidth: '40px', maxWidth: '40px', flex: 0 }}>
83+
<CloseButton onClick={() => remove(index)} size="sm" />
84+
</div>
85+
</Flex>
86+
);
87+
},
88+
(prevProps, nextProps) => isEqual(prevProps, nextProps),
89+
);

dashboard/src/components/plugins/editor-components/name-color-map-editor/select-palette.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Box, Button, Combobox, Menu } from '@mantine/core';
2-
import _ from 'lodash';
32
import numbro from 'numbro';
3+
import { FieldValues, UseFieldArrayReplace } from 'react-hook-form';
44
import { useTranslation } from 'react-i18next';
55
import { NameColorMapRow } from './types';
66
import { ColorMapPalettes } from './palette';
@@ -11,7 +11,7 @@ function getBackgroundImage(colors: string[]) {
1111
return '';
1212
}
1313
const format: numbro.Format = { output: 'percent', mantissa: 4, trimMantissa: true };
14-
const step = _.divide(1, len);
14+
const step = 1 / len;
1515
const stops: string[] = [];
1616
colors.forEach((c, i) => {
1717
stops.push(`${c} ${numbro(i * step).format(format)}`);
@@ -22,25 +22,25 @@ function getBackgroundImage(colors: string[]) {
2222
}
2323

2424
type Props = {
25-
value: NameColorMapRow[];
26-
onChange: (v: NameColorMapRow[]) => void;
25+
fields: (NameColorMapRow & { id: string })[];
26+
replace: UseFieldArrayReplace<FieldValues, string>;
2727
};
2828

29-
export const SelectPalette = ({ value, onChange }: Props) => {
29+
export const SelectPalette = ({ fields, replace }: Props) => {
3030
const { t } = useTranslation();
3131

3232
const applyPalette = (colors: string[]) => {
33-
const newValue = value.map((v, i) => ({
34-
name: v.name,
35-
color: colors[i],
33+
const newValue = fields.map((f, i) => ({
34+
name: f.name,
35+
color: colors[i] ?? f.color,
3636
}));
37-
for (let j = newValue.length - 1; j < colors.length; j++) {
37+
for (let j = newValue.length; j < colors.length; j++) {
3838
newValue.push({
3939
name: '',
4040
color: colors[j],
4141
});
4242
}
43-
onChange(newValue);
43+
replace(newValue);
4444
};
4545

4646
return (

dashboard/src/components/plugins/editor-components/viz-config-banner.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { ActionIcon, Group, Text } from '@mantine/core';
22
import { IconDeviceFloppy } from '@tabler/icons-react';
3+
import React, { memo } from 'react';
4+
import { Control, useFormState } from 'react-hook-form';
35
import { useTranslation } from 'react-i18next';
46

57
type Props = {
@@ -18,3 +20,13 @@ export function VizConfigBanner({ canSubmit, buttonRef }: Props) {
1820
</Group>
1921
);
2022
}
23+
24+
type FormVizConfigBannerProps = {
25+
control: Control<$TSFixMe>;
26+
buttonRef?: React.RefObject<HTMLButtonElement>;
27+
};
28+
29+
export const FormVizConfigBanner = memo(({ control, buttonRef }: FormVizConfigBannerProps) => {
30+
const { isDirty, isValid } = useFormState({ control });
31+
return <VizConfigBanner canSubmit={isDirty && isValid} buttonRef={buttonRef} />;
32+
});

0 commit comments

Comments
 (0)