Skip to content

Commit b71e95b

Browse files
committed
Add line cap and join settings
1 parent 326c313 commit b71e95b

File tree

14 files changed

+320
-11
lines changed

14 files changed

+320
-11
lines changed

src/server/modes/charts/plugins/datalens/preparers/line/gravity-charts.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ export function prepareGravityChartLine(args: PrepareFunctionArgs) {
208208
},
209209
rangeSlider,
210210
lineWidth: graph.lineWidth,
211+
linecap: graph.linecap,
212+
linejoin: graph.linejoin,
211213
};
212214
});
213215

src/server/modes/charts/plugins/datalens/preparers/line/prepare-line-data.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,8 @@ export function prepareLineData(args: PrepareFunctionArgs) {
470470
colorGuid: colorItem?.guid || null,
471471
shapeGuid: shapeItem?.guid || null,
472472
measureFieldTitle: line.fieldTitle,
473+
linecap: shapesConfig?.linecap,
474+
linejoin: shapesConfig?.linejoin,
473475
};
474476

475477
// For one point (non-zero), the setting of the connection empty values has a strange effect:

src/shared/types/config/wizard/v15.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ export type V15ShapesConfig = {
313313
mountedShapes?: Record<string, string>;
314314
mountedShapesLineWidths?: Record<string, number>;
315315
chartLineWidth?: number;
316+
linecap?: string;
317+
linejoin?: string;
316318
fieldGuid?: string;
317319
};
318320

src/shared/types/wizard/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,8 @@ export interface ShapesConfig {
513513
mountedShapesLineWidths?: Record<string, number>;
514514
chartLineWidth?: number;
515515
fieldGuid?: string;
516+
linecap?: string;
517+
linejoin?: string;
516518
}
517519

