fix: Prevent color selector from closing on choosing a color (#2783)

* improv: wrap ColorPicker in memo to prevent unnecessary rerenders

* improv appearance: wrap updateColor in useCallback to prevent unnecessary rerenders due to changing reference to the func

* improv: define ColorCollection Component as top level, and modify it to prevent unnecessary rerenders
This commit is contained in:
Pranav Joglekar 2023-03-09 01:43:57 +05:30 committed by GitHub
parent 108eda0a6a
commit e80db09ab1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,4 +1,4 @@
import React, { FC, useContext, useEffect, useState } from 'react'; import React, { FC, useContext, useCallback, useEffect, useState } from 'react';
import { Button, Col, Collapse, Row, Slider, Space } from 'antd'; import { Button, Col, Collapse, Row, Slider, Space } from 'antd';
import Paragraph from 'antd/lib/typography/Paragraph'; import Paragraph from 'antd/lib/typography/Paragraph';
@ -24,6 +24,11 @@ interface AppearanceVariable {
description: string; description: string;
} }
type ColorCollectionProps = {
variables: { name; description; value }[];
updateColor: (variable: string, color: string, description: string) => void;
};
const chatColorVariables = [ const chatColorVariables = [
{ name: 'theme-color-users-0', description: '' }, { name: 'theme-color-users-0', description: '' },
{ name: 'theme-color-users-1', description: '' }, { name: 'theme-color-users-1', description: '' },
@ -70,18 +75,18 @@ const allAvailableValues = [...componentColorVariables, ...chatColorVariables, .
); );
// eslint-disable-next-line react/function-component-definition // eslint-disable-next-line react/function-component-definition
function ColorPicker({ const ColorPicker = React.memo(
value, ({
name, value,
description, name,
onChange, description,
}: { onChange,
value: string; }: {
name: string; value: string;
description: string; name: string;
onChange: (name: string, value: string, description: string) => void; description: string;
}) { onChange: (name: string, value: string, description: string) => void;
return ( }) => (
<Col span={3} key={name}> <Col span={3} key={name}>
<input <input
type="color" type="color"
@ -94,8 +99,26 @@ function ColorPicker({
/> />
<div style={{ padding: '2px' }}>{description}</div> <div style={{ padding: '2px' }}>{description}</div>
</Col> </Col>
); ),
} );
const ColorCollection: FC<ColorCollectionProps> = ({ variables, updateColor }) => {
const cc = variables.map(colorVar => {
const { name, description, value } = colorVar;
return (
<ColorPicker
key={name}
value={value}
name={name}
description={description}
onChange={updateColor}
/>
);
});
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{cc}</>;
};
// eslint-disable-next-line react/function-component-definition // eslint-disable-next-line react/function-component-definition
export default function Appearance() { export default function Appearance() {
@ -144,12 +167,12 @@ export default function Appearance() {
setCustomValues(c); setCustomValues(c);
}, [appearanceVariables]); }, [appearanceVariables]);
const updateColor = (variable: string, color: string, description: string) => { const updateColor = useCallback((variable: string, color: string, description: string) => {
setCustomValues({ setCustomValues(oldCustomValues => ({
...customValues, ...oldCustomValues,
[variable]: { value: color, description }, [variable]: { value: color, description },
}); }));
}; }, []);
const reset = async () => { const reset = async () => {
await postConfigUpdateToAPI({ await postConfigUpdateToAPI({
@ -193,34 +216,18 @@ export default function Appearance() {
updateColor(variableName, `${value.toString()}px`, ''); updateColor(variableName, `${value.toString()}px`, '');
}; };
type ColorCollectionProps = {
variables: { name; description }[];
};
// eslint-disable-next-line react/no-unstable-nested-components
const ColorCollection: FC<ColorCollectionProps> = ({ variables }) => {
const cc = variables.map(colorVar => {
const source = customValues?.[colorVar.name] ? customValues : defaultValues;
const { name, description } = colorVar;
const { value } = source[name];
return (
<ColorPicker
key={name}
value={value}
name={name}
description={description}
onChange={updateColor}
/>
);
});
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{cc}</>;
};
if (!defaultValues) { if (!defaultValues) {
return <div>Loading...</div>; return <div>Loading...</div>;
} }
const transformToColorMap = variables =>
variables.map(colorVar => {
const source = customValues?.[colorVar.name] ? customValues : defaultValues;
const { name, description } = colorVar;
const { value } = source[name];
return { name, description, value };
});
return ( return (
<Space direction="vertical"> <Space direction="vertical">
<Title>Customize Appearance</Title> <Title>Customize Appearance</Title>
@ -232,15 +239,20 @@ export default function Appearance() {
Certain sections of the interface can be customized by selecting new colors for them. Certain sections of the interface can be customized by selecting new colors for them.
</p> </p>
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<ColorCollection variables={componentColorVariables} /> <ColorCollection
variables={transformToColorMap(componentColorVariables)}
updateColor={updateColor}
/>
</Row> </Row>
</Panel> </Panel>
<Panel header={<Title level={3}>Chat User Colors</Title>} key="2"> <Panel header={<Title level={3}>Chat User Colors</Title>} key="2">
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<ColorCollection variables={chatColorVariables} /> <ColorCollection
variables={transformToColorMap(chatColorVariables)}
updateColor={updateColor}
/>
</Row> </Row>
</Panel> </Panel>
<Panel header={<Title level={3}>Other Settings</Title>} key="4"> <Panel header={<Title level={3}>Other Settings</Title>} key="4">
How rounded should corners be? How rounded should corners be?
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>