Skip to content

Commit 863333d

Browse files
committed
fix: add additionalLayoutContent
1 parent 8819579 commit 863333d

File tree

13 files changed

+147
-95
lines changed

13 files changed

+147
-95
lines changed

src/lib/core/components/Form/Controller/Controller.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface ControllerProps<DirtyValue extends FieldValue, SpecType extends
2121
) => void)
2222
| null;
2323
parentOnUnmount: ((childName: string) => void) | null;
24+
additionalContentLayout?: React.ReactNode;
2425
}
2526

2627
export const Controller = <
@@ -33,6 +34,7 @@ export const Controller = <
3334
value: valueFromParent,
3435
parentOnChange,
3536
parentOnUnmount,
37+
additionalContentLayout,
3638
}: ControllerProps<DirtyValue, SpecType>) => {
3739
const {config, tools, mutatorsStore, __mirror} = useDynamicFormsCtx();
3840

@@ -47,6 +49,7 @@ export const Controller = <
4749
tools,
4850
parentOnChange,
4951
parentOnUnmount,
52+
additionalContentLayout,
5053
}),
5154
);
5255

@@ -120,8 +123,9 @@ export const Controller = <
120123
onDrop: methods.onDrop,
121124
},
122125
meta: {...omit(store.state, 'value'), submitFailed: store.tools.submitFailed},
126+
additionalContentLayout: additionalContentLayout,
123127
}),
124-
[methods, store.name, store.state, store.tools.submitFailed],
128+
[methods, store.name, store.state, store.tools.submitFailed, additionalContentLayout],
125129
);
126130

127131
const withSearch = useSearch(store.spec, store.state.value, store.name);

src/lib/core/components/Form/Controller/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import React from 'react';
12
import {FormValue, Spec} from '../../../types';
23
import {
34
DynamicFormConfig,
@@ -42,6 +43,7 @@ export interface GetRenderParams<
4243
| InputEntity<DirtyValue, undefined, undefined, SpecType>
4344
| IndependentInputEntity<DirtyValue, undefined, undefined, SpecType>;
4445
Layout?: LayoutType<DirtyValue, undefined, undefined, SpecType>;
46+
additionalContentLayout?: React.ReactNode;
4547
}
4648

4749
export interface GetValidateParams<SpecType extends Spec> {
@@ -110,6 +112,7 @@ export interface InitializeStoreParams<DirtyValue extends FieldValue, SpecType e
110112
) => void)
111113
| null;
112114
parentOnUnmount: ((childName: string) => void) | null;
115+
additionalContentLayout?: React.ReactNode;
113116
}
114117

115118
export interface ControllerStore<
@@ -148,6 +151,7 @@ export interface ControllerStore<
148151
childErrors: Record<string, ValidateError>;
149152
};
150153
afterStoreUpdateCB?: () => void;
154+
additionalContentLayout?: React.ReactNode;
151155
}
152156

153157
export interface UpdateStoreParams<

src/lib/core/components/Form/Controller/utils.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ export const getRender = <DirtyValue extends FieldValue, SpecType extends Spec>(
142142
spec,
143143
inputEntity,
144144
Layout,
145+
additionalContentLayout,
145146
}: GetRenderParams<DirtyValue, SpecType>) => {
146147
const render = (props: FieldRenderProps<DirtyValue>) => {
147148
if (inputEntity && isCorrectSpec(spec) && isString(name)) {
@@ -169,7 +170,13 @@ export const getRender = <DirtyValue extends FieldValue, SpecType extends Spec>(
169170

170171
if (Layout) {
171172
return (
172-
<Layout spec={spec} name={name} layoutProps={layoutProps} {...props}>
173+
<Layout
174+
spec={spec}
175+
name={name}
176+
layoutProps={layoutProps}
177+
additionalContentLayout={additionalContentLayout}
178+
{...props}
179+
>
173180
{input}
174181
</Layout>
175182
);
@@ -467,10 +474,11 @@ export const initializeStore = <
467474
tools,
468475
parentOnChange,
469476
parentOnUnmount,
477+
additionalContentLayout,
470478
}: InitializeStoreParams<DirtyValue, SpecType>): ControllerStore<DirtyValue, Value, SpecType> => {
471479
const spec = getSpec({name, spec: _spec, mutatorsStore});
472480
const components = getComponents<DirtyValue, Value, SpecType>({spec, config});
473-
const render = getRender({name, spec, ...components});
481+
const render = getRender({name, spec, ...components, additionalContentLayout});
474482
const validate = getValidate({spec, config});
475483
const state = getFieldInitials({
476484
name,

src/lib/core/components/Form/types/layout.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type LayoutProps<
1212
> = {
1313
children: React.ReactElement;
1414
layoutProps?: LayoutComponentProps;
15+
additionalContentLayout?: React.ReactNode;
1516
} & Omit<InputProps<Value, InputComponentProps, LayoutComponentProps, SpecType>, 'inputProps'>;
1617

1718
export type LayoutType<

src/lib/core/types/specs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export interface ArraySpec<
4747
placement?: 'horizontal' | 'vertical';
4848
disabled?: Record<string, boolean>;
4949
};
50+
enableLockLength?: boolean;
5051
};
5152
}
5253

src/lib/kit/components/Inputs/ArrayBase/ArrayBase.scss

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,6 @@
2222
min-width: unset;
2323
}
2424
}
25-
26-
&_disabled-remove-button {
27-
& > * .df-row__remove-button {
28-
color: var(--g-color-text-hint);
29-
background-color: transparent;
30-
cursor: default;
31-
pointer-events: none;
32-
}
33-
}
3425
}
3526