518520
export type ClientChartsConfig = {
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import React from 'react';
2+
3+
import {Flex, Icon, SegmentedRadioGroup, Text} from '@gravity-ui/uikit';
4+
import type {IconData} from '@gravity-ui/uikit';
5+
import {LINE_CAPS, LINE_JOINS} from 'ui/units/wizard/constants/shapes';
6+
import type {LineCap, LineJoin} from 'ui/units/wizard/typings/shapes';
7+
8+
import {LineCapRoundIcon} from './icons/LineCapRoundIcon';
9+
import {LineCapSquareIcon} from './icons/LineCapSquareIcon';
10+
import {LineJoinRoundIcon} from './icons/LineJoinRoundIcon';
11+
import {LineJoinSquareIcon} from './icons/LineJoinSquareIcon';
12+
13+
export type DialogLineCapAndJoinValue = {
14+
cap: LineCap;
15+
join: LineJoin;
16+
};
17+
18+
type Props = {
19+
value: DialogLineCapAndJoinValue;
20+
onChange: (value: DialogLineCapAndJoinValue) => void;
21+
};
22+
23+
const CAP_ICONS: Record<LineCap, IconData> = {
24+
[LINE_CAPS.ROUND]: LineCapRoundIcon,
25+
[LINE_CAPS.SQUARE]: LineCapSquareIcon,
26+
};
27+
28+
const JOIN_ICONS: Record<LineJoin, IconData> = {
29+
[LINE_JOINS.MITER]: LineJoinSquareIcon,
30+
[LINE_JOINS.ROUND]: LineJoinRoundIcon,
31+
};
32+
33+
const CAP_VALUES: LineCap[] = [LINE_CAPS.SQUARE, LINE_CAPS.ROUND];
34+
const JOIN_VALUES: LineJoin[] = [LINE_JOINS.MITER, LINE_JOINS.ROUND];
35+
36+
const OPTIONS_GROUP_STYLE = {
37+
width: '136px',
38+
};
39+
40+
// TODO: Fix in a separate branch
41+
const I18N_STUB = {
42+
'dialog-line-cap-title': 'Концы линий',
43+
'dialog-line-join-title': 'Соединения линий',
44+
};
45+
46+
type OptionRowProps<T extends string> = {
47+
label: string;
48+
value: T;
49+
icons: Record<T, IconData>;
50+
values: T[];
51+
onUpdate: (value: T) => void;
52+
};
53+
54+
const OptionRow = <T extends string>({
55+
label,
56+
value,
57+
icons,
58+
values,
59+
onUpdate,
60+
}: OptionRowProps<T>) => (
61+
<Flex direction="row" alignItems="center" justifyContent="space-between">
62+
<Text variant="body-1">{label}</Text>
63+
<SegmentedRadioGroup
64+
value={value}
65+
style={OPTIONS_GROUP_STYLE}
66+
onUpdate={onUpdate as (value: string) => void}
67+
>
68+
{values.map((optionValue) => (
69+
<SegmentedRadioGroup.Option key={optionValue} value={optionValue}>
70+
<Icon data={icons[optionValue]} />
71+
</SegmentedRadioGroup.Option>
72+
))}
73+
</SegmentedRadioGroup>
74+
</Flex>
75+
);
76+
77+
export const DialogLineCapAndJoin = React.memo(({onChange, value}: Props) => {
78+
const [join, setJoin] = React.useState<LineJoin>(value.join);
79+
const [cap, setCap] = React.useState<LineCap>(value.cap);
80+
81+
React.useEffect(() => {
82+
onChange({cap, join});
83+
}, [cap, join, onChange]);
84+
85+
return (
86+
<Flex gap={2} direction="column">
87+
<OptionRow
88+
label={I18N_STUB['dialog-line-join-title']}
89+
value={join}
90+
icons={JOIN_ICONS}
91+
values={JOIN_VALUES}
92+
onUpdate={setJoin}
93+
/>
94+
<OptionRow
95+
label={I18N_STUB['dialog-line-cap-title']}
96+
value={cap}
97+
icons={CAP_ICONS}
98+
values={CAP_VALUES}
99+
onUpdate={setCap}
100+
/>
101+
</Flex>
102+
);
103+
});
104+
105+
DialogLineCapAndJoin.displayName = 'DialogLineCapAndJoin';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react';
2+
3+
export const LineCapRoundIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
4+
<svg
5+
xmlns="http://www.w3.org/2000/svg"
6+
width="16"
7+
height="16"
8+
viewBox="0 0 16 16"
9+
fill="none"
10+
{...props}
11+
>
12+
<path
13+
d="M2.51594 1.98763C8.02214 1.98763 13.082 0.992346 13.082 8.01058C13.0819 14.8157 7.83019 14.0335 2.51594 14.0335"
14+
stroke="black"
15+
strokeOpacity="0.85"
16+
strokeWidth="1.5"
17+
strokeLinecap="round"
18+
/>
19+
<line
20+
x1="2.49365"
21+
y1="8.00061"
22+
x2="8.99365"
23+
y2="8.00061"
24+
stroke="black"
25+
strokeOpacity="0.85"
26+
strokeWidth="1.5"
27+
strokeLinecap="round"
28+
strokeDasharray="3 3"
29+
/>
30+
</svg>
31+
);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react';
2+
3+
export const LineCapSquareIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
4+
<svg
5+
xmlns="http://www.w3.org/2000/svg"
6+
width="16"
7+
height="16"
8+
viewBox="0 0 16 16"
9+
fill="none"
10+
{...props}
11+
>
12+
<path
13+
d="M2.51594 1.97705L13.082 1.97705L13.082 8L13.082 14.023L2.51594 14.0229"
14+
stroke="black"
15+
strokeOpacity="0.85"
16+
strokeWidth="1.5"
17+
strokeLinecap="round"
18+
/>
19+
<line
20+
x1="2.49365"
21+
y1="8.00061"
22+
x2="8.99365"
23+
y2="8.00061"
24+
stroke="black"
25+
strokeOpacity="0.85"
26+
strokeWidth="1.5"
27+
strokeLinecap="round"
28+
strokeDasharray="3 3"
29+
/>
30+
</svg>
31+
);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React from 'react';
2+
3+
export const LineJoinRoundIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
4+
<svg
5+
xmlns="http://www.w3.org/2000/svg"
6+
width="16"
7+
height="16"
8+
viewBox="0 0 16 16"
9+
fill="none"
10+
{...props}
11+
>
12+
<g clipPath="url(#clip0_873_29)">
13+
<path
14+
d="M2.4971 3.49985L6.50155 3.49963C9.81539 3.49946 12.5019 6.1858 12.5019 9.49964L12.5019 13.501"
15+
stroke="black"
16+
strokeOpacity="0.85"
17+
strokeWidth="1.5"
18+
strokeLinecap="round"
19+
/>
20+
</g>
21+
<defs>
22+
<clipPath id="clip0_873_29">
23+
<rect width="16" height="16" fill="white" />
24+
</clipPath>
25+
</defs>
26+
</svg>
27+
);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React from 'react';
2+
3+
export const LineJoinSquareIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
4+
<svg
5+
xmlns="http://www.w3.org/2000/svg"
6+
width="16"
7+
height="16"
8+
viewBox="0 0 16 16"
9+
fill="none"
10+
{...props}
11+
>
12+
<g clipPath="url(#clip0_873_17)">
13+
<path
14+
d="M2.49722 3.49985L12.502 3.49931L12.502 13.501"
15+
stroke="black"
16+
strokeOpacity="0.85"
17+
strokeWidth="1.5"
18+
strokeLinecap="round"
19+
/>
20+
</g>
21+
<defs>
22+
<clipPath id="clip0_873_17">
23+
<rect width="16" height="16" fill="white" />
24+
</clipPath>
25+
</defs>
26+
</svg>
27+
);

src/ui/units/wizard/components/Dialogs/DialogShapes/DialogShapes.tsx

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,17 @@ import {
1515
type ValueOf,
1616
} from 'shared';
1717
import {getColorsConfigKey} from 'shared/modules/colors/common-helpers';
18-
import {LINE_WIDTH_AUTO_VALUE, LINE_WIDTH_DEFAULT_VALUE} from 'ui/units/wizard/constants/shapes';
18+
import {
19+
DEFAULT_LINE_CAP,
20+
DFAULT_LINE_JOIN,
21+
LINE_WIDTH_AUTO_VALUE,
22+
LINE_WIDTH_DEFAULT_VALUE,
23+
} from 'ui/units/wizard/constants/shapes';
24+
import type {LineCap, LineJoin} from 'ui/units/wizard/typings/shapes';
1925

