@servicetitan/marketing-ui 1.8.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ads/ads-stat.d.ts +3 -3
- package/dist/components/ads/ads-stat.d.ts.map +1 -1
- package/dist/components/ads/ads-stat.js.map +1 -1
- package/dist/components/charts/funnel-chart/components/funnel-chart.d.ts +2 -2
- package/dist/components/charts/funnel-chart/components/funnel-chart.d.ts.map +1 -1
- package/dist/components/charts/funnel-chart/components/funnel-chart.js.map +1 -1
- package/dist/components/charts/funnel-chart/components/funnel-svg.d.ts +2 -2
- package/dist/components/charts/funnel-chart/components/funnel-svg.d.ts.map +1 -1
- package/dist/components/charts/funnel-chart/components/funnel-svg.js.map +1 -1
- package/dist/components/charts/funnel-chart/funnel-chart.stories.d.ts +2 -2
- package/dist/components/charts/funnel-chart/funnel-chart.stories.d.ts.map +1 -1
- package/dist/components/charts/funnel-chart/funnel-chart.stories.js.map +1 -1
- package/dist/components/charts/line-chart/components/body.d.ts +2 -2
- package/dist/components/charts/line-chart/components/body.d.ts.map +1 -1
- package/dist/components/charts/line-chart/components/body.js +2 -2
- package/dist/components/charts/line-chart/components/body.js.map +1 -1
- package/dist/components/charts/line-chart/components/container.d.ts +2 -2
- package/dist/components/charts/line-chart/components/container.d.ts.map +1 -1
- package/dist/components/charts/line-chart/components/container.js.map +1 -1
- package/dist/components/charts/line-chart/components/hover-popover.d.ts +2 -2
- package/dist/components/charts/line-chart/components/hover-popover.d.ts.map +1 -1
- package/dist/components/charts/line-chart/components/hover-popover.js +2 -2
- package/dist/components/charts/line-chart/components/hover-popover.js.map +1 -1
- package/dist/components/charts/line-chart/components/sidebar.d.ts +2 -2
- package/dist/components/charts/line-chart/components/sidebar.d.ts.map +1 -1
- package/dist/components/charts/line-chart/components/sidebar.js.map +1 -1
- package/dist/components/charts/line-chart/components/stuff.d.ts +4 -4
- package/dist/components/charts/line-chart/components/stuff.d.ts.map +1 -1
- package/dist/components/charts/line-chart/components/stuff.js.map +1 -1
- package/dist/components/charts/line-chart/components/svg-bars.d.ts +3 -3
- package/dist/components/charts/line-chart/components/svg-bars.d.ts.map +1 -1
- package/dist/components/charts/line-chart/components/svg-bars.js.map +1 -1
- package/dist/components/charts/line-chart/components/svg-body.d.ts +3 -3
- package/dist/components/charts/line-chart/components/svg-body.d.ts.map +1 -1
- package/dist/components/charts/line-chart/components/svg-body.js.map +1 -1
- package/dist/components/charts/line-chart/components/svg-lines.d.ts +2 -2
- package/dist/components/charts/line-chart/components/svg-lines.d.ts.map +1 -1
- package/dist/components/charts/line-chart/components/svg-lines.js.map +1 -1
- package/dist/components/charts/line-chart/line-chart.stories.d.ts +2 -2
- package/dist/components/charts/line-chart/line-chart.stories.d.ts.map +1 -1
- package/dist/components/charts/line-chart/line-chart.stories.js.map +1 -1
- package/dist/components/charts/line-chart/utils/interfaces.d.ts +2 -2
- package/dist/components/charts/line-chart/utils/interfaces.d.ts.map +1 -1
- package/dist/components/charts/pie-chart/components/pie-chart.d.ts +2 -2
- package/dist/components/charts/pie-chart/components/pie-chart.d.ts.map +1 -1
- package/dist/components/charts/pie-chart/components/pie-chart.js +3 -3
- package/dist/components/charts/pie-chart/components/pie-chart.js.map +1 -1
- package/dist/components/charts/pie-chart/components/pie.d.ts +5 -2
- package/dist/components/charts/pie-chart/components/pie.d.ts.map +1 -1
- package/dist/components/charts/pie-chart/components/pie.js +12 -32
- package/dist/components/charts/pie-chart/components/pie.js.map +1 -1
- package/dist/components/charts/pie-chart/pie-chart.stories.d.ts +3 -2
- package/dist/components/charts/pie-chart/pie-chart.stories.d.ts.map +1 -1
- package/dist/components/charts/pie-chart/pie-chart.stories.js +7 -1
- package/dist/components/charts/pie-chart/pie-chart.stories.js.map +1 -1
- package/dist/components/charts/pie-chart/utils/const.d.ts +2 -2
- package/dist/components/charts/pie-chart/utils/const.d.ts.map +1 -1
- package/dist/components/charts/pie-chart/utils/const.js +9 -7
- package/dist/components/charts/pie-chart/utils/const.js.map +1 -1
- package/dist/components/charts/pie-chart/utils/interface.d.ts +5 -2
- package/dist/components/charts/pie-chart/utils/interface.d.ts.map +1 -1
- package/dist/components/image-cropper/image-cropper.d.ts +2 -2
- package/dist/components/image-cropper/image-cropper.d.ts.map +1 -1
- package/dist/components/image-cropper/image-cropper.js +2 -2
- package/dist/components/image-cropper/image-cropper.js.map +1 -1
- package/dist/components/image-cropper/image-cropper.stories.js +5 -8
- package/dist/components/image-cropper/image-cropper.stories.js.map +1 -1
- package/dist/components/stat/stat-card.d.ts +5 -3
- package/dist/components/stat/stat-card.d.ts.map +1 -1
- package/dist/components/stat/stat-card.js +12 -6
- package/dist/components/stat/stat-card.js.map +1 -1
- package/dist/components/stat/stat-cards.stories.d.ts +3 -2
- package/dist/components/stat/stat-cards.stories.d.ts.map +1 -1
- package/dist/components/stat/stat-cards.stories.js +2 -1
- package/dist/components/stat/stat-cards.stories.js.map +1 -1
- package/dist/components/stat/stat-extended-card.d.ts.map +1 -1
- package/dist/components/stat/stat-extended-card.stories.d.ts +2 -2
- package/dist/components/stat/stat-extended-card.stories.d.ts.map +1 -1
- package/dist/components/stat/stat-extended-card.stories.js.map +1 -1
- package/dist/components/ui/centered-spinner.d.ts +2 -2
- package/dist/components/ui/centered-spinner.d.ts.map +1 -1
- package/dist/components/ui/centered-spinner.js +1 -1
- package/dist/components/ui/centered-spinner.js.map +1 -1
- package/dist/components/ui/centered-spinner.stories.d.ts +2 -2
- package/dist/components/ui/centered-spinner.stories.d.ts.map +1 -1
- package/dist/components/ui/centered-spinner.stories.js.map +1 -1
- package/dist/components/ui/date-range-picker/date-range-picker.js.map +1 -1
- package/dist/components/ui/date-range-picker/date-range-picker.stories.js.map +1 -1
- package/dist/components/ui/disabled-button.d.ts +2 -2
- package/dist/components/ui/disabled-button.d.ts.map +1 -1
- package/dist/components/ui/disabled-button.js.map +1 -1
- package/dist/components/ui/line-text/line-text-body.stories.d.ts +2 -2
- package/dist/components/ui/line-text/line-text-body.stories.d.ts.map +1 -1
- package/dist/components/ui/line-text/line-text-body.stories.js.map +1 -1
- package/dist/components/ui/line-text/line-text-head.stories.d.ts +2 -2
- package/dist/components/ui/line-text/line-text-head.stories.d.ts.map +1 -1
- package/dist/components/ui/line-text/line-text-head.stories.js.map +1 -1
- package/dist/components/ui/line-text/line-text.d.ts +3 -3
- package/dist/components/ui/line-text/line-text.d.ts.map +1 -1
- package/dist/components/ui/line-text/line-text.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/ads-texts.d.ts +1 -1
- package/dist/utils/ads-texts.d.ts.map +1 -1
- package/dist/utils/ads-texts.js +1 -0
- package/dist/utils/ads-texts.js.map +1 -1
- package/dist/utils/format-big-numbers.d.ts +2 -0
- package/dist/utils/format-big-numbers.d.ts.map +1 -0
- package/dist/utils/format-big-numbers.js +17 -0
- package/dist/utils/format-big-numbers.js.map +1 -0
- package/dist/utils/helpers.d.ts +15 -0
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/helpers.js +41 -1
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/use-client-rect.js +2 -21
- package/dist/utils/use-client-rect.js.map +1 -1
- package/package.json +2 -2
- package/src/components/ads/ads-stat.tsx +3 -3
- package/src/components/charts/funnel-chart/components/funnel-chart.tsx +2 -2
- package/src/components/charts/funnel-chart/components/funnel-svg.tsx +2 -2
- package/src/components/charts/funnel-chart/funnel-chart.stories.tsx +2 -2
- package/src/components/charts/line-chart/components/body.tsx +4 -4
- package/src/components/charts/line-chart/components/container.tsx +2 -2
- package/src/components/charts/line-chart/components/hover-popover.tsx +4 -4
- package/src/components/charts/line-chart/components/sidebar.tsx +2 -2
- package/src/components/charts/line-chart/components/stuff.tsx +4 -4
- package/src/components/charts/line-chart/components/svg-bars.tsx +3 -3
- package/src/components/charts/line-chart/components/svg-body.tsx +4 -4
- package/src/components/charts/line-chart/components/svg-lines.tsx +3 -3
- package/src/components/charts/line-chart/line-chart.stories.tsx +1 -1
- package/src/components/charts/line-chart/utils/interfaces.ts +2 -2
- package/src/components/charts/pie-chart/components/pie-chart.tsx +20 -7
- package/src/components/charts/pie-chart/components/pie.tsx +42 -22
- package/src/components/charts/pie-chart/pie-chart.stories.tsx +20 -1
- package/src/components/charts/pie-chart/utils/const.ts +11 -6
- package/src/components/charts/pie-chart/utils/interface.ts +5 -2
- package/src/components/image-cropper/image-cropper.stories.tsx +8 -8
- package/src/components/image-cropper/image-cropper.tsx +2 -2
- package/src/components/stat/stat-card.tsx +17 -6
- package/src/components/stat/stat-cards.stories.tsx +5 -2
- package/src/components/stat/stat-extended-card.stories.tsx +2 -2
- package/src/components/stat/stat-extended-card.tsx +1 -1
- package/src/components/ui/centered-spinner.stories.tsx +2 -2
- package/src/components/ui/centered-spinner.tsx +2 -6
- package/src/components/ui/date-range-picker/date-range-picker.stories.tsx +2 -2
- package/src/components/ui/date-range-picker/date-range-picker.tsx +1 -1
- package/src/components/ui/disabled-button.tsx +2 -2
- package/src/components/ui/line-text/line-text-body.stories.tsx +2 -2
- package/src/components/ui/line-text/line-text-head.stories.tsx +2 -2
- package/src/components/ui/line-text/line-text.tsx +3 -11
- package/src/index.ts +1 -0
- package/src/utils/__tests__/format-big-numbers.test.ts +17 -0
- package/src/utils/__tests__/helpers.test.ts +31 -0
- package/src/utils/ads-texts.tsx +3 -0
- package/src/utils/format-big-numbers.ts +15 -0
- package/src/utils/helpers.ts +43 -0
- package/src/utils/use-client-rect.ts +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useCallback, useMemo, FC, Fragment } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { observer } from 'mobx-react';
|
|
4
4
|
import { useDependencies } from '@servicetitan/react-ioc';
|
|
@@ -10,7 +10,7 @@ import { periodDateTitleFormatter } from '../utils/labels';
|
|
|
10
10
|
import * as Styles from './hover-popover.module.less';
|
|
11
11
|
import { ColorTag } from './stuff';
|
|
12
12
|
|
|
13
|
-
export const HoverPopover:
|
|
13
|
+
export const HoverPopover: FC = observer(() => {
|
|
14
14
|
const [{ periods, resolution, hoveredIndex, metrics, display }, svgStore] = useDependencies(
|
|
15
15
|
LineChartStore,
|
|
16
16
|
SvgStore
|
|
@@ -42,7 +42,7 @@ export const HoverPopover: React.FC = observer(() => {
|
|
|
42
42
|
const partialWeek = !!period.partial;
|
|
43
43
|
|
|
44
44
|
return (
|
|
45
|
-
<
|
|
45
|
+
<Fragment>
|
|
46
46
|
<div
|
|
47
47
|
className={Styles.line}
|
|
48
48
|
style={{ left: svgStore.fpx(svgStore.periodX(hoveredIndex)) + '%' }}
|
|
@@ -73,6 +73,6 @@ export const HoverPopover: React.FC = observer(() => {
|
|
|
73
73
|
)
|
|
74
74
|
)}
|
|
75
75
|
</div>
|
|
76
|
-
</
|
|
76
|
+
</Fragment>
|
|
77
77
|
);
|
|
78
78
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FC } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { Stack } from '@servicetitan/design-system';
|
|
4
4
|
import * as Styles from './sidebar.module.less';
|
|
@@ -12,7 +12,7 @@ interface SidebarProps {
|
|
|
12
12
|
right?: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export const Sidebar:
|
|
15
|
+
export const Sidebar: FC<SidebarProps> = ({ settings, right, className, classNameTitle }) => {
|
|
16
16
|
const sidebarTitle = settings.title ? (
|
|
17
17
|
<Stack.Item
|
|
18
18
|
className={classNames(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { CSSProperties, useMemo, FC } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { BodyText, Stack } from '@servicetitan/design-system';
|
|
4
4
|
import * as Styles from './stuff.module.less';
|
|
@@ -13,7 +13,7 @@ interface ColorTagProps {
|
|
|
13
13
|
dashed?: boolean;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export const ColorTag:
|
|
16
|
+
export const ColorTag: FC<ColorTagProps> = ({ label, color, className, small, dashed }) => (
|
|
17
17
|
<Stack alignItems="center" className={className}>
|
|
18
18
|
<div
|
|
19
19
|
style={
|
|
@@ -39,7 +39,7 @@ interface MetricsTitleProps {
|
|
|
39
39
|
metrics: LineChartMetric[];
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export const MetricsTitle:
|
|
42
|
+
export const MetricsTitle: FC<MetricsTitleProps> = ({ metrics }) => (
|
|
43
43
|
<Stack alignItems="center">
|
|
44
44
|
{metrics.map(m => (
|
|
45
45
|
<ColorTag
|
|
@@ -53,7 +53,7 @@ export const MetricsTitle: React.FC<MetricsTitleProps> = ({ metrics }) => (
|
|
|
53
53
|
</Stack>
|
|
54
54
|
);
|
|
55
55
|
|
|
56
|
-
export const XAxisLabels:
|
|
56
|
+
export const XAxisLabels: FC<{
|
|
57
57
|
labels: ChartXLabels;
|
|
58
58
|
width: number;
|
|
59
59
|
left: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FC } from 'react';
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
3
|
import { useDependencies } from '@servicetitan/react-ioc';
|
|
4
4
|
import { ChartMetric } from '../utils/internal-interfaces';
|
|
@@ -10,7 +10,7 @@ interface SvgBarsProps {
|
|
|
10
10
|
isStackedBarChart?: boolean;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export const SvgBars:
|
|
13
|
+
export const SvgBars: FC<SvgBarsProps> = observer(({ metrics, isStackedBarChart }) => {
|
|
14
14
|
const [store] = useDependencies(SvgStore);
|
|
15
15
|
const { fpx, fpy, barWidth, length } = store;
|
|
16
16
|
const barWidthHalf = barWidth / 2;
|
|
@@ -65,7 +65,7 @@ interface SvgBarsHoverProps {
|
|
|
65
65
|
onLeave(ind: number): void;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export const SvgBarsHover:
|
|
68
|
+
export const SvgBarsHover: FC<SvgBarsHoverProps> = observer(({ onHover, onLeave }) => {
|
|
69
69
|
const [store] = useDependencies(SvgStore);
|
|
70
70
|
const { fpx, fpy, barWidth, length } = store;
|
|
71
71
|
const barWidthHalf = barWidth / 2;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FC } from 'react';
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
3
|
import tokens from '@servicetitan/tokens';
|
|
4
4
|
import { useDependencies } from '@servicetitan/react-ioc';
|
|
@@ -11,7 +11,7 @@ import { SvgBars, SvgBarsHover } from './svg-bars';
|
|
|
11
11
|
import { SvgLines } from './svg-lines';
|
|
12
12
|
import * as Styles from './svg.module.less';
|
|
13
13
|
|
|
14
|
-
const SvgGrid:
|
|
14
|
+
const SvgGrid: FC = () => {
|
|
15
15
|
const lines = [];
|
|
16
16
|
|
|
17
17
|
for (let i = 1; i <= 10; i += 1) {
|
|
@@ -39,7 +39,7 @@ interface SvgBodyProps {
|
|
|
39
39
|
metrics: ChartMetric[];
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export const SvgBody:
|
|
42
|
+
export const SvgBody: FC<SvgBodyProps> = observer(({ horizontalGrid, metrics }) => {
|
|
43
43
|
const [{ key }] = useDependencies(SvgStore);
|
|
44
44
|
const barMetrics = metrics.filter(m => m.type === 'bar');
|
|
45
45
|
const stackedBarMetrics = metrics.filter(m => m.type === 'stacked-bar');
|
|
@@ -71,7 +71,7 @@ interface SvgBodyHoverProps {
|
|
|
71
71
|
onValueLeave(ind: number): void;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
export const SvgBodyHover:
|
|
74
|
+
export const SvgBodyHover: FC<SvgBodyHoverProps> = ({ onValueHover, onValueLeave }) => (
|
|
75
75
|
<svg className={Styles.svgHover} viewBox="0 0 100 100" preserveAspectRatio="none">
|
|
76
76
|
<SvgBarsHover onHover={onValueHover} onLeave={onValueLeave} />
|
|
77
77
|
</svg>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useMemo, FC } from 'react';
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
3
|
import { useDependencies } from '@servicetitan/react-ioc';
|
|
4
4
|
import { ChartMetric } from '../utils/internal-interfaces';
|
|
@@ -11,7 +11,7 @@ interface SvgLineProps {
|
|
|
11
11
|
dashed: boolean;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const SvgLine:
|
|
14
|
+
const SvgLine: FC<SvgLineProps> = ({ color, points, dashed }) => (
|
|
15
15
|
<path
|
|
16
16
|
className={Styles.line}
|
|
17
17
|
d={points.map(([px, py], ind) => (ind === 0 ? 'M' : 'L') + `${px} ${py}`).join(' ')}
|
|
@@ -26,7 +26,7 @@ interface SvgLinesProps {
|
|
|
26
26
|
metrics: ChartMetric[];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export const SvgLines:
|
|
29
|
+
export const SvgLines: FC<SvgLinesProps> = observer(({ metrics }) => {
|
|
30
30
|
const [store] = useDependencies(SvgStore);
|
|
31
31
|
|
|
32
32
|
const lines = useMemo(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
2
|
|
|
3
3
|
export type LineChartResolution = 'hour' | 'day' | 'week' | 'month';
|
|
4
4
|
export type LineChartMetricType = 'line' | 'bar' | 'stacked-bar';
|
|
@@ -74,7 +74,7 @@ export interface LineChartProps extends LineChartData {
|
|
|
74
74
|
|
|
75
75
|
export interface LineChartCardProps extends LineChartProps {
|
|
76
76
|
title: string;
|
|
77
|
-
headerRight?:
|
|
77
|
+
headerRight?: ReactElement;
|
|
78
78
|
loading?: boolean;
|
|
79
79
|
grayControls?: boolean;
|
|
80
80
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useMemo, FC } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { BodyText, Stack, StatusLight } from '@servicetitan/design-system';
|
|
4
4
|
import { PieChartProps, PiePiece } from '../utils/interface';
|
|
5
|
-
import { convertSessionsToPieces } from '../utils/const';
|
|
5
|
+
import { convertSessionsToPieces, radiusRelativeDefault } from '../utils/const';
|
|
6
6
|
import { Pie } from './pie';
|
|
7
7
|
import * as Styles from './pie-chart.module.less';
|
|
8
8
|
|
|
9
|
-
const PieTitles:
|
|
9
|
+
const PieTitles: FC<{ title: string; pieces: PiePiece[] }> = ({ title, pieces }) => {
|
|
10
10
|
return (
|
|
11
11
|
<div className={classNames(Styles.titleWrapper, 'of-y-auto p-t-2')}>
|
|
12
12
|
<div>
|
|
@@ -28,20 +28,33 @@ const PieTitles: React.FC<{ title: string; pieces: PiePiece[] }> = ({ title, pie
|
|
|
28
28
|
);
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
export const PieChart:
|
|
31
|
+
export const PieChart: FC<PieChartProps> = ({
|
|
32
32
|
height,
|
|
33
33
|
width,
|
|
34
34
|
title,
|
|
35
35
|
sections,
|
|
36
36
|
popoverContent,
|
|
37
|
+
content,
|
|
38
|
+
radiusRelative = radiusRelativeDefault,
|
|
39
|
+
hideTitles = false,
|
|
37
40
|
}) => {
|
|
38
|
-
const pieces = useMemo(
|
|
41
|
+
const pieces = useMemo(
|
|
42
|
+
() => convertSessionsToPieces(sections, radiusRelative),
|
|
43
|
+
[sections, radiusRelative]
|
|
44
|
+
);
|
|
39
45
|
const style = useMemo(() => ({ height, width }), [height, width]);
|
|
40
46
|
|
|
41
47
|
return (
|
|
42
48
|
<div className="d-f flex-row" style={style}>
|
|
43
|
-
<Pie
|
|
44
|
-
|
|
49
|
+
<Pie
|
|
50
|
+
title={title}
|
|
51
|
+
pieces={pieces}
|
|
52
|
+
content={content}
|
|
53
|
+
popoverContent={popoverContent}
|
|
54
|
+
radiusRelative={radiusRelative}
|
|
55
|
+
hideTitles={hideTitles}
|
|
56
|
+
/>
|
|
57
|
+
{!hideTitles && <PieTitles title={title} pieces={pieces} />}
|
|
45
58
|
</div>
|
|
46
59
|
);
|
|
47
60
|
};
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useCallback, useMemo, useState, FC, Fragment } from 'react';
|
|
2
2
|
import tokens from '@servicetitan/tokens';
|
|
3
3
|
import { BodyText, Popover, Stack, StatusLight } from '@servicetitan/design-system';
|
|
4
4
|
|
|
5
5
|
import { useClientRect } from '../../../../utils/use-client-rect';
|
|
6
|
-
import { radiusRelative } from '../utils/const';
|
|
7
6
|
import { PieChartPopoverContentType, PiePiece } from '../utils/interface';
|
|
8
7
|
|
|
9
8
|
const chartPadding = 8;
|
|
10
9
|
const px = (value?: number) => `${value ?? 0}px`;
|
|
11
10
|
|
|
12
|
-
const PiePieceSvg:
|
|
11
|
+
const PiePieceSvg: FC<{
|
|
13
12
|
piece: PiePiece;
|
|
14
13
|
selected?: boolean;
|
|
15
14
|
}> = ({ piece: { id, color, opacity, points, text, path }, selected }) =>
|
|
@@ -48,7 +47,7 @@ const PiePieceSvg: React.FC<{
|
|
|
48
47
|
</g>
|
|
49
48
|
) : null;
|
|
50
49
|
|
|
51
|
-
const PiePieceHover:
|
|
50
|
+
const PiePieceHover: FC<{
|
|
52
51
|
piece: PiePiece;
|
|
53
52
|
onMouse(id: string, isEnter: boolean): void;
|
|
54
53
|
}> = ({ piece, onMouse }) => {
|
|
@@ -66,10 +65,11 @@ const PiePieceHover: React.FC<{
|
|
|
66
65
|
);
|
|
67
66
|
};
|
|
68
67
|
|
|
69
|
-
const PieSvg:
|
|
68
|
+
const PieSvg: FC<{
|
|
70
69
|
pieces: PiePiece[];
|
|
71
70
|
selectedIndex: number;
|
|
72
|
-
|
|
71
|
+
radiusRelative: number;
|
|
72
|
+
}> = ({ pieces, selectedIndex, radiusRelative }) => (
|
|
73
73
|
<svg
|
|
74
74
|
className="position-absolute"
|
|
75
75
|
style={{ inset: px(chartPadding) }}
|
|
@@ -87,10 +87,11 @@ const PieSvg: React.FC<{
|
|
|
87
87
|
</svg>
|
|
88
88
|
);
|
|
89
89
|
|
|
90
|
-
const PieSvgHover:
|
|
90
|
+
const PieSvgHover: FC<{
|
|
91
91
|
pieces: PiePiece[];
|
|
92
|
+
radiusRelative: number;
|
|
92
93
|
onMouse(id: string, isEnter: boolean): void;
|
|
93
|
-
}> = ({ pieces, onMouse }) => (
|
|
94
|
+
}> = ({ pieces, onMouse, radiusRelative }) => (
|
|
94
95
|
<svg
|
|
95
96
|
className="position-absolute z-global-nav"
|
|
96
97
|
style={{ inset: px(chartPadding) }}
|
|
@@ -104,11 +105,20 @@ const PieSvgHover: React.FC<{
|
|
|
104
105
|
</svg>
|
|
105
106
|
);
|
|
106
107
|
|
|
107
|
-
export const Pie:
|
|
108
|
+
export const Pie: FC<{
|
|
108
109
|
title: string;
|
|
109
110
|
pieces: PiePiece[];
|
|
111
|
+
radiusRelative: number;
|
|
112
|
+
content?: FC;
|
|
110
113
|
popoverContent?: PieChartPopoverContentType;
|
|
111
|
-
|
|
114
|
+
hideTitles?: boolean;
|
|
115
|
+
}> = ({
|
|
116
|
+
pieces,
|
|
117
|
+
popoverContent: PopoverContent,
|
|
118
|
+
content: PieContent,
|
|
119
|
+
radiusRelative,
|
|
120
|
+
hideTitles,
|
|
121
|
+
}) => {
|
|
112
122
|
const [selectedIndex, setSelectedIndex] = useState(-1);
|
|
113
123
|
const [rect, ref] = useClientRect();
|
|
114
124
|
|
|
@@ -153,7 +163,7 @@ export const Pie: React.FC<{
|
|
|
153
163
|
: { top: '', left: '' }
|
|
154
164
|
)
|
|
155
165
|
: [],
|
|
156
|
-
[pieces, container]
|
|
166
|
+
[pieces, container, radiusRelative]
|
|
157
167
|
);
|
|
158
168
|
|
|
159
169
|
return (
|
|
@@ -163,7 +173,7 @@ export const Pie: React.FC<{
|
|
|
163
173
|
No Data
|
|
164
174
|
</Stack>
|
|
165
175
|
) : (
|
|
166
|
-
<
|
|
176
|
+
<Fragment>
|
|
167
177
|
{triggersStyles
|
|
168
178
|
.filter(ts => !!ts.left && !!ts.top)
|
|
169
179
|
.map((ts, ind) => (
|
|
@@ -178,12 +188,14 @@ export const Pie: React.FC<{
|
|
|
178
188
|
>
|
|
179
189
|
{selectedIndex === ind && (
|
|
180
190
|
<Stack direction="column">
|
|
181
|
-
|
|
182
|
-
<
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
191
|
+
{!hideTitles && (
|
|
192
|
+
<Stack alignItems="center">
|
|
193
|
+
<StatusLight color={pieces[ind].color} />
|
|
194
|
+
<BodyText size="small" bold>
|
|
195
|
+
{pieces[ind].title}
|
|
196
|
+
</BodyText>
|
|
197
|
+
</Stack>
|
|
198
|
+
)}
|
|
187
199
|
{!!PopoverContent && (
|
|
188
200
|
<Stack.Item className="m-l-1">
|
|
189
201
|
<PopoverContent
|
|
@@ -199,10 +211,18 @@ export const Pie: React.FC<{
|
|
|
199
211
|
</Popover>
|
|
200
212
|
</div>
|
|
201
213
|
))}
|
|
202
|
-
|
|
203
|
-
<PieSvg
|
|
204
|
-
|
|
205
|
-
|
|
214
|
+
{!!PieContent && <PieContent />}
|
|
215
|
+
<PieSvg
|
|
216
|
+
pieces={pieces}
|
|
217
|
+
selectedIndex={selectedIndex}
|
|
218
|
+
radiusRelative={radiusRelative}
|
|
219
|
+
/>
|
|
220
|
+
<PieSvgHover
|
|
221
|
+
pieces={pieces}
|
|
222
|
+
onMouse={onMouse}
|
|
223
|
+
radiusRelative={radiusRelative}
|
|
224
|
+
/>
|
|
225
|
+
</Fragment>
|
|
206
226
|
)}
|
|
207
227
|
</div>
|
|
208
228
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { PieChart } from './index';
|
|
2
|
+
import { Eyebrow, Stack, BodyText } from '@servicetitan/design-system';
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
5
|
title: 'Marketing UI/charts/PieChart',
|
|
@@ -21,6 +21,25 @@ export const pieChart5AutoColor = () => (
|
|
|
21
21
|
/>
|
|
22
22
|
);
|
|
23
23
|
|
|
24
|
+
export const pieChartWithContent = () => (
|
|
25
|
+
<PieChart
|
|
26
|
+
title="Pie Chart"
|
|
27
|
+
height={300}
|
|
28
|
+
sections={[
|
|
29
|
+
{ title: 'New Customer', value: 61 },
|
|
30
|
+
{ title: 'Existing Customer', value: 90 },
|
|
31
|
+
]}
|
|
32
|
+
content={() => (
|
|
33
|
+
<Stack className="h-100" justifyContent="center" alignItems="center" direction="column">
|
|
34
|
+
<BodyText bold>244</BodyText>
|
|
35
|
+
<Eyebrow size="small">Total Leads</Eyebrow>
|
|
36
|
+
</Stack>
|
|
37
|
+
)}
|
|
38
|
+
radiusRelative={45}
|
|
39
|
+
hideTitles
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
|
|
24
43
|
export const pieChart1CustomColor = () => (
|
|
25
44
|
<PieChart
|
|
26
45
|
title="Pie Chart"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { PiecePoints, PieChartSection, PiePiece } from './interface';
|
|
2
2
|
import { formatNumber } from 'accounting';
|
|
3
3
|
|
|
4
|
-
export const
|
|
4
|
+
export const radiusRelativeDefault = 50;
|
|
5
5
|
const radiusInt = 20;
|
|
6
|
-
const
|
|
7
|
-
const
|
|
6
|
+
const getRadiusExt = (radiusRelative: number) => radiusRelative - 5; // need to have some space to stroke selected piece
|
|
7
|
+
const getRadiusMid = (radiusRelative: number) => (3 * radiusRelative) / 4;
|
|
8
8
|
|
|
9
9
|
const angleInitial = -0.5;
|
|
10
10
|
const lowestOpacity = 0.1;
|
|
@@ -25,7 +25,7 @@ const formatValue = (val: number): string => {
|
|
|
25
25
|
return `0.${valueDecimal}%`;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
const convertPointsToPath = (points: PiecePoints, wideAngle: boolean): string =>
|
|
28
|
+
const convertPointsToPath = (points: PiecePoints, wideAngle: boolean, radiusExt: number): string =>
|
|
29
29
|
`M ${points[3][0]},${points[3][1]} ` +
|
|
30
30
|
`L ${points[0][0]},${points[0][1]} ` +
|
|
31
31
|
`A ${radiusExt},${radiusExt} 0 ${wideAngle ? 1 : 0} 1 ${points[1][0]},${points[1][1]} ` +
|
|
@@ -33,10 +33,15 @@ const convertPointsToPath = (points: PiecePoints, wideAngle: boolean): string =>
|
|
|
33
33
|
`A ${radiusInt},${radiusInt} 0 ${wideAngle ? 1 : 0} 0 ${points[3][0]},${points[3][1]} ` +
|
|
34
34
|
`L ${points[0][0]},${points[0][1]} `;
|
|
35
35
|
|
|
36
|
-
export const convertSessionsToPieces = <T>(
|
|
36
|
+
export const convertSessionsToPieces = <T>(
|
|
37
|
+
sections: PieChartSection<T>[],
|
|
38
|
+
radiusRelative: number
|
|
39
|
+
): PiePiece<T>[] => {
|
|
37
40
|
const total = sections.reduce((sum, curr) => sum + curr.value, 0);
|
|
38
41
|
const opacityStep = (1 - lowestOpacity) / (Math.max(sections.length, 2) - 1);
|
|
39
42
|
let angleSum = 0;
|
|
43
|
+
const radiusExt = getRadiusExt(radiusRelative);
|
|
44
|
+
const radiusMid = getRadiusMid(radiusRelative);
|
|
40
45
|
|
|
41
46
|
const pieces = sections
|
|
42
47
|
.slice()
|
|
@@ -71,7 +76,7 @@ export const convertSessionsToPieces = <T>(sections: PieChartSection<T>[]): PieP
|
|
|
71
76
|
value: s.value,
|
|
72
77
|
points,
|
|
73
78
|
path: points
|
|
74
|
-
? convertPointsToPath(points, angleEnd - angleStart >= Math.PI)
|
|
79
|
+
? convertPointsToPath(points, angleEnd - angleStart >= Math.PI, radiusExt)
|
|
75
80
|
: undefined,
|
|
76
81
|
};
|
|
77
82
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FC } from 'react';
|
|
2
2
|
|
|
3
3
|
export interface PieChartSection<T> {
|
|
4
4
|
title: string;
|
|
@@ -7,7 +7,7 @@ export interface PieChartSection<T> {
|
|
|
7
7
|
data?: T;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export type PieChartPopoverContentType<T = any> =
|
|
10
|
+
export type PieChartPopoverContentType<T = any> = FC<{
|
|
11
11
|
index: number;
|
|
12
12
|
value: number;
|
|
13
13
|
data?: T;
|
|
@@ -18,8 +18,11 @@ export interface PieChartProps<T = any> {
|
|
|
18
18
|
title: string;
|
|
19
19
|
sections: PieChartSection<T>[];
|
|
20
20
|
popoverContent?: PieChartPopoverContentType<T>;
|
|
21
|
+
content?: FC<T>;
|
|
21
22
|
height?: number;
|
|
22
23
|
width?: number;
|
|
24
|
+
radiusRelative?: number;
|
|
25
|
+
hideTitles?: boolean;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
export type PiecePoint = [number, number];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FC, ChangeEvent, useRef, useState, Fragment } from 'react';
|
|
2
2
|
import { ImageCropper } from './image-cropper';
|
|
3
3
|
|
|
4
4
|
export default {
|
|
@@ -7,13 +7,13 @@ export default {
|
|
|
7
7
|
parameters: {},
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
const BasicExample:
|
|
11
|
-
const [imageToEdit, setImageToEdit] =
|
|
12
|
-
const [croppedImage, setCroppedImage] =
|
|
10
|
+
const BasicExample: FC = () => {
|
|
11
|
+
const [imageToEdit, setImageToEdit] = useState<File>();
|
|
12
|
+
const [croppedImage, setCroppedImage] = useState<string>();
|
|
13
13
|
|
|
14
|
-
const imageCropper =
|
|
14
|
+
const imageCropper = useRef<ImageCropper>(null);
|
|
15
15
|
|
|
16
|
-
const handleFileChange = (e:
|
|
16
|
+
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
17
17
|
const file = e.target.files?.[0];
|
|
18
18
|
if (!file) {
|
|
19
19
|
return;
|
|
@@ -38,7 +38,7 @@ const BasicExample: React.FC = () => {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
|
-
<
|
|
41
|
+
<Fragment>
|
|
42
42
|
<input type="file" accept="image/png, image/jpeg" onChange={handleFileChange} />
|
|
43
43
|
{imageToEdit && (
|
|
44
44
|
<div style={{ marginTop: 30 }}>
|
|
@@ -62,7 +62,7 @@ const BasicExample: React.FC = () => {
|
|
|
62
62
|
</div>
|
|
63
63
|
</div>
|
|
64
64
|
)}
|
|
65
|
-
</
|
|
65
|
+
</Fragment>
|
|
66
66
|
);
|
|
67
67
|
};
|
|
68
68
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Component } from 'react';
|
|
2
2
|
import { observable, action, makeObservable } from 'mobx';
|
|
3
3
|
import { observer } from 'mobx-react';
|
|
4
4
|
import ReactCrop, { Crop } from 'react-image-crop';
|
|
@@ -13,7 +13,7 @@ interface ImageCropperProps {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
@observer
|
|
16
|
-
export class ImageCropper extends
|
|
16
|
+
export class ImageCropper extends Component<ImageCropperProps> {
|
|
17
17
|
@observable private src?: string;
|
|
18
18
|
@observable private crop?: Crop;
|
|
19
19
|
@observable private image?: HTMLImageElement;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FC } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import {
|
|
4
4
|
BodyText,
|
|
@@ -42,9 +42,10 @@ interface StatDiffProps {
|
|
|
42
42
|
inverted?: boolean;
|
|
43
43
|
neutral?: boolean;
|
|
44
44
|
className?: string;
|
|
45
|
+
diffPercentOnly?: boolean;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
export const StatDiff:
|
|
48
|
+
export const StatDiff: FC<StatDiffProps> = ({
|
|
48
49
|
value,
|
|
49
50
|
prev,
|
|
50
51
|
size,
|
|
@@ -52,6 +53,7 @@ export const StatDiff: React.FC<StatDiffProps> = ({
|
|
|
52
53
|
inverted,
|
|
53
54
|
neutral,
|
|
54
55
|
className,
|
|
56
|
+
diffPercentOnly = false,
|
|
55
57
|
}) => {
|
|
56
58
|
const percents = format === 'percent';
|
|
57
59
|
const [absDiff, diffPercent, isIncrease] = calculateDiff(value ?? 0, prev ?? 0, percents);
|
|
@@ -61,10 +63,16 @@ export const StatDiff: React.FC<StatDiffProps> = ({
|
|
|
61
63
|
if (percents) {
|
|
62
64
|
text += formatDifferencePercentage(absDiff, isIncrease);
|
|
63
65
|
} else {
|
|
64
|
-
|
|
66
|
+
const diffPercentage = formatDifferencePercentage(diffPercent, isIncrease);
|
|
65
67
|
|
|
66
|
-
if (
|
|
67
|
-
text +=
|
|
68
|
+
if (diffPercentOnly) {
|
|
69
|
+
text += `${diffPercentage}`;
|
|
70
|
+
} else {
|
|
71
|
+
text += `${formatDifference(absDiff, isIncrease, format)}`;
|
|
72
|
+
|
|
73
|
+
if (diffPercent !== 0) {
|
|
74
|
+
text += ` (${diffPercentage})`;
|
|
75
|
+
}
|
|
68
76
|
}
|
|
69
77
|
}
|
|
70
78
|
|
|
@@ -101,9 +109,10 @@ export interface StatCardProps {
|
|
|
101
109
|
fill?: boolean;
|
|
102
110
|
valueOnly?: boolean;
|
|
103
111
|
className?: string;
|
|
112
|
+
diffPercentOnly?: boolean;
|
|
104
113
|
}
|
|
105
114
|
|
|
106
|
-
export const StatCard:
|
|
115
|
+
export const StatCard: FC<StatCardProps> = ({
|
|
107
116
|
title,
|
|
108
117
|
description,
|
|
109
118
|
value,
|
|
@@ -117,6 +126,7 @@ export const StatCard: React.FC<StatCardProps> = ({
|
|
|
117
126
|
fill,
|
|
118
127
|
valueOnly,
|
|
119
128
|
className,
|
|
129
|
+
diffPercentOnly = false,
|
|
120
130
|
}) => {
|
|
121
131
|
const format = money ? 'money' : percent ? 'percent' : rate ? 'rate' : 'number';
|
|
122
132
|
const val = value === undefined ? '\u00A0' : formatValue(value, format);
|
|
@@ -145,6 +155,7 @@ export const StatCard: React.FC<StatCardProps> = ({
|
|
|
145
155
|
format={format}
|
|
146
156
|
inverted={inverted}
|
|
147
157
|
neutral={neutral}
|
|
158
|
+
diffPercentOnly={diffPercentOnly}
|
|
148
159
|
/>
|
|
149
160
|
)}
|
|
150
161
|
</Stack>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
2
|
import { StatCard } from './stat-card';
|
|
3
3
|
|
|
4
4
|
export default {
|
|
@@ -7,9 +7,12 @@ export default {
|
|
|
7
7
|
parameters: {},
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
const w = (cb: () =>
|
|
10
|
+
const w = (cb: () => ReactElement) => () => <div style={{ width: '400px' }}>{cb()}</div>;
|
|
11
11
|
|
|
12
12
|
export const statCardNumber = w(() => <StatCard title="number" value={133} prev={1000} />);
|
|
13
|
+
export const statDiffNumberPercentOnly = w(() => (
|
|
14
|
+
<StatCard title="number" value={133} prev={1000} diffPercentOnly />
|
|
15
|
+
));
|
|
13
16
|
export const statCardMoney = w(() => <StatCard title="money" value={10000} prev={11000} money />);
|
|
14
17
|
export const statCardPercentInverted = w(() => (
|
|
15
18
|
<StatCard title="percent" value={0.27} prev={0.27333} percent inverted />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
2
|
import { StatExtendedCard } from './stat-extended-card';
|
|
3
3
|
|
|
4
4
|
export default {
|
|
@@ -7,7 +7,7 @@ export default {
|
|
|
7
7
|
parameters: {},
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
const w = (cb: () =>
|
|
10
|
+
const w = (cb: () => ReactElement) => () => <div style={{ width: '400px' }}>{cb()}</div>;
|
|
11
11
|
|
|
12
12
|
export const statCardMoneyWithPrev = w(() => (
|
|
13
13
|
<StatExtendedCard
|