Skip to content

Commit caa76bc

Browse files
authored
feat(OneOfTogglerCard): added new display toggler card (#99)
1 parent ff23507 commit caa76bc

File tree

11 files changed

+162
-16
lines changed

11 files changed

+162
-16
lines changed

docs/spec.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,9 @@ type Spec = ArraySpec | BooleanSpec | NumberSpec | ObjectSpec | StringSpec;
136136

137137
#### OneOfParams
138138

139-
| Property | Type | Required | Description |
140-
| :------- | :------------------- | :------: | :---------- |
141-
| toggler | `'select'` `'radio'` | | Switch type |
139+
| Property | Type | Required | Description |
140+
| :------- | :---------------------------- | :------: | :---------- |
141+
| toggler | `'select'` `'radio'` `'card'` | | Switch type |
142142

143143
#### FileInput
144144

src/lib/core/types/specs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export interface ObjectSpec<LinkType = any> {
8787
order?: string[];
8888
link?: LinkType;
8989
oneOfParams?: {
90-
toggler?: 'select' | 'radio';
90+
toggler?: 'select' | 'radio' | 'card';
9191
};
9292
placeholder?: string;
9393
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@import '../../styles/variables.scss';
2+
3+
.#{$ns}toggler-card {
4+
width: 254px;
5+
padding: 10px;
6+
height: 88px;
7+
8+
&__header {
9+
display: flex;
10+
justify-content: space-between;
11+
align-items: baseline;
12+
}
13+
14+
&__text {
15+
margin-top: 12px;
16+
display: block;
17+
margin-right: 15px;
18+
height: 36px;
19+
overflow: hidden;
20+
text-overflow: ellipsis;
21+
}
22+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from 'react';
2+
3+
import {HelpPopover} from '@gravity-ui/components';
4+
import {Card, Text} from '@gravity-ui/uikit';
5+
6+
import {block} from '../../utils';
7+
8+
import './TogglerCard.scss';
9+
10+
const b = block('toggler-card');
11+
12+
interface TogglerCardProps {
13+
description?: string;
14+
title: string;
15+
text: string;
16+
onClick: () => void;
17+
disabled?: boolean;
18+
selected: boolean;
19+
}
20+
21+
export const TogglerCard: React.FC<TogglerCardProps> = ({
22+
description,
23+
title,
24+
text,
25+
onClick,
26+
disabled,
27+
selected,
28+
}) => {
29+
return (
30+
<Card
31+
onClick={onClick}
32+
type="selection"
33+
disabled={disabled ? !selected : disabled}
34+
selected={selected}
35+
size="m"
36+
className={b()}
37+
>
38+
<div className={b('header')}>
39+
<Text variant="subheader-2" ellipsis>
40+
{title}
41+
</Text>
42+
{description ? (
43+
<HelpPopover htmlContent={description} placement={['bottom', 'top']} />
44+
) : null}
45+
</div>
46+
<Text variant="body-1" color="secondary" className={b('text')}>
47+
{text}
48+
</Text>
49+
</Card>
50+
);
51+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './TogglerCard';

src/lib/kit/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ export * from './Inputs';
77
export * from './Layouts';
88
export * from './LongValue';
99
export * from './SimpleVerticalAccordeon';
10+
export * from './TogglerCard';
1011
export * from './Views';
1112
export * from './ViewLayouts';

src/lib/kit/hooks/useOneOf/useOneOf.scss

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,22 @@
88
max-width: unset;
99
}
1010
}
11+
12+
&_card {
13+
& + .#{$ns}group-indent {
14+
& > .#{$ns}use-search:not(.#{$ns}group-indent) {
15+
padding-top: 0px;
16+
margin-top: 15px;
17+
}
18+
}
19+
}
20+
}
21+
22+
&__card {
23+
display: flex;
24+
25+
& > :first-child {
26+
margin-right: 8px;
27+
}
1128
}
1229
}

src/lib/kit/hooks/useOneOf/useOneOf.tsx

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {RadioButton, Select} from '@gravity-ui/uikit';
44
import _ from 'lodash';
55

66
import {ObjectIndependentInputProps} from '../../../core';
7+
import {TogglerCard} from '../../components';
78
import {block} from '../../utils';
89