3627
&__item-prefix {
@@ -43,4 +34,8 @@
4334
margin-left: 4px;
4435
}
4536
}
37+
38+
&__remove-button {
39+
margin-left: 4px;
40+
}
4641
}

src/lib/kit/components/Inputs/ArrayBase/ArrayBase.tsx

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22

3-
import {Plus} from '@gravity-ui/icons';
4-
import {Button, Icon, Label} from '@gravity-ui/uikit';
3+
import {Plus, TrashBin} from '@gravity-ui/icons';
4+
import {Button, Icon, Label, Popover} from '@gravity-ui/uikit';
55
import set from 'lodash/set';
66

77
import {
@@ -21,6 +21,7 @@ import {
2121
transformArrIn,
2222
} from '../../../../core';
2323
import {block} from '../../../utils';
24+
import i18n from '../../../i18n';
2425

2526
import './ArrayBase.scss';
2627

@@ -42,9 +43,19 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => {
4243
return isBooleanSpec(spec.items) || isNumberSpec(spec.items) || isStringSpec(spec.items);
4344
}, [spec.items]);
4445

45-
const disabledRemoveButton = React.useMemo(() => {
46-
return keys.length <= spec.minLength;
47-
}, [keys.length, spec.minLength]);
46+
const disabledButtonLength = React.useMemo(() => {
47+
if (spec.viewSpec.enableLockLength) {
48+
return {
49+
remove: keys.length <= (spec.minLength || 0),
50+
add: keys.length >= (spec.maxLength || Infinity),
51+
};
52+
}
53+
54+
return {
55+
remove: false,
56+
add: false,
57+
};
58+
}, [keys.length, spec.maxLength, spec.minLength, spec.viewSpec.enableLockLength]);
4859

4960
const getItemSpec = React.useCallback(
5061
(idx: number): typeof spec.items | null => {
@@ -76,6 +87,13 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => {
7687
[input.onChange, input.name],
7788
);
7889

90+
const removeItem = React.useCallback(
91+
(key: string) => {
92+
arrayInput.onItemRemove(key);
93+
},
94+
[arrayInput],
95+
);
96+
7997
const AddButton: React.FC = React.useCallback(() => {
8098
let onClick = () => arrayInput.onItemAdd(undefined);
8199

@@ -91,20 +109,25 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => {
91109
title = spec.viewSpec.layoutTitle;
92110
}
93111

94-
if (keys.length >= spec.maxLength) {
95-
return null;
96-
}
97-
98112
return (
99-
<Button
100-
onClick={onClick}
101-
disabled={spec.viewSpec.disabled}
102-
qa={qa}
103-
className={b('add-button', {right: spec.viewSpec.addButtonPosition === 'right'})}
113+
<Popover
114+
content={i18n('label_error-max-length-array', {
115+
count: spec.maxLength,
116+
})}
117+
disabled={!disabledButtonLength.add}
104118
>
105-
<Icon data={Plus} size={14} />
106-
{title || null}
107-
</Button>
119+
<Button
120+
onClick={onClick}
121+
disabled={spec.viewSpec.disabled || disabledButtonLength.add}
122+
qa={qa}
123+
className={b('add-button', {
124+
right: spec.viewSpec.addButtonPosition === 'right',
125+
})}
126+
>
127+
<Icon data={Plus} size={14} />
128+
{title || null}
129+
</Button>
130+
</Popover>
108131
);
109132
}, [
110133
arrayInput,
@@ -115,7 +138,7 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => {
115138
spec.viewSpec.itemLabel,
116139
spec.viewSpec.layoutTitle,
117140
spec.viewSpec.addButtonPosition,
118-
keys,
141+
disabledButtonLength,
119142
]);
120143

121144
const items = React.useMemo(
@@ -142,6 +165,25 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => {
142165
parentOnUnmount={input.parentOnUnmount}
143166
spec={itemSpec}
144167
name={`${name}.<${key}>`}
168+
additionalContentLayout={
169+
<Popover
170+
content={i18n('label_error-min-length-array', {
171+
count: spec.minLength,
172+
})}
173+
disabled={!disabledButtonLength.remove}
174+
>
175+
<Button
176+
view="flat-secondary"
177+
onClick={() => removeItem(key)}
178+
key={`remove-${key}`}
179+
qa={`${name}-item-remove-${key}`}
180+
disabled={disabledButtonLength.remove}
181+
className={b('remove-button')}
182+
>
183+
<Icon data={TrashBin} size={16} />
184+
</Button>
185+
</Popover>
186+
}
145187
/>
146188
</React.Fragment>
147189
);
@@ -154,6 +196,9 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => {
154196
input.parentOnUnmount,
155197
input.value,
156198
spec.viewSpec.itemPrefix,
199+
spec.minLength,
200+
disabledButtonLength.remove,
201+
removeItem,
157202
],
158203
);
159204

@@ -168,7 +213,6 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => {
168213
'add-button-down':
169214
spec.viewSpec.addButtonPosition !== 'right' && keys.length > 0,
170215
'items-primitive': itemsPrimitive,
171-
'disabled-remove-button': disabledRemoveButton,
172216
})}
173217
>
174218
{items}

src/lib/kit/components/Inputs/TableArrayInput/TableArrayInput.tsx

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22

33
import {Plus, TrashBin} from '@gravity-ui/icons';
4-
import {Button, Flex, Icon, Table} from '@gravity-ui/uikit';
4+
import {Button, Flex, Icon, Popover, Table} from '@gravity-ui/uikit';
55
import {HelpPopover} from '@gravity-ui/components';
66
import noop from 'lodash/noop';
77
import set from 'lodash/set';
@@ -23,6 +23,7 @@ import {
2323
} from '../../../../core';
2424
import {useSearchContext} from '../../../../core/components/Form/hooks';
2525
import {block} from '../../../utils';
26+
import i18n from '../../../i18n';
2627

2728
import './TableArrayInput.scss';
2829

@@ -43,6 +44,20 @@ export const TableArrayInput: ArrayInput = ({spec, name, arrayInput, input}) =>
4344
[arrayInput.value],
4445
);
4546

