diff --git a/api/package.json b/api/package.json index e5194535f..756ce9012 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "@devtable/api", - "version": "14.60.0", + "version": "14.60.1", "description": "", "main": "index.js", "scripts": { diff --git a/dashboard/package.json b/dashboard/package.json index 0e14c76fc..0c23fd457 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -1,6 +1,6 @@ { "name": "@devtable/dashboard", - "version": "14.60.0", + "version": "14.60.1", "license": "Apache-2.0", "repository": { "url": "https://github.com/merico-dev/table" @@ -44,7 +44,7 @@ }, "dependencies": { "@dnd-kit/core": "^6.1.0", - "@dnd-kit/helpers": "^0.0.3", + "@dnd-kit/helpers": "0.0.4", "@dnd-kit/react": "^0.0.4", "@dnd-kit/sortable": "^8.0.0", "@emotion/cache": "^11.11.0", diff --git a/dashboard/src/components/plugins/editor-components/name-color-map-editor/name-color-map-editor.tsx b/dashboard/src/components/plugins/editor-components/name-color-map-editor/name-color-map-editor.tsx index b697a6cec..10297cba1 100644 --- a/dashboard/src/components/plugins/editor-components/name-color-map-editor/name-color-map-editor.tsx +++ b/dashboard/src/components/plugins/editor-components/name-color-map-editor/name-color-map-editor.tsx @@ -1,72 +1,58 @@ import { move } from '@dnd-kit/helpers'; import { DragDropProvider } from '@dnd-kit/react'; import { Group, Stack, Text } from '@mantine/core'; -import _ from 'lodash'; -import { forwardRef, useMemo } from 'react'; +import { ComponentProps, forwardRef, memo } from 'react'; +import { Control, FieldValues, useFieldArray } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import { v4 as uuidv4 } from 'uuid'; import { NameColorMapRow } from './types'; import { RowEditor } from './row-editor'; import { AddARow } from './add-a-row'; import { SelectPalette } from './select-palette'; +type FieldWithId = NameColorMapRow & { id: string }; + type Props = { - value: NameColorMapRow[]; - onChange: (v: NameColorMapRow[]) => void; - zIndex?: number; + control: Control<$TSFixMe>; + name: string; names: string[]; }; -export const NameColorMapEditor = forwardRef(({ value, onChange, zIndex = 340, names }, ref) => { - const { t } = useTranslation(); - const rows = useMemo(() => { - return value.map((r) => ({ - id: uuidv4(), - ...r, - })); - }, [value]); +export const NameColorMapEditor = memo( + forwardRef(({ control, name, names }, ref) => { + const { t } = useTranslation(); + const { fields, append, remove, update, replace } = useFieldArray({ + control, + name, + }); + + const typedFields = fields as FieldWithId[]; - const append = (v: NameColorMapRow) => { - onChange([...value, v]); - }; - const remove = (index: number) => { - const newValue = [...value]; - newValue.splice(index, 1); - onChange(newValue); - }; - const getChangeHandler = (index: number) => (v: NameColorMapRow) => { - const newValue = [...value]; - newValue[index] = v; - onChange(newValue); - }; + const handleAppend = (v: NameColorMapRow) => { + append(v); + }; - const onDragEnd = (event: any) => { - const { source, target } = event.operation; - const newRows = move(rows, source, target); - onChange(newRows.map((v) => _.omit(v, 'id'))); - }; + const onDragEnd: ComponentProps['onDragEnd'] = (event) => { + const { source, target } = event.operation; + if (!source || !target) return; + const newFields = move(typedFields, source, target); + replace(newFields.map((f) => ({ name: f.name, color: f.color }))); + }; - return ( - - - - {t('viz.pie_chart.color.map.label')} - - - - - {rows.map((r, index) => ( - remove(index)} - index={index} - names={names} - /> - ))} - - - - ); -}); + return ( + + + + {t('viz.pie_chart.color.map.label')} + + + + + {typedFields.map((field, index) => ( + + ))} + + + + ); + }), +); diff --git a/dashboard/src/components/plugins/editor-components/name-color-map-editor/row-editor.tsx b/dashboard/src/components/plugins/editor-components/name-color-map-editor/row-editor.tsx index de307bb04..d75022f0e 100644 --- a/dashboard/src/components/plugins/editor-components/name-color-map-editor/row-editor.tsx +++ b/dashboard/src/components/plugins/editor-components/name-color-map-editor/row-editor.tsx @@ -1,98 +1,89 @@ import { useSortable } from '@dnd-kit/react/sortable'; -import { ActionIcon, Autocomplete, Badge, Center, CloseButton, ColorInput, Flex, Group } from '@mantine/core'; +import { ActionIcon, Autocomplete, Badge, Center, CloseButton, Flex, Group } from '@mantine/core'; +import { ColorPickerPopoverForViz } from '~/components/widgets/color-picker-popover/color-picker-popover-for-viz'; import { IconGripVertical } from '@tabler/icons-react'; import { useBoolean } from 'ahooks'; +import { isEqual } from 'lodash'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { NameColorMapRow } from './types'; -type RowFieldItem = { - id: string; -} & NameColorMapRow; - type NameColorMapRowProps = { - row: RowFieldItem; - handleChange: (v: RowFieldItem) => void; - handleRemove: () => void; + field: NameColorMapRow & { id: string }; + update: (index: number, data: NameColorMapRow) => void; + remove: (index: number) => void; index: number; names: string[]; }; -export const RowEditor = ({ row, index, handleChange, handleRemove, names }: NameColorMapRowProps) => { - const { t } = useTranslation(); - const [touched, { setTrue: setTouched }] = useBoolean(false); - const [hovering, { setTrue, setFalse }] = useBoolean(false); - const { ref, handleRef } = useSortable({ - id: row.id, - index, - }); - - const changeName = (name: string) => { - handleChange({ - ...row, - name, +export const RowEditor = memo( + ({ field, index, update, remove, names }: NameColorMapRowProps) => { + const { t } = useTranslation(); + const [touched, { setTrue: setTouched }] = useBoolean(false); + const [hovering, { setTrue, setFalse }] = useBoolean(false); + const { ref, handleRef } = useSortable({ + id: field.id, + index, }); - }; - const changeColor = (color: string) => { - handleChange({ - ...row, - color, - }); - }; + const changeName = (name: string) => { + update(index, { + name, + color: field.color, + }); + }; - return ( - -
- {hovering ? ( - - - - ) : ( - - {index + 1} - - )} -
- - - - -
- -
-
- ); -}; + const changeColor = (color: string) => { + update(index, { + name: field.name, + color, + }); + }; + + return ( + +
+ {hovering ? ( + + + + ) : ( + + {index + 1} + + )} +
+ + + + +
+ remove(index)} size="sm" /> +
+
+ ); + }, + (prevProps, nextProps) => isEqual(prevProps, nextProps), +); diff --git a/dashboard/src/components/plugins/editor-components/name-color-map-editor/select-palette.tsx b/dashboard/src/components/plugins/editor-components/name-color-map-editor/select-palette.tsx index 121c7d357..c340f8757 100644 --- a/dashboard/src/components/plugins/editor-components/name-color-map-editor/select-palette.tsx +++ b/dashboard/src/components/plugins/editor-components/name-color-map-editor/select-palette.tsx @@ -1,6 +1,6 @@ import { Box, Button, Combobox, Menu } from '@mantine/core'; -import _ from 'lodash'; import numbro from 'numbro'; +import { FieldValues, UseFieldArrayReplace } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { NameColorMapRow } from './types'; import { ColorMapPalettes } from './palette'; @@ -11,7 +11,7 @@ function getBackgroundImage(colors: string[]) { return ''; } const format: numbro.Format = { output: 'percent', mantissa: 4, trimMantissa: true }; - const step = _.divide(1, len); + const step = 1 / len; const stops: string[] = []; colors.forEach((c, i) => { stops.push(`${c} ${numbro(i * step).format(format)}`); @@ -22,25 +22,25 @@ function getBackgroundImage(colors: string[]) { } type Props = { - value: NameColorMapRow[]; - onChange: (v: NameColorMapRow[]) => void; + fields: (NameColorMapRow & { id: string })[]; + replace: UseFieldArrayReplace; }; -export const SelectPalette = ({ value, onChange }: Props) => { +export const SelectPalette = ({ fields, replace }: Props) => { const { t } = useTranslation(); const applyPalette = (colors: string[]) => { - const newValue = value.map((v, i) => ({ - name: v.name, - color: colors[i], + const newValue = fields.map((f, i) => ({ + name: f.name, + color: colors[i] ?? f.color, })); - for (let j = newValue.length - 1; j < colors.length; j++) { + for (let j = newValue.length; j < colors.length; j++) { newValue.push({ name: '', color: colors[j], }); } - onChange(newValue); + replace(newValue); }; return ( diff --git a/dashboard/src/components/plugins/editor-components/viz-config-banner.tsx b/dashboard/src/components/plugins/editor-components/viz-config-banner.tsx index ba591e324..be8b280b4 100644 --- a/dashboard/src/components/plugins/editor-components/viz-config-banner.tsx +++ b/dashboard/src/components/plugins/editor-components/viz-config-banner.tsx @@ -1,5 +1,7 @@ import { ActionIcon, Group, Text } from '@mantine/core'; import { IconDeviceFloppy } from '@tabler/icons-react'; +import React, { memo } from 'react'; +import { Control, useFormState } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; type Props = { @@ -18,3 +20,13 @@ export function VizConfigBanner({ canSubmit, buttonRef }: Props) { ); } + +type FormVizConfigBannerProps = { + control: Control<$TSFixMe>; + buttonRef?: React.RefObject; +}; + +export const FormVizConfigBanner = memo(({ control, buttonRef }: FormVizConfigBannerProps) => { + const { isDirty, isValid } = useFormState({ control }); + return ; +}); diff --git a/dashboard/src/components/plugins/viz-components/pie-chart/translation.ts b/dashboard/src/components/plugins/viz-components/pie-chart/translation.ts index 6f880f00b..f6994fb81 100644 --- a/dashboard/src/components/plugins/viz-components/pie-chart/translation.ts +++ b/dashboard/src/components/plugins/viz-components/pie-chart/translation.ts @@ -15,6 +15,7 @@ const en = { map: { label: 'Color Map', name: 'Record name', + color: 'Color', add_a_row: 'Add a mapping rule', use_a_palette: 'Use a palette', click_to_apply_palette: 'Click to apply palette', @@ -43,6 +44,7 @@ const zh = { map: { label: '颜色映射表', name: '数据名称', + color: '颜色', add_a_row: '增加一条映射规则', use_a_palette: '使用预设配色方案', click_to_apply_palette: '点击应用配色方案', diff --git a/dashboard/src/components/plugins/viz-components/pie-chart/viz-pie-chart-editor.tsx b/dashboard/src/components/plugins/viz-components/pie-chart/viz-pie-chart-editor.tsx index ee3211f53..ba785c700 100644 --- a/dashboard/src/components/plugins/viz-components/pie-chart/viz-pie-chart-editor.tsx +++ b/dashboard/src/components/plugins/viz-components/pie-chart/viz-pie-chart-editor.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'; import { DataFieldSelector } from '~/components/panel/settings/common/data-field-selector'; import { useStorageData } from '~/components/plugins/hooks'; import { VizConfigProps } from '~/types/plugin'; -import { FieldArrayTabs, NameColorMapEditor, VizConfigBanner } from '../../editor-components'; +import { FieldArrayTabs, FormVizConfigBanner, NameColorMapEditor } from '../../editor-components'; import { RadiusSlider } from './editors'; import { IPieChartConf } from './type'; import { useEditPanelContext } from '~/contexts'; @@ -26,7 +26,7 @@ function Editor({ conf, setConf, context }: EditorProps) { const { t } = useTranslation(); const defaultValues: IPieChartConf = useMemo(() => defaults({}, conf), [conf]); - const { control, handleSubmit, watch, formState, reset } = useForm({ defaultValues }); + const { control, handleSubmit, watch, reset } = useForm({ defaultValues }); useEffect(() => { reset(defaultValues); }, [defaultValues]); @@ -45,7 +45,7 @@ function Editor({ conf, setConf, context }: EditorProps) { return (
- + } /> - } - /> +
diff --git a/dashboard/src/components/plugins/viz-components/radar-chart/editors/dimensions/index.tsx b/dashboard/src/components/plugins/viz-components/radar-chart/editors/dimensions/index.tsx index 044add890..289625136 100644 --- a/dashboard/src/components/plugins/viz-components/radar-chart/editors/dimensions/index.tsx +++ b/dashboard/src/components/plugins/viz-components/radar-chart/editors/dimensions/index.tsx @@ -1,3 +1,4 @@ +import { memo } from 'react'; import { Control, UseFormWatch } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { FieldArrayTabs } from '~/components/plugins/editor-components'; @@ -10,7 +11,7 @@ interface IDimensionsField { watch: UseFormWatch; } -export function DimensionsField({ control, watch }: IDimensionsField) { +export const DimensionsField = memo(function DimensionsField({ control, watch }: IDimensionsField) { const { t } = useTranslation(); const getItem = () => { @@ -45,4 +46,4 @@ export function DimensionsField({ control, watch }: IDimensionsField) { ); -} +}); diff --git a/dashboard/src/components/plugins/viz-components/radar-chart/editors/series-style-field.tsx b/dashboard/src/components/plugins/viz-components/radar-chart/editors/series-style-field.tsx index ea7cfd6ab..6cca0acf6 100644 --- a/dashboard/src/components/plugins/viz-components/radar-chart/editors/series-style-field.tsx +++ b/dashboard/src/components/plugins/viz-components/radar-chart/editors/series-style-field.tsx @@ -1,4 +1,5 @@ import { Divider, Group, NumberInput, Stack } from '@mantine/core'; +import { memo } from 'react'; import { Control, Controller } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { LineTypeSelector } from '~/components/plugins/common-echarts-fields/line-type'; @@ -9,7 +10,7 @@ type Props = { path: 'main_series_style'; }; -export function RadarSeriesStyleField({ control, path }: Props) { +export const RadarSeriesStyleField = memo(function RadarSeriesStyleField({ control, path }: Props) { const { t } = useTranslation(); return ( @@ -46,4 +47,4 @@ export function RadarSeriesStyleField({ control, path }: Props) { /> ); -} +}); diff --git a/dashboard/src/components/plugins/viz-components/radar-chart/viz-radar-chart-editor.tsx b/dashboard/src/components/plugins/viz-components/radar-chart/viz-radar-chart-editor.tsx index 8137979a9..eb0424efd 100644 --- a/dashboard/src/components/plugins/viz-components/radar-chart/viz-radar-chart-editor.tsx +++ b/dashboard/src/components/plugins/viz-components/radar-chart/viz-radar-chart-editor.tsx @@ -10,7 +10,7 @@ import { extractData } from '~/utils'; import { useTranslation } from 'react-i18next'; import { useStorageData } from '~/components/plugins/hooks'; import { VizConfigProps } from '~/types/plugin'; -import { NameColorMapEditor, VizConfigBanner } from '../../editor-components'; +import { FormVizConfigBanner, NameColorMapEditor } from '../../editor-components'; import { AdditionalSeriesField } from './editors/additional-series'; import { DimensionsField } from './editors/dimensions'; import { RadarSeriesStyleField } from './editors/series-style-field'; @@ -22,7 +22,7 @@ export function VizRadarChartEditor({ context }: VizConfigProps) { const { variables } = context; const conf: IRadarChartConf = useMemo(() => defaultsDeep({}, confValue, DEFAULT_CONFIG), [confValue]); - const { control, handleSubmit, watch, reset, formState } = useForm({ + const { control, handleSubmit, watch, reset } = useForm({ defaultValues: conf, mode: 'all', }); @@ -44,7 +44,7 @@ export function VizRadarChartEditor({ context }: VizConfigProps) { return (
- + {t('chart.series.label')} @@ -109,11 +109,7 @@ export function VizRadarChartEditor({ context }: VizConfigProps) { )} /> - } - /> + diff --git a/dashboard/src/components/widgets/color-picker-popover/color-picker-popover.tsx b/dashboard/src/components/widgets/color-picker-popover/color-picker-popover.tsx index 54a3d77f7..0ed7ae46e 100644 --- a/dashboard/src/components/widgets/color-picker-popover/color-picker-popover.tsx +++ b/dashboard/src/components/widgets/color-picker-popover/color-picker-popover.tsx @@ -1,8 +1,7 @@ import { ActionIcon, ColorPicker, Group, Popover, SimpleGrid, Stack, useMantineTheme } from '@mantine/core'; -import { RichTextEditor } from '@mantine/tiptap'; import { IconCircleOff, IconX } from '@tabler/icons-react'; import { useBoolean } from 'ahooks'; -import { ReactNode, useCallback, useMemo, useState } from 'react'; +import { ReactNode, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ColorInput } from './color-input'; @@ -58,15 +57,15 @@ export const ColorPickerPopover = ({ Trigger, value, onChange, clear }: Props) = shouldClose && close(); }; - const handleColorInputChange = useCallback((value: string) => { + const handleColorInputChange = (value: string) => { setColor(value, false); setShouldUpdateInput(false); - }, []); + }; - const handleColorPickerChange = useCallback((value: string) => { + const handleColorPickerChange = (value: string) => { setColor(value, false); setShouldUpdateInput(true); - }, []); + }; const unsetColor = () => { clear(); diff --git a/settings-form/package.json b/settings-form/package.json index eaf52912b..2eb2c2f7e 100644 --- a/settings-form/package.json +++ b/settings-form/package.json @@ -1,6 +1,6 @@ { "name": "@devtable/settings-form", - "version": "14.60.0", + "version": "14.60.1", "license": "Apache-2.0", "repository": { "url": "https://github.com/merico-dev/table" diff --git a/yarn.lock b/yarn.lock index 4f245f55a..d569a5b88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1918,7 +1918,7 @@ __metadata: resolution: "@devtable/dashboard@workspace:dashboard" dependencies: "@dnd-kit/core": "npm:^6.1.0" - "@dnd-kit/helpers": "npm:^0.0.3" + "@dnd-kit/helpers": "npm:0.0.4" "@dnd-kit/react": "npm:^0.0.4" "@dnd-kit/sortable": "npm:^8.0.0" "@emotion/babel-plugin": "npm:^11.11.0" @@ -2191,17 +2191,6 @@ __metadata: languageName: unknown linkType: soft -"@dnd-kit/abstract@npm:^0.0.3": - version: 0.0.3 - resolution: "@dnd-kit/abstract@npm:0.0.3" - dependencies: - "@dnd-kit/geometry": "npm:^0.0.3" - "@dnd-kit/state": "npm:^0.0.3" - tslib: "npm:^2.6.2" - checksum: 10/8318a7a84c5b0768128146c534e27016555a5d70ea3a22aa69165f8a5089fa8de19de1a749c9b620a295e609352c129e4abf13ec6cbc4541f0e233c0fcbdc984 - languageName: node - linkType: hard - "@dnd-kit/abstract@npm:^0.0.4": version: 0.0.4 resolution: "@dnd-kit/abstract@npm:0.0.4" @@ -2262,16 +2251,6 @@ __metadata: languageName: node linkType: hard -"@dnd-kit/geometry@npm:^0.0.3": - version: 0.0.3 - resolution: "@dnd-kit/geometry@npm:0.0.3" - dependencies: - "@dnd-kit/state": "npm:^0.0.3" - tslib: "npm:^2.6.2" - checksum: 10/edb507ede4ded2ccf3efbb24f6789ab7697a234edb57f727db50af2b6cf167e63d05b026e6953ea8d72583f208fcad382008a14f1b62306bc3fc5d4a58d2c7f2 - languageName: node - linkType: hard - "@dnd-kit/geometry@npm:^0.0.4": version: 0.0.4 resolution: "@dnd-kit/geometry@npm:0.0.4" @@ -2282,13 +2261,13 @@ __metadata: languageName: node linkType: hard -"@dnd-kit/helpers@npm:^0.0.3": - version: 0.0.3 - resolution: "@dnd-kit/helpers@npm:0.0.3" +"@dnd-kit/helpers@npm:0.0.4": + version: 0.0.4 + resolution: "@dnd-kit/helpers@npm:0.0.4" dependencies: - "@dnd-kit/abstract": "npm:^0.0.3" + "@dnd-kit/abstract": "npm:^0.0.4" tslib: "npm:^2.6.2" - checksum: 10/d1884482b9260bbb6a1dba0967bd3c9c2859e861822596cb00f92c4b06a089fe60b4d30f3baeca6bdf4b0038771ff455dcf102dd39039391678a70ccb57f877a + checksum: 10/65d4bb1f10487b4b3dd5246a99a94f8a8176e8f96cfe361749179cbd1876364323f00f663d8403d143a82bcf1da4ba6cd2999916f927ad969ac1df5ddb8a4726 languageName: node linkType: hard @@ -2320,16 +2299,6 @@ __metadata: languageName: node linkType: hard -"@dnd-kit/state@npm:^0.0.3": - version: 0.0.3 - resolution: "@dnd-kit/state@npm:0.0.3" - dependencies: - "@preact/signals-core": "npm:^1.6.0" - tslib: "npm:^2.6.2" - checksum: 10/f3dc265c87ff6ea787989fc6f8c9dbf43fdbd7a4b4c7f859181256a79eb27dc7dbac581f78e23d2f856f211ac542a4cd3060970b6b607890f8c502d258cb75c6 - languageName: node - linkType: hard - "@dnd-kit/state@npm:^0.0.4": version: 0.0.4 resolution: "@dnd-kit/state@npm:0.0.4"