Skip to content

Commit

Permalink
add indicator plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
korvin89 committed Jul 22, 2022
1 parent 04c18b1 commit fd0b376
Show file tree
Hide file tree
Showing 13 changed files with 262 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/constants/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// This css class should be added for DOM element for correct calculation of scrollHeight
export const CHARTKIT_SCROLLABLE_NODE_CLASSNAME = 'chartkit-scrollable-node';
1 change: 1 addition & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {CHARTKIT_SCROLLABLE_NODE_CLASSNAME} from './common';
1 change: 1 addition & 0 deletions src/i18n/keysets/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"tooltip-rest": "Rest"
},
"error": {
"label_no-data": "No data",
"label_unknown-plugin": "Unknown plugin type \"{{type}}\"",
"label_unknown-error": "Unknown error"
}
Expand Down
1 change: 1 addition & 0 deletions src/i18n/keysets/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"tooltip-rest": "Остальные"
},
"error": {
"label_no-data": "Нет данных",
"label_unknown-plugin": "Неизвестный тип плагина \"{{type}}\"",
"label_unknown-error": "Неизвестная ошибка"
}
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export {YagrPlugin} from './yagr';
export type {YagrWidgetData, YagrWidgetProps} from './yagr/types';
export {IndicatorPlugin} from './indicator';
export type {IndicatorWidgetData, IndicatorWidgetProps} from './indicator/types';
63 changes: 63 additions & 0 deletions src/plugins/indicator/__stories__/Indicator.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import {Meta, Story} from '@storybook/react';
import {withKnobs, boolean, color as colorKnob, radios, text} from '@storybook/addon-knobs';
import {cloneDeep} from 'lodash';
import {Button} from '@yandex-cloud/uikit';
import {settings} from '../../../libs';
import {ChartKit} from '../../../components/ChartKit';
import type {ChartKitRef} from '../../../types';
import {IndicatorPlugin} from '../';
import type {IndicatorWidgetData, IndicatorWidgetDataItem} from '../types';

const data: IndicatorWidgetData = {
data: [
{
content: {
current: {
value: 1539577973,
},
},
},
],
};

const Template: Story = () => {
const [shown, setShown] = React.useState(false);
const chartkitRef = React.useRef<ChartKitRef>();
const color = colorKnob('color', '#4da2f1');
const size = radios<IndicatorWidgetDataItem['size']>(
'size',
{s: 's', m: 'm', l: 'l', xl: 'xl'},
'm',
);
const title = text('title', 'Value title');
const nowrap = boolean('nowrap', false);
const resultData = cloneDeep(data);

if (resultData.data) {
resultData.data[0].size = size;
resultData.data[0].color = color;
resultData.data[0].title = title;
resultData.data[0].nowrap = nowrap;
}

if (!shown) {
settings.set({plugins: [IndicatorPlugin]});
return <Button onClick={() => setShown(true)}>Show chart</Button>;
}

return (
<div style={{height: 300, width: '100%'}}>
<ChartKit ref={chartkitRef} id="1" type="indicator" data={resultData} />
</div>
);
};

export const Showcase = Template.bind({});

const meta: Meta = {
title: 'Plugins/Indicator',
decorators: [withKnobs],
};

export default meta;
7 changes: 7 additions & 0 deletions src/plugins/indicator/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react';
import {ChartKitPlugin} from '../../types';

export const IndicatorPlugin: ChartKitPlugin = {
type: 'indicator',
renderer: React.lazy(() => import('./renderer/IndicatorWidget')),
};
35 changes: 35 additions & 0 deletions src/plugins/indicator/renderer/IndicatorItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import block from 'bem-cn-lite';
import type {IndicatorWidgetProps, IndicatorWidgetDataItem} from '../types';

const b = block('chartkit-indicator');

export const IndicatorItem = (
props: IndicatorWidgetDataItem & {
defaultColor?: string;
formatNumber?: IndicatorWidgetProps['formatNumber'];
},
) => {
const {formatNumber, content, color, defaultColor, size, title, nowrap} = props;
const mods = {size, nowrap};
const style: React.CSSProperties = {color: color || defaultColor};

let value = content.current.value;

if (formatNumber && typeof value === 'number') {
value = formatNumber(value, content.current);
}

return (
<div className={b('item', mods)}>
{title && (
<div className={b('item-title')} title={nowrap ? title : ''}>
{title}
</div>
)}
<div className={b('item-value')} style={style}>
{value}
</div>
</div>
);
};
66 changes: 66 additions & 0 deletions src/plugins/indicator/renderer/IndicatorWidget.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
.chartkit-indicator {
$class: &;

width: 100%;
height: 100%;
display: flex;
flex-direction: column;

&__content {
width: 100%;
overflow: auto;
}

&__item {
padding: 15px;
font-size: inherit;
box-sizing: border-box;

&_nowrap {
#{$class}__item-title {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}

&_size_s #{$class}__item-title {
font-size: 13px;
}

&_size_s #{$class}__item-value {
font-size: 24px;
}

&_size_l #{$class}__item-title {
font-size: 20px;
}

