Components & hooks: optimize
This commit is contained in:
parent
2267ce36ad
commit
962ed35352
@ -36,6 +36,17 @@
|
||||
"lines-between-class-members": "off",
|
||||
"@typescript-eslint/lines-between-class-members": ["off"],
|
||||
"indent": "off",
|
||||
"@typescript-eslint/indent": ["off"]
|
||||
"@typescript-eslint/indent": ["off"],
|
||||
"react/jsx-no-bind": [
|
||||
"error",
|
||||
{
|
||||
"ignoreDOMComponents": false,
|
||||
"ignoreRefs": false,
|
||||
"allowArrowFunctions": false,
|
||||
"allowFunctions": true,
|
||||
"allowBind": false
|
||||
}
|
||||
],
|
||||
"newline-before-return": "warn"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useStatus } from 'stores/calculation/statuses/hooks';
|
||||
import { getAction } from '../config/map-actions';
|
||||
@ -7,7 +8,7 @@ export default function buildAction({ elementName, Component, ...props }: Builde
|
||||
const actionName = getAction(elementName);
|
||||
|
||||
return observer(() => {
|
||||
const { status } = useStatus(elementName);
|
||||
const status = useStatus(elementName);
|
||||
|
||||
return (
|
||||
<Component
|
||||
|
||||
@ -8,8 +8,8 @@ export default function buildComputedValue({ elementName, Component, ...props }:
|
||||
const computedValueName = getComputedValueName(elementName);
|
||||
|
||||
return observer(() => {
|
||||
const { computedValue } = useComputedValue(computedValueName);
|
||||
const { status } = useStatus(elementName);
|
||||
const computedValue = useComputedValue(computedValueName);
|
||||
const status = useStatus(elementName);
|
||||
|
||||
return <Component value={computedValue} status={status} {...props} />;
|
||||
});
|
||||
|
||||
@ -2,7 +2,7 @@ import { observer } from 'mobx-react-lite';
|
||||
import { useOptions } from 'stores/calculation/options/hooks';
|
||||
import { useStatus } from 'stores/calculation/statuses/hooks';
|
||||
import { useValidation } from 'stores/calculation/validation/hooks';
|
||||
import { useValue } from 'stores/calculation/values/hooks';
|
||||
import { useSetValue, useValue } from 'stores/calculation/values/hooks';
|
||||
import { getValueName } from '../config/map-values';
|
||||
import type { BuilderProps } from './types';
|
||||
|
||||
@ -10,10 +10,11 @@ export default function buildOptions({ elementName, Component, ...props }: Build
|
||||
const valueName = getValueName(elementName);
|
||||
|
||||
return observer(() => {
|
||||
const { value, setValue } = useValue(valueName);
|
||||
const { status } = useStatus(elementName);
|
||||
const value = useValue(valueName);
|
||||
const setValue = useSetValue(valueName);
|
||||
const status = useStatus(elementName);
|
||||
const { isValid, help } = useValidation(elementName);
|
||||
const { options } = useOptions(elementName);
|
||||
const options = useOptions(elementName);
|
||||
|
||||
return (
|
||||
<Component
|
||||
|
||||
@ -7,8 +7,8 @@ export default function buildReadonly({ elementName, Component, ...props }: Buil
|
||||
const valueName = getValueName(elementName);
|
||||
|
||||
return observer(() => {
|
||||
const { value } = useValue(valueName);
|
||||
const value = useValue(valueName);
|
||||
|
||||
return <Component value={value} {...props} />;
|
||||
return <Component value={value} readonly {...props} />;
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useStatus } from 'stores/calculation/statuses/hooks';
|
||||
import { useValidation } from 'stores/calculation/validation/hooks';
|
||||
import { useValue } from 'stores/calculation/values/hooks';
|
||||
import { useSetValue } from 'stores/calculation/values/hooks';
|
||||
import { getValueName } from '../config/map-values';
|
||||
import type { BuilderProps } from './types';
|
||||
|
||||
@ -9,19 +9,12 @@ export default function buildValue({ elementName, Component, ...props }: Builder
|
||||
const valueName = getValueName(elementName);
|
||||
|
||||
return observer(() => {
|
||||
const { value, setValue } = useValue(valueName);
|
||||
const { status } = useStatus(elementName);
|
||||
const setValue = useSetValue(valueName);
|
||||
const status = useStatus(elementName);
|
||||
const { isValid, help } = useValidation(elementName);
|
||||
|
||||
return (
|
||||
<Component
|
||||
value={value}
|
||||
setValue={setValue}
|
||||
status={status}
|
||||
isValid={isValid}
|
||||
help={help}
|
||||
{...props}
|
||||
/>
|
||||
<Component setValue={setValue} status={status} isValid={isValid} help={help} {...props} />
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { ButtonProps } from 'antd';
|
||||
import { Button } from 'antd';
|
||||
import { Button as AntButton } from 'antd';
|
||||
import { throttle } from 'lodash';
|
||||
import type { Status } from './types';
|
||||
|
||||
@ -9,19 +9,19 @@ type ElementProps = {
|
||||
text: string;
|
||||
};
|
||||
|
||||
export default function Element({ status, action, text }: ElementProps) {
|
||||
export default function Button({ status, action, text }: ElementProps) {
|
||||
const throttledAction = throttle(action, 1200, {
|
||||
trailing: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<Button
|
||||
<AntButton
|
||||
disabled={status === 'Disabled'}
|
||||
loading={status === 'Loading'}
|
||||
onClick={throttledAction}
|
||||
>
|
||||
{text}
|
||||
</Button>
|
||||
</AntButton>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import type { CheckboxProps } from 'antd';
|
||||
import { Checkbox, Form } from 'antd';
|
||||
import { Checkbox as AntCheckbox, Form } from 'antd';
|
||||
import type { CheckboxChangeEvent } from 'antd/lib/checkbox';
|
||||
import type { BaseElementProps } from './types';
|
||||
|
||||
const { Item: FormItem } = Form;
|
||||
|
||||
export default function Element({
|
||||
export default function Checkbox({
|
||||
value,
|
||||
setValue,
|
||||
status,
|
||||
@ -12,11 +13,15 @@ export default function Element({
|
||||
help,
|
||||
...props
|
||||
}: BaseElementProps<boolean>) {
|
||||
function handleChange(e: CheckboxChangeEvent) {
|
||||
setValue(e.target.checked);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
|
||||
<Checkbox
|
||||
<AntCheckbox
|
||||
checked={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
onChange={handleChange}
|
||||
disabled={status === 'Disabled'}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import type { InputProps } from 'antd';
|
||||
import { Form, Input } from 'antd';
|
||||
import { Form, Input as AntInput } from 'antd';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import type { BaseElementProps } from './types';
|
||||
|
||||
const { Item: FormItem } = Form;
|
||||
|
||||
export default function Element({
|
||||
export default function Input({
|
||||
value,
|
||||
setValue,
|
||||
status,
|
||||
@ -12,14 +13,13 @@ export default function Element({
|
||||
help,
|
||||
...props
|
||||
}: BaseElementProps<string>) {
|
||||
function handleChange(e: ChangeEvent<HTMLInputElement>) {
|
||||
setValue(e.target.value);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
|
||||
<Input
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
disabled={status === 'Disabled'}
|
||||
{...props}
|
||||
/>
|
||||
<AntInput value={value} onChange={handleChange} disabled={status === 'Disabled'} {...props} />
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,17 +1,10 @@
|
||||
import type { InputNumberProps } from 'antd';
|
||||
import { Form, InputNumber } from 'antd';
|
||||
import { Form, InputNumber as AntInputNumber } from 'antd';
|
||||
import type { BaseElementProps } from './types';
|
||||
|
||||
const { Item: FormItem } = Form;
|
||||
|
||||
const parser = (val?: string): number => {
|
||||
const res = val?.replace(/[^0-9.,]+/, '');
|
||||
if (!res || res === '') return 0;
|
||||
return parseFloat(res);
|
||||
};
|
||||
|
||||
export default function Element({
|
||||
value = 0,
|
||||
export default function InputNumber({
|
||||
setValue,
|
||||
status,
|
||||
isValid,
|
||||
@ -20,11 +13,10 @@ export default function Element({
|
||||
}: BaseElementProps<number>) {
|
||||
return (
|
||||
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
|
||||
<InputNumber
|
||||
value={value}
|
||||
onChange={(val) => setValue(val)}
|
||||
<AntInputNumber
|
||||
type="number"
|
||||
onChange={setValue}
|
||||
disabled={status === 'Disabled'}
|
||||
parser={parser}
|
||||
{...props}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import DownloadOutlined from '@ant-design/icons/lib/icons/DownloadOutlined';
|
||||
import type { ButtonProps } from 'antd';
|
||||
import { Button } from 'antd';
|
||||
import { Button as AntButton } from 'antd';
|
||||
import type { BaseElementProps } from './types';
|
||||
|
||||
export default function Element({ value, setValue, status, ...props }: BaseElementProps<string>) {
|
||||
export default function Link({ value, setValue, status, ...props }: BaseElementProps<string>) {
|
||||
return (
|
||||
<Button
|
||||
<AntButton
|
||||
rel="noopener"
|
||||
target="_blank"
|
||||
href={value}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { RadioProps } from 'antd';
|
||||
import { Form, Radio } from 'antd';
|
||||
import type { RadioChangeEvent, RadioProps } from 'antd';
|
||||
import { Form, Radio as AntRadio } from 'antd';
|
||||
import type { BaseElementProps, BaseOption } from './types';
|
||||
|
||||
const { Item: FormItem } = Form;
|
||||
@ -8,7 +8,7 @@ type ElementProps = BaseElementProps<string | null> & {
|
||||
options: BaseOption[];
|
||||
};
|
||||
|
||||
export default function Element({
|
||||
export default function Radio({
|
||||
value = null,
|
||||
setValue,
|
||||
options,
|
||||
@ -17,12 +17,16 @@ export default function Element({
|
||||
help,
|
||||
...props
|
||||
}: ElementProps) {
|
||||
function handleChange(e: RadioChangeEvent) {
|
||||
setValue(e.target.value);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
|
||||
<Radio.Group
|
||||
<AntRadio.Group
|
||||
value={value}
|
||||
options={options}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
onChange={handleChange}
|
||||
disabled={status === 'Disabled'}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import type { SelectProps } from 'antd';
|
||||
import { Form, Select } from 'antd';
|
||||
import { Form, Select as AntSelect } from 'antd';
|
||||
import { useMemo } from 'react';
|
||||
import type { BaseElementProps, BaseOption } from './types';
|
||||
|
||||
const { Item: FormItem } = Form;
|
||||
@ -8,7 +9,7 @@ type ElementProps = BaseElementProps<string | null> & {
|
||||
options: BaseOption[];
|
||||
};
|
||||
|
||||
export default function Element({
|
||||
export default function Select({
|
||||
value = null,
|
||||
setValue,
|
||||
options,
|
||||
@ -17,20 +18,26 @@ export default function Element({
|
||||
help,
|
||||
...props
|
||||
}: ElementProps) {
|
||||
const optionsWithNull = useMemo(
|
||||
() => [
|
||||
{
|
||||
label: 'Не выбрано',
|
||||
value: null,
|
||||
},
|
||||
...options,
|
||||
],
|
||||
[options]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
|
||||
<Select
|
||||
<AntSelect
|
||||
value={value}
|
||||
onChange={(val) => setValue(val)}
|
||||
onChange={setValue}
|
||||
disabled={status === 'Disabled'}
|
||||
loading={status === 'Loading'}
|
||||
optionFilterProp="children"
|
||||
options={[
|
||||
{
|
||||
label: 'Не выбрано',
|
||||
value: null,
|
||||
},
|
||||
].concat(options)}
|
||||
options={optionsWithNull}
|
||||
{...props}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import type { SwitchProps } from 'antd';
|
||||
import { Form, Switch } from 'antd';
|
||||
import { Form, Switch as AntSwitch } from 'antd';
|
||||
import type { BaseElementProps } from './types';
|
||||
|
||||
const { Item: FormItem } = Form;
|
||||
|
||||
export default function Element({
|
||||
export default function Switch({
|
||||
value,
|
||||
setValue,
|
||||
status,
|
||||
@ -14,12 +14,7 @@ export default function Element({
|
||||
}: BaseElementProps<boolean>) {
|
||||
return (
|
||||
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
|
||||
<Switch
|
||||
checked={value}
|
||||
onChange={(enabled) => setValue(enabled)}
|
||||
disabled={status === 'Disabled'}
|
||||
{...props}
|
||||
/>
|
||||
<AntSwitch checked={value} onChange={setValue} disabled={status === 'Disabled'} {...props} />
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import styled from 'styled-components';
|
||||
import type { BaseElementProps } from './types';
|
||||
|
||||
const Text = styled.span`
|
||||
const Span = styled.span`
|
||||
margin-bottom: 18px;
|
||||
font-size: 0.85rem;
|
||||
`;
|
||||
|
||||
export default function Element({ value, ...props }: BaseElementProps<string>) {
|
||||
return <Text {...props}>{value}</Text>;
|
||||
export default function Text({ value, ...props }: BaseElementProps<string>) {
|
||||
return <Span {...props}>{value}</Span>;
|
||||
}
|
||||
|
||||
type TextProps = {
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { computed } from 'mobx';
|
||||
import { useMemo } from 'react';
|
||||
import { useStore } from 'stores/hooks';
|
||||
|
||||
export function useOptions(elementName) {
|
||||
const { $calculation } = useStore();
|
||||
const options = $calculation.$options.getOptions(elementName);
|
||||
|
||||
const options = useMemo(
|
||||
() => computed(() => $calculation.$options.getOptions(elementName)),
|
||||
[$calculation.$options, elementName]
|
||||
);
|
||||
|
||||
return {
|
||||
options: options.get(),
|
||||
};
|
||||
return options;
|
||||
}
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { computed } from 'mobx';
|
||||
import { useMemo } from 'react';
|
||||
import { useStore } from 'stores/hooks';
|
||||
|
||||
export function useStatus(elementName) {
|
||||
const { $calculation } = useStore();
|
||||
const status = $calculation.$status.getStatus(elementName);
|
||||
|
||||
const status = useMemo(
|
||||
() => computed(() => $calculation.$status.getStatus(elementName)),
|
||||
[$calculation.$status, elementName]
|
||||
);
|
||||
|
||||
return {
|
||||
status: status.get(),
|
||||
};
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1,18 +1,12 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { computed } from 'mobx';
|
||||
import { useMemo } from 'react';
|
||||
import { useStore } from 'stores/hooks';
|
||||
|
||||
export function useValidation(elementName) {
|
||||
const { $calculation } = useStore();
|
||||
|
||||
const validationResult = useMemo(
|
||||
() => computed(() => $calculation.$validation.getValidation(elementName)),
|
||||
[$calculation.$validation, elementName]
|
||||
).get();
|
||||
const validationResult = $calculation.$validation.getValidation(elementName);
|
||||
|
||||
return {
|
||||
...validationResult,
|
||||
help: validationResult.isValid === false ? 'Некорректные данные' : null,
|
||||
help: validationResult?.isValid === false ? 'Некорректные данные' : null,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,62 +1,30 @@
|
||||
import { computed } from 'mobx';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useStore } from 'stores/hooks';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
const WAIT = 350;
|
||||
const MAX_WAIT = 1500;
|
||||
|
||||
export function useValue(valueName) {
|
||||
const [value, setValue] = useState();
|
||||
const { $calculation } = useStore();
|
||||
const value = $calculation.$values.getValue(valueName);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function useSetValue(valueName) {
|
||||
const { $calculation } = useStore();
|
||||
|
||||
const setStoreValue = useCallback(
|
||||
(val) => $calculation.$values.setValue(valueName, val),
|
||||
[$calculation.$values, valueName]
|
||||
);
|
||||
const debouncedSetStoreValue = useDebouncedCallback(setStoreValue, WAIT, {
|
||||
maxWait: MAX_WAIT,
|
||||
});
|
||||
|
||||
/** Set local value to global store */
|
||||
useEffect(() => {
|
||||
debouncedSetStoreValue(value);
|
||||
}, [debouncedSetStoreValue, value]);
|
||||
|
||||
const storeValue = useMemo(
|
||||
() => computed(() => $calculation.$values.getValue(valueName)),
|
||||
[$calculation.$values, valueName]
|
||||
).get();
|
||||
|
||||
/** Set initial value from global store to local state (only once) */
|
||||
useEffect(
|
||||
() => {
|
||||
setValue(storeValue);
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
|
||||
/** Get global store value and set it to local state */
|
||||
useEffect(() => {
|
||||
if (!debouncedSetStoreValue.isPending()) {
|
||||
setValue(storeValue);
|
||||
const debouncedSetStoreValue = useDebouncedCallback(
|
||||
(value) => $calculation.$values.setValue(valueName, value),
|
||||
350,
|
||||
{
|
||||
maxWait: 1500,
|
||||
}
|
||||
}, [debouncedSetStoreValue, storeValue]);
|
||||
);
|
||||
|
||||
return {
|
||||
value,
|
||||
setValue,
|
||||
};
|
||||
return debouncedSetStoreValue;
|
||||
}
|
||||
|
||||
export function useComputedValue(valueName) {
|
||||
const { $calculation } = useStore();
|
||||
|
||||
const computedValue = $calculation.$values.$computed[valueName];
|
||||
|
||||
return {
|
||||
computedValue,
|
||||
};
|
||||
return computedValue;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user