2026
import {PaletteTypes} from '../../../constants';
2127

28+
import type {DialogLineCapAndJoinValue} from './DialogLineCapAndJoin/DialogLineCapAndJoin';
2229
import {DialogShapesChartSettingsTab} from './tabs/DialogShapesChartSettingsTab';
2330
import {DialogShapesGraphSettingsTab} from './tabs/DialogShapesGraphSettingsTab';
2431

@@ -53,6 +60,7 @@ export type ShapesState = {
5360
mountedShapes: Record<string, string>;
5461
mountedShapesLineWidths: Record<string, string>;
5562
chartLineWidth: string;
63+
capAndJoin: DialogLineCapAndJoinValue;
5664
selectedValue: string | null;
5765
};
5866

@@ -122,6 +130,10 @@ function getInitialShapesState({
122130
mountedShapes,
123131
mountedShapesLineWidths,
124132
chartLineWidth,
133+
capAndJoin: {
134+
cap: (shapesConfig.linecap as LineCap | undefined) || DEFAULT_LINE_CAP,
135+
join: (shapesConfig.linejoin as LineJoin | undefined) || DFAULT_LINE_JOIN,
136+
},
125137
};
126138
}
127139

@@ -192,12 +204,23 @@ const DialogShapes: React.FC<Props> = ({
192204
}));
193205
}, []);
194206

207+
const onLineCapAndJoinChange = React.useCallback((nextValue: DialogLineCapAndJoinValue) => {
208+
setShapesState((prevState) => ({
209+
...prevState,
210+
capAndJoin: {cap: nextValue.cap, join: nextValue.join},
211+
}));
212+
}, []);
213+
195214
const onReset = React.useCallback(() => {
196215
setShapesState((prevState) => ({
197216
...prevState,
198217
mountedShapes: {},
199218
mountedShapesLineWidths: {},
200219
chartLineWidth: LINE_WIDTH_DEFAULT_VALUE,
220+
capAndJoin: {
221+
cap: DEFAULT_LINE_CAP,
222+
join: DFAULT_LINE_JOIN,
223+
},
201224
}));
202225
}, []);
203226

@@ -208,12 +231,22 @@ const DialogShapes: React.FC<Props> = ({
208231
shapesState.mountedShapesLineWidths &&
209232
!Object.keys(shapesState.mountedShapesLineWidths).length;
210233
const isChartLineWidthDefault = shapesState.chartLineWidth === LINE_WIDTH_DEFAULT_VALUE;
234+
const isCapAndJoinDefault =
235+
shapesState.capAndJoin.cap === DEFAULT_LINE_CAP &&
236+
shapesState.capAndJoin.join === DFAULT_LINE_JOIN;
211237

212-
return hasNoMountedShapes && hasNoMountedShapesLineWidths && isChartLineWidthDefault;
238+
return (
239+
hasNoMountedShapes &&
240+
hasNoMountedShapesLineWidths &&
241+
isChartLineWidthDefault &&
242+
isCapAndJoinDefault
243+
);
213244
}, [
214245
shapesState.mountedShapes,
215246
shapesState.mountedShapesLineWidths,
216247
shapesState.chartLineWidth,
248+
shapesState.capAndJoin.cap,
249+
shapesState.capAndJoin.join,
217250
]);
218251

219252
const onClose = React.useCallback(() => {
@@ -243,14 +276,18 @@ const DialogShapes: React.FC<Props> = ({
243276
mountedShapesLineWidths: filteredMountedShapesLineWidths,
244277
}
245278
: {}),
279+
linecap: shapesState.capAndJoin.cap,
280+
linejoin: shapesState.capAndJoin.join,
246281
fieldGuid: item.guid,
247282
};
248283
onApply(nextShapesConfig);
249284
onClose();
250285
}, [
251-
shapesState.mountedShapes,
252286
shapesState.mountedShapesLineWidths,
253287
shapesState.chartLineWidth,
288+
shapesState.mountedShapes,
289+
shapesState.capAndJoin.cap,
290+
shapesState.capAndJoin.join,
254291
paletteType,
255292
item.guid,
256293
onApply,
@@ -306,7 +343,9 @@ const DialogShapes: React.FC<Props> = ({
306343
>
307344
<DialogShapesChartSettingsTab
308345
shapesState={shapesState}
346+
paletteType={paletteType}
309347
onChartLineWidthChange={onChartLineWidthChange}
348+
onLineCapAndJoinChange={onLineCapAndJoinChange}
310349
/>
311350
</TabPanel>
312351
</React.Fragment>
@@ -322,6 +361,7 @@ const DialogShapes: React.FC<Props> = ({
322361
item,
323362
items,
324363
onChartLineWidthChange,
364+
onLineCapAndJoinChange,
325365
onLineWidthChange,
326366
onPaletteItemClick,
327367
options,

0 commit comments

Comments
 (0)