&_size_l #{$class}__item-value {
font-size: 64px;
}

&_size_xl #{$class}__item-title {
font-size: 24px;
}

&_size_xl #{$class}__item-value {
font-size: 80px;
}
}

&__item-title {
font-weight: 500;
line-height: 1.2;
color: var(--yc-color-text-primary);
font-size: 16px;
padding-bottom: 0.125em;
}

&__item-value {
font-weight: 500;
line-height: 1.2;
font-size: 48px;
white-space: nowrap;
}
}
52 changes: 52 additions & 0 deletions src/plugins/indicator/renderer/IndicatorWidget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import block from 'bem-cn-lite';
import {i18n} from '../../../i18n';
import {CHARTKIT_ERROR_CODE, ChartKitError} from '../../../libs';
import {CHARTKIT_SCROLLABLE_NODE_CLASSNAME} from '../../../constants';
import type {ChartKitWidgetRef} from '../../../types';
import type {IndicatorWidgetProps} from '../types';
import {IndicatorItem} from './IndicatorItem';

import './IndicatorWidget.scss';

const b = block('chartkit-indicator');

const IndicatorWidget = React.forwardRef<ChartKitWidgetRef | undefined, IndicatorWidgetProps>(
// _ref needs to avoid this warning:
// "forwardRef render functions accept exactly two parameters: props and ref"
(props, _ref) => {
const {
onLoad,
formatNumber,
data: {data, defaultColor},
} = props;

React.useEffect(() => {
onLoad?.();
}, []);

if (!data) {
throw new ChartKitError({
code: CHARTKIT_ERROR_CODE.NO_DATA,
message: i18n('error', 'label_no-data'),
});
}

return (
<div className={b()}>
<div className={b('content', CHARTKIT_SCROLLABLE_NODE_CLASSNAME)}>
{data.map((item, index) => (
<IndicatorItem
{...item}
key={`${index}-ck-indicator-item`}
defaultColor={defaultColor}
formatNumber={formatNumber}
/>
))}
</div>
</div>
);
},
);

export default IndicatorWidget;
24 changes: 24 additions & 0 deletions src/plugins/indicator/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type {ChartKitFormatNumber} from '../../types';

export type IndicatorWidgetDataItem = {
content: {
current: {
value: string | number;
} & Record<string, unknown>;
};
color?: string;
size?: 's' | 'm' | 'l' | 'xl';
title?: string;
nowrap?: boolean;
};

export type IndicatorWidgetData = {
data?: IndicatorWidgetDataItem[];
defaultColor?: string;
};

export type IndicatorWidgetProps = {
data: IndicatorWidgetData;
onLoad?: () => void;
formatNumber?: ChartKitFormatNumber;
};
3 changes: 3 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ export type ChartKitOnLoadData<T extends ChartkitType> = {

export type ChartKitOnError = (data: {error: any}) => void;

export type ChartKitFormatNumber = (value: number, options?: unknown) => string;

export type ChartKitProps<T extends ChartkitType> = {
type: T;
data: ChartkitWidget[T]['data'];
id?: string;
onLoad?: (data?: ChartKitOnLoadData<T>) => void;
onError?: ChartKitOnError;
formatNumber?: ChartKitFormatNumber;
} & {[key in keyof Omit<ChartkitWidget[T], 'data' | 'widget'>]: ChartkitWidget[T][key]};

export type ChartKitPlugin = {
Expand Down
5 changes: 5 additions & 0 deletions src/types/widget.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import type Yagr from 'yagr';
import type {YagrWidgetData} from '../plugins/yagr/types';
import type {IndicatorWidgetData} from '../plugins/indicator/types';

export interface ChartkitWidget {
yagr: {
data: YagrWidgetData;
widget: Yagr;
};
indicator: {
data: IndicatorWidgetData;
widget: never;
};
}

0 comments on commit fd0b376

Please sign in to comment.