910
import './useOneOf.scss';
@@ -68,17 +69,49 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => {
6869
[spec.description, spec.viewSpec.order, specProperties],
6970
);
7071

71-
const selectToggler = React.useMemo(
72-
() =>
72+
const togglerType = React.useMemo(() => {
73+
if (spec.viewSpec.oneOfParams?.toggler === 'card' && options.length < 3) {
74+
return 'card';
75+
}
76+
77+
if (
7378
spec.viewSpec.oneOfParams?.toggler !== 'radio' &&
7479
(spec.viewSpec.oneOfParams?.toggler === 'select' ||
7580
options.length > 3 ||
76-
_.some(options, ({title}) => title.length > MAX_TAB_TITLE_LENGTH)),
77-
[options, spec.viewSpec.oneOfParams?.toggler],
78-
);
81+
_.some(options, ({title}) => title.length > MAX_TAB_TITLE_LENGTH))
82+
) {
83+
return 'select';
84+
}
85+
86+
return 'radio';
87+
}, [options, spec.viewSpec.oneOfParams?.toggler]);
7988

8089
const togglerInput = React.useMemo(() => {
81-
if (selectToggler) {
90+
if (togglerType === 'card') {
91+
return (
92+
<div className={b('card')}>
93+
{options.map(({value}) => {
94+
const onClick = () => {
95+
onOneOfChange([value]);
96+
};
97+
98+
return (
99+
<TogglerCard
100+
title={specProperties[value]?.viewSpec.layoutTitle || ''}
101+
disabled={spec.viewSpec.disabled}
102+
text={spec.description?.[value] || ''}
103+
description={specProperties[value]?.viewSpec.layoutDescription}
104+
onClick={onClick}
105+
selected={oneOfValue === value}
106+
key={value}
107+
/>
108+
);
109+
})}
110+
</div>
111+
);
112+
}
113+
114+
if (togglerType === 'select') {
82115
return (
83116
<Select
84117
width="max"
@@ -106,14 +139,23 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => {
106139
))}
107140
</RadioButton>
108141
);
109-
}, [selectToggler, oneOfValue, spec.viewSpec.disabled, name, options, onOneOfChange]);
142+
}, [
143+
togglerType,
144+
oneOfValue,
145+
spec.viewSpec.disabled,
146+
name,
147+
options,
148+
onOneOfChange,
149+
specProperties,
150+
]);
110151

111152
const toggler = React.useMemo(() => {
112153
if (Layout) {
113154
return (
114155
<div
115156
className={b('toggler', {
116-
radio: !selectToggler,
157+
radio: togglerType === 'radio',
158+
card: togglerType === 'card',
117159
})}
118160
>
119161
<Layout {...props}>{togglerInput}</Layout>
@@ -122,7 +164,7 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => {
122164
}
123165

124166
return <div>{togglerInput}</div>;
125-
}, [Layout, togglerInput, selectToggler, props]);
167+
}, [Layout, togglerInput, togglerType, props]);
126168

127169
return {oneOfValue, specProperties, toggler, togglerInput};
128170
};

src/stories/ObjectMultiOneOf.stories.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,13 @@ const spec: ObjectSpec = {
5555
},
5656
};
5757

58-
const excludeOptions = ['items', 'viewSpec.type', 'viewSpec.itemLabel', 'viewSpec.table'];
58+
const excludeOptions = [
59+
'items',
60+
'viewSpec.type',
61+
'viewSpec.itemLabel',
62+
'viewSpec.table',
63+
'viewSpec.oneOfParams',
64+
];
5965

6066
const value = {
6167
person: {

src/stories/ObjectMultiOneOfFlat.stories.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,13 @@ const spec: ObjectSpec = {
5555
},
5656
};
5757

58-
const excludeOptions = ['items', 'viewSpec.type', 'viewSpec.itemLabel', 'viewSpec.table'];
58+
const excludeOptions = [
59+
'items',
60+
'viewSpec.type',
61+
'viewSpec.itemLabel',
62+
'viewSpec.table',
63+
'viewSpec.oneOfParams',
64+
];
5965

6066
const value = {
6167
person: {

0 commit comments

Comments
 (0)