Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/i18n-keysets/wizard/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,12 @@
"label_default-period": "Default period",
"label_delete-all-filters-action": "Delete all filters in section",
"label_dialog-column-info-text": "The total width of the table always occupies 100% of the available space, regardless of the specified widths of the individual columns.",
"label_dialog-line-width": "Line width",
"label_dialog-radius": "Point size",
"label_dialog-radius-max": "Maximal point size",
"label_dialog-radius-min": "Minimal point size",
"label_dialog-shapes-chart-settings-tab": "General settings",
"label_dialog-shapes-line-settings-tab": "Lines",
"label_direction": "Direction",
"label_disabled": "Disabled",
"label_discrete": "Discrete",
Expand Down Expand Up @@ -188,6 +191,7 @@
"label_limit": "Limit",
"label_limit-input-error": "Enter a value from {{min}} to {{max}}",
"label_limit-message": "Only first 1000 values shown",
"label_line-width-auto-value": "Automatically",
"label_linear": "Linear",
"label_link-failed": "This dimension is not part of dataset links",
"label_links": "Links",
Expand Down
4 changes: 4 additions & 0 deletions src/i18n-keysets/wizard/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,12 @@
"label_default-period": "Период по умолчанию",
"label_delete-all-filters-action": "Удалить все фильтры в секции",
"label_dialog-column-info-text": "Общая ширина таблицы всегда занимает 100% доступного пространства вне зависимости от указанной ширины отдельных столбцов.",
"label_dialog-line-width": "Толщина линии",
"label_dialog-radius": "Размер точек",
"label_dialog-radius-max": "Максимальный размер точек",
"label_dialog-radius-min": "Минимальный размер точек",
"label_dialog-shapes-chart-settings-tab": "Общие настройки",
"label_dialog-shapes-line-settings-tab": "Линии",
"label_direction": "Направление",
"label_disabled": "Выключена",
"label_discrete": "Дискретный",
Expand Down Expand Up @@ -188,6 +191,7 @@
"label_limit": "Лимит",
"label_limit-input-error": "Введите значение от {{min}} до {{max}}",
"label_limit-message": "Выводится только первая тысяча значений",
"label_line-width-auto-value": "Автоматически",
"label_linear": "Линейная",
"label_link-failed": "Измерение не участвует в связи датасетов",
"label_links": "Связи",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ export function prepareGravityChartLine(args: PrepareFunctionArgs) {
shapeValue: graph.shapeValue,
},
rangeSlider,
lineWidth: graph.lineWidth,
};
});

Expand Down
20 changes: 20 additions & 0 deletions src/server/modes/charts/plugins/datalens/utils/shape-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,25 @@ export const mapAndShapeGraph = ({

graph.dashStyle = SHAPES_IN_ORDER[shapeIndex % SHAPES_IN_ORDER.length];
}

// Determine line width: use individual line width if set, otherwise fall back to chart-level width
let lineWidth: number | undefined;

if (title && shapesConfig?.mountedShapesLineWidths?.[title]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if title is an empty string (or null)? Is everything going to be okay?

// Individual line has a specific width set
lineWidth = shapesConfig.mountedShapesLineWidths[title];
} else if (shapesConfig?.chartLineWidth !== undefined) {
// Fall back to chart-level line width
lineWidth = shapesConfig.chartLineWidth;
}

if (lineWidth !== undefined) {
graph.lineWidth = lineWidth;
graph.states = {
hover: {
lineWidth: lineWidth + 2,
},
};
}
});
};
9 changes: 9 additions & 0 deletions src/shared/constants/qa/wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,12 @@ export const enum ChartSettingsDialogQA {
IndicatorTitleMode = 'indicator-title-mode',
PreserveWhiteSpace = 'preserve-white-space',
}