47+
const disabledButtonLength = React.useMemo(() => {
48+
if (spec.viewSpec.enableLockLength) {
49+
return {
50+
remove: keys.length <= (spec.minLength || 0),
51+
add: keys.length >= (spec.maxLength || Infinity),
52+
};
53+
}
54+
55+
return {
56+
remove: false,
57+
add: false,
58+
};
59+
}, [keys.length, spec.maxLength, spec.minLength, spec.viewSpec.enableLockLength]);
60+
4661
const onItemAdd = React.useCallback(() => {
4762
arrayInput.onItemAdd({});
4863
}, [arrayInput.onItemAdd]);
@@ -90,15 +105,22 @@ export const TableArrayInput: ArrayInput = ({spec, name, arrayInput, input}) =>
90105
name: '',
91106
sticky: 'right',
92107
template: ({key}: {key: string}) => (
93-
<Button
94-
view="flat-secondary"
95-
onClick={() => onItemRemove(key)}
96-
key={`remove-${key}`}
97-
qa={`${name}-item-remove-${key}`}
98-
disabled={keys.length <= spec.minLength}
108+
<Popover
109+
content={i18n('label_error-min-length-array', {
110+
count: spec.minLength,
111+
})}
112+
disabled={!disabledButtonLength.remove}
99113
>
100-
<Icon data={TrashBin} size={16} />
101-
</Button>
114+
<Button
115+
view="flat-secondary"
116+
onClick={() => onItemRemove(key)}
117+
key={`remove-${key}`}
118+
qa={`${name}-item-remove-${key}`}
119+
disabled={disabledButtonLength.remove}
120+
>
121+
<Icon data={TrashBin} size={16} />
122+
</Button>
123+
</Popover>
102124
),
103125
};
104126

@@ -200,18 +222,21 @@ export const TableArrayInput: ArrayInput = ({spec, name, arrayInput, input}) =>
200222
{spec.viewSpec.layoutTitle || null}
201223
</Button>
202224
) : (
203-
<React.Fragment>
204-
{keys.length >= spec.maxLength ? null : (
205-
<Button
206-
onClick={onItemAdd}
207-
disabled={spec.viewSpec.disabled}
208-
qa={`${name}-add-item`}
209-
>
210-
<Icon data={Plus} size={14} />
211-
{spec.viewSpec.itemLabel || null}
212-
</Button>
213-
)}
214-
</React.Fragment>
225+
<Popover
226+
content={i18n('label_error-max-length-array', {
227+
count: spec.maxLength,
228+
})}
229+
disabled={!disabledButtonLength.add}
230+
>
231+
<Button
232+
onClick={onItemAdd}
233+
disabled={spec.viewSpec.disabled || disabledButtonLength.add}
234+
qa={`${name}-add-item`}
235+
>
236+
<Icon data={Plus} size={14} />
237+
{spec.viewSpec.itemLabel || null}
238+
</Button>
239+
</Popover>
215240
)}
216241
</div>
217242
);

0 commit comments

Comments
 (0)