export const enum DialogShapeSettings {
LineSettingsGraphScopeTabPanel = 'line-settings-graph-scope-tab-panel',
LineSettingsChartScopeTabPanel = 'line-settings-chart-scope-tab-panel',
LineSettingsGraphScopeTab = 'line-settings-graph-scope-tab',
LineSettingsChartScopeTab = 'line-settings-chart-scope-tab',
LineWidthSelectControl = 'line-width-select-control',
LineWidthSelectOption = 'line-width-select-option',
}
2 changes: 2 additions & 0 deletions src/shared/types/config/wizard/v15.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ export type V15ColorsConfig = {

export type V15ShapesConfig = {
mountedShapes?: Record<string, string>;
mountedShapesLineWidths?: Record<string, number>;
chartLineWidth?: number;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to refer to it without using a chart? The whole config is already related to the chart, so this doesn’t add any meaning.

fieldGuid?: string;
};

Expand Down
2 changes: 2 additions & 0 deletions src/shared/types/wizard/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ export type Sort = Field & {

export interface ShapesConfig {
mountedShapes?: Record<string, string>;
mountedShapesLineWidths?: Record<string, number>;
chartLineWidth?: number;
fieldGuid?: string;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
.wizard-number-input {
display: flex;
max-width: 120px;
max-width: 136px;
width: 136px;

&__input-button {
width: 45px;
}

// This beautiful crutch is already here
// https://github.com/gravity-ui/chartkit/blob/master/src/components/Widget/Table/Paginator/Paginator.scss#L25
Expand All @@ -14,5 +19,12 @@
& input[type='number'] {
-moz-appearance: textfield;
}
&__text-input {
width: 46px;
}
/* stylelint-enable */
&__text-input > .g-text-input__content {
border-left: none;
border-right: none;
}
}
141 changes: 114 additions & 27 deletions src/ui/components/NumberFormatSettings/NumberInput/NumberInput.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,47 @@
import React from 'react';

import {Button, TextInput} from '@gravity-ui/uikit';
import {Minus, Plus} from '@gravity-ui/icons';
import {Button, Icon, TextInput} from '@gravity-ui/uikit';
import block from 'bem-cn-lite';
import type {ValueOf} from 'shared';

import './NumberInput.scss';

const STEP_BUTTON_DIRECTION = {
Plus: '+',
Minus: '-',
} as const;

type StepButtonDirection = ValueOf<typeof STEP_BUTTON_DIRECTION>;

interface StepButtonProps {
direction: StepButtonDirection;
disabled: boolean;
onClick: () => void;
}

const DIRECTION_CONFIG: Record<
StepButtonDirection,
{icon: typeof Plus | typeof Minus; pin: 'brick-round' | 'round-brick'}
> = {
[STEP_BUTTON_DIRECTION.Plus]: {icon: Plus, pin: 'brick-round'},
[STEP_BUTTON_DIRECTION.Minus]: {icon: Minus, pin: 'round-brick'},
};

const b = block('wizard-number-input');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There doesn't seem to be any specifics in the component specifically for wizard? It is better not to add unnecessary names to the classes in this case - it will be more difficult to reuse later somewhere else


const StepButton: React.FC<StepButtonProps> = ({direction, disabled, onClick}) => {
const {icon, pin} = DIRECTION_CONFIG[direction];

return (
<div className={b('input-button')}>
<Button view="outlined" pin={pin} width="max" disabled={disabled} onClick={onClick}>
<Icon data={icon} />
</Button>
</div>
);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you can put StepButton in a separate file - there's already quite a lot besides the layout


interface NumberInputProps {
value: number;
max?: number;
Expand All @@ -13,53 +50,103 @@ interface NumberInputProps {
qa?: string;
}

const b = block('wizard-number-input');
const DEFAULT_VALUE = 0;

const NumberInput: React.FC<NumberInputProps> = ({
value,
onChange,
max = Infinity,
min = -Infinity,
qa,
}) => {
const onBlur = React.useCallback(() => {
if (Number.isNaN(value)) {
onChange(DEFAULT_VALUE);
}
}, [onChange, value]);
const [internalValue, setInternalValue] = React.useState<string>(String(value));

const onInput = React.useCallback((newValue) => onChange(parseInt(newValue, 10)), [onChange]);
React.useEffect(() => {
setInternalValue(String(value));
}, [value]);

const onPlus = React.useCallback(() => {
const newValue = Number.isNaN(value) ? DEFAULT_VALUE : Math.min(value + 1, max);
onChange(newValue);
}, [max, onChange, value]);
const commitValue = React.useCallback(
(newValue: number) => {
setInternalValue(String(newValue));
onChange(newValue);
},
[onChange],
);

const clampAndCommit = React.useCallback(
(delta = 0) => {
const parsed = parseInt(internalValue, 10);
const baseValue = Number.isNaN(parsed) ? value : parsed;
const newValue = baseValue + delta;

commitValue(Math.max(min, Math.min(max, newValue)));
},
[internalValue, min, max, commitValue, value],
);

const onBlur = React.useCallback(
(e: React.FocusEvent<HTMLInputElement>) => {
const relatedTarget = e.relatedTarget as HTMLElement | null;
if (relatedTarget?.closest(`.${b('input-button')}`)) {
return;
}

clampAndCommit();
},
[clampAndCommit],
);

const onKeyDown = React.useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
clampAndCommit();
}
},
[clampAndCommit],
);

const onMinus = React.useCallback(() => {
const newValue = Number.isNaN(value) ? DEFAULT_VALUE : Math.max(value - 1, min);
onChange(newValue);
}, [min, onChange, value]);
const onInput = React.useCallback((newValue: string) => {
setInternalValue(newValue);
}, []);

const memorizedInputAttrs = React.useMemo(() => ({min, max}), [min, max]);
const onPlus = React.useCallback(() => clampAndCommit(1), [clampAndCommit]);

const onMinus = React.useCallback(() => clampAndCommit(-1), [clampAndCommit]);

const memorizedInputAttrs: React.InputHTMLAttributes<HTMLInputElement> = React.useMemo(
() => ({
min: Number.isFinite(min) ? min : undefined,
max: Number.isFinite(max) ? max : undefined,
style: {textAlign: 'center'},
}),
[min, max],
);

const parsedInternal = parseInt(internalValue, 10);
const isMinusDisabled = !Number.isNaN(parsedInternal) && parsedInternal <= min;
const isPlusDisabled = !Number.isNaN(parsedInternal) && parsedInternal >= max;

return (
<div className={b()}>
<Button pin="round-brick" onClick={onMinus}>
-
</Button>
<div className={b({})}>
<StepButton
direction={STEP_BUTTON_DIRECTION.Minus}
disabled={isMinusDisabled}
onClick={onMinus}
/>
<TextInput
qa={qa}
type="number"
pin="brick-brick"
controlProps={memorizedInputAttrs}
value={String(value)}
value={internalValue}
onBlur={onBlur}
onUpdate={onInput}
onKeyDown={onKeyDown}
className={b('text-input')}
/>
<StepButton
direction={STEP_BUTTON_DIRECTION.Plus}
disabled={isPlusDisabled}
onClick={onPlus}
/>
<Button pin="brick-round" onClick={onPlus}>
+
</Button>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';

import {Flex, Text} from '@gravity-ui/uikit';
import {i18n} from 'i18n';
import NumberInput from 'ui/components/NumberFormatSettings/NumberInput/NumberInput';
import {
LINE_WIDTH_AUTO_VALUE,
LINE_WIDTH_MAX_VALUE,
LINE_WIDTH_MIN_VALUE,
} from 'ui/units/wizard/constants/shapes';

import {LineWidthSelect} from '../../../LineWidthSelect/LineWidthSelect';

interface DialogLineWidthProps {
value: string;
onChange: (lineWidth: string) => void;
style?: React.CSSProperties;
allowDefault?: boolean;
}

export const DialogLineWidth = React.memo(
({value, onChange, allowDefault, style}: DialogLineWidthProps) => {
const handleNumberInputChange = React.useCallback(
(nextValue: number) => {
return onChange(nextValue.toString());
},
[onChange],
);

const isAutoValue = value === LINE_WIDTH_AUTO_VALUE;

const numberInput = React.useMemo(() => {
if (isAutoValue) {
return null;
}

const valueNumber = Number.parseInt(value, 10);

if (Number.isNaN(valueNumber)) {
return null;
}

return (
<Flex direction="row" alignItems="center" justifyContent="flex-end">
<NumberInput
value={valueNumber}
min={LINE_WIDTH_MIN_VALUE}
max={LINE_WIDTH_MAX_VALUE}
onChange={handleNumberInputChange}
/>
</Flex>
);
}, [handleNumberInputChange, value, isAutoValue]);

return (
<Flex direction="column" gap={2} style={style}>
<Flex direction="row" alignItems="center" justifyContent="space-between">
<Text variant="body-1">{i18n('wizard', 'label_dialog-line-width')}</Text>
<LineWidthSelect
allowDefault={allowDefault}
value={value}
onChange={onChange}
/>
</Flex>
{numberInput}
</Flex>
);
},
);

DialogLineWidth.displayName = 'DialogLineWidth';
Loading
Loading