@servicetitan/marketing-ui 5.11.1 → 6.0.1

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.
Files changed (99) hide show
  1. package/dist/components/charts/common/color-tag.d.ts +15 -0
  2. package/dist/components/charts/common/color-tag.d.ts.map +1 -0
  3. package/dist/components/charts/common/color-tag.js +79 -0
  4. package/dist/components/charts/common/color-tag.js.map +1 -0
  5. package/dist/components/charts/common/color-tag.module.less +23 -0
  6. package/dist/components/charts/common/color-tag.module.less.d.ts +6 -0
  7. package/dist/components/charts/common/index.d.ts +2 -0
  8. package/dist/components/charts/common/index.d.ts.map +1 -0
  9. package/dist/components/charts/common/index.js +3 -0
  10. package/dist/components/charts/common/index.js.map +1 -0
  11. package/dist/components/charts/funnel-chart/components/funnel-chart.d.ts.map +1 -1
  12. package/dist/components/charts/funnel-chart/components/funnel-chart.js +115 -70
  13. package/dist/components/charts/funnel-chart/components/funnel-chart.js.map +1 -1
  14. package/dist/components/charts/funnel-chart/components/funnel-chart.module.less +28 -10
  15. package/dist/components/charts/funnel-chart/components/funnel-chart.module.less.d.ts +3 -1
  16. package/dist/components/charts/funnel-chart/components/funnel-svg.d.ts +2 -0
  17. package/dist/components/charts/funnel-chart/components/funnel-svg.d.ts.map +1 -1
  18. package/dist/components/charts/funnel-chart/components/funnel-svg.js +72 -31
  19. package/dist/components/charts/funnel-chart/components/funnel-svg.js.map +1 -1
  20. package/dist/components/charts/funnel-chart/funnel-chart.stories.d.ts.map +1 -1
  21. package/dist/components/charts/funnel-chart/utils/const.d.ts +1 -1
  22. package/dist/components/charts/funnel-chart/utils/const.js +1 -1
  23. package/dist/components/charts/funnel-chart/utils/const.js.map +1 -1
  24. package/dist/components/charts/funnel-chart/utils/interface.d.ts +1 -0
  25. package/dist/components/charts/funnel-chart/utils/interface.d.ts.map +1 -1
  26. package/dist/components/charts/funnel-chart/utils/interface.js.map +1 -1
  27. package/dist/components/charts/funnel-chart/utils/svg-rounded-path.d.ts +2 -0
  28. package/dist/components/charts/funnel-chart/utils/svg-rounded-path.d.ts.map +1 -0
  29. package/dist/components/charts/funnel-chart/utils/svg-rounded-path.js +47 -0
  30. package/dist/components/charts/funnel-chart/utils/svg-rounded-path.js.map +1 -0
  31. package/dist/components/charts/line-chart/components/hover-popover.d.ts.map +1 -1
  32. package/dist/components/charts/line-chart/components/hover-popover.js +13 -7
  33. package/dist/components/charts/line-chart/components/hover-popover.js.map +1 -1
  34. package/dist/components/charts/line-chart/components/hover-popover.module.less +10 -0
  35. package/dist/components/charts/line-chart/components/hover-popover.module.less.d.ts +2 -0
  36. package/dist/components/charts/line-chart/components/stuff.d.ts +0 -8
  37. package/dist/components/charts/line-chart/components/stuff.d.ts.map +1 -1
  38. package/dist/components/charts/line-chart/components/stuff.js +6 -20
  39. package/dist/components/charts/line-chart/components/stuff.js.map +1 -1
  40. package/dist/components/charts/line-chart/components/stuff.module.less +0 -16
  41. package/dist/components/charts/line-chart/components/stuff.module.less.d.ts +0 -3
  42. package/dist/components/charts/line-chart/components/svg-bars.d.ts.map +1 -1
  43. package/dist/components/charts/line-chart/components/svg-bars.js +97 -13
  44. package/dist/components/charts/line-chart/components/svg-bars.js.map +1 -1
  45. package/dist/components/charts/line-chart/line-chart.stories.d.ts.map +1 -1
  46. package/dist/components/charts/line-chart/stores/line-chart.store.d.ts +5 -0
  47. package/dist/components/charts/line-chart/stores/line-chart.store.d.ts.map +1 -1
  48. package/dist/components/charts/line-chart/stores/line-chart.store.js +41 -1
  49. package/dist/components/charts/line-chart/stores/line-chart.store.js.map +1 -1
  50. package/dist/components/charts/line-chart/utils/interfaces.d.ts +4 -0
  51. package/dist/components/charts/line-chart/utils/interfaces.d.ts.map +1 -1
  52. package/dist/components/charts/line-chart/utils/interfaces.js.map +1 -1
  53. package/dist/components/charts/line-chart/utils/labels.js +1 -1
  54. package/dist/components/charts/line-chart/utils/labels.js.map +1 -1
  55. package/dist/components/charts/pie-chart/components/pie-chart.d.ts.map +1 -1
  56. package/dist/components/charts/pie-chart/components/pie-chart.js +24 -13
  57. package/dist/components/charts/pie-chart/components/pie-chart.js.map +1 -1
  58. package/dist/components/charts/pie-chart/components/pie-chart.module.less +15 -0
  59. package/dist/components/charts/pie-chart/components/pie-chart.module.less.d.ts +1 -0
  60. package/dist/components/charts/pie-chart/components/pie.d.ts.map +1 -1
  61. package/dist/components/charts/pie-chart/components/pie.js +106 -28
  62. package/dist/components/charts/pie-chart/components/pie.js.map +1 -1
  63. package/dist/components/charts/pie-chart/pie-chart.stories.d.ts.map +1 -1
  64. package/dist/components/charts/pie-chart/utils/const.js +1 -1
  65. package/dist/components/charts/pie-chart/utils/const.js.map +1 -1
  66. package/dist/components/stat/stat-card.d.ts.map +1 -1
  67. package/dist/components/stat/stat-card.js +28 -12
  68. package/dist/components/stat/stat-card.js.map +1 -1
  69. package/package.json +5 -3
  70. package/src/components/charts/common/color-tag.module.less +23 -0
  71. package/src/components/charts/common/color-tag.module.less.d.ts +6 -0
  72. package/src/components/charts/common/color-tag.tsx +92 -0
  73. package/src/components/charts/common/index.ts +1 -0
  74. package/src/components/charts/funnel-chart/components/funnel-chart.module.less +28 -10
  75. package/src/components/charts/funnel-chart/components/funnel-chart.module.less.d.ts +3 -1
  76. package/src/components/charts/funnel-chart/components/funnel-chart.tsx +107 -78
  77. package/src/components/charts/funnel-chart/components/funnel-svg.tsx +91 -23
  78. package/src/components/charts/funnel-chart/funnel-chart.stories.tsx +3 -1
  79. package/src/components/charts/funnel-chart/utils/const.ts +1 -1
  80. package/src/components/charts/funnel-chart/utils/interface.ts +1 -0
  81. package/src/components/charts/funnel-chart/utils/svg-rounded-path.ts +86 -0
  82. package/src/components/charts/line-chart/components/hover-popover.module.less +10 -0
  83. package/src/components/charts/line-chart/components/hover-popover.module.less.d.ts +2 -0
  84. package/src/components/charts/line-chart/components/hover-popover.tsx +29 -9
  85. package/src/components/charts/line-chart/components/stuff.module.less +0 -16
  86. package/src/components/charts/line-chart/components/stuff.module.less.d.ts +0 -3
  87. package/src/components/charts/line-chart/components/stuff.tsx +4 -30
  88. package/src/components/charts/line-chart/components/svg-bars.tsx +106 -9
  89. package/src/components/charts/line-chart/line-chart.stories.tsx +13 -8
  90. package/src/components/charts/line-chart/stores/line-chart.store.ts +39 -1
  91. package/src/components/charts/line-chart/utils/interfaces.ts +4 -0
  92. package/src/components/charts/line-chart/utils/labels.ts +1 -1
  93. package/src/components/charts/pie-chart/components/pie-chart.module.less +15 -0
  94. package/src/components/charts/pie-chart/components/pie-chart.module.less.d.ts +1 -0
  95. package/src/components/charts/pie-chart/components/pie-chart.tsx +23 -13
  96. package/src/components/charts/pie-chart/components/pie.tsx +104 -40
  97. package/src/components/charts/pie-chart/pie-chart.stories.tsx +3 -4
  98. package/src/components/charts/pie-chart/utils/const.ts +1 -1
  99. package/src/components/stat/stat-card.tsx +44 -16
@@ -1,20 +1,22 @@
1
1
  import { useCallback, useMemo, useState, FC, Fragment } from 'react';
2
2
  import { tokens } from '@servicetitan/tokens/core';
3
- import { BodyText, Popover, Stack, StatusLight } from '@servicetitan/design-system';
3
+ import { BodyText, Popover, Stack } from '@servicetitan/design-system';
4
4
 
5
5
  import { useClientRect } from '../../../../utils/use-client-rect';
6
6
  import { PieChartPopoverContentType, PiePiece, PopoverDirection } from '../utils/interface';
7
+ import { ColorTag } from '../../common';
7
8
 
8
9
  const chartPadding = 8;
9
10
  const px = (value?: number) => `${value ?? 0}px`;
11
+ const GAP_PX = 4;
10
12
 
11
13
  const PiePieceSvg: FC<{
12
14
  piece: PiePiece;
13
15
  selected?: boolean;
14
- }> = ({ piece: { id, color, opacity, points, text, path }, selected }) =>
16
+ }> = ({ piece: { id, color, points, text, path }, selected }) =>
15
17
  points && path ? (
16
18
  <g id={id}>
17
- {!!selected && (
19
+ {selected && (
18
20
  <path
19
21
  d={path}
20
22
  fill={tokens.colorWhite}
@@ -25,25 +27,43 @@ const PiePieceSvg: FC<{
25
27
  />
26
28
  )}
27
29
  <path d={path} fill={color} />
30
+ {points[4] && (
31
+ <g transform={`translate(${points[4][0]}, ${points[4][1]})`} pointerEvents="none">
32
+ {(() => {
33
+ const fontSize = 3;
34
+ const height = 6;
35
+ const radius = height / 2;
28
36
 
29
- <svg
30
- x={points[4][0] - 10}
31
- y={points[4][1] - (points[4][1] > 0 ? 3 : 1)}
32
- width={20}
33
- height={6}
34
- >
35
- <text
36
- x="50%"
37
- y="50%"
38
- fontSize={4}
39
- fontWeight={600}
40
- fill={opacity > 0.3 ? tokens.colorWhite : tokens.colorBlack}
41
- dominantBaseline="middle"
42
- textAnchor="middle"
43
- >
44
- {text}
45
- </text>
46
- </svg>
37
+ const width = Math.max(8, text.length);
38
+
39
+ return (
40
+ <Fragment>
41
+ <rect
42
+ x={-width / 2}
43
+ y={-height / 2}
44
+ width={width}
45
+ height={height}
46
+ rx={radius}
47
+ ry={radius}
48
+ fill="rgba(255,255,255,0.80)"
49
+ strokeWidth={0.6}
50
+ />
51
+ <text
52
+ x="0"
53
+ y="0"
54
+ fontSize={fontSize}
55
+ fontWeight={600}
56
+ textAnchor="middle"
57
+ dominantBaseline="middle"
58
+ fill={tokens.colorBlack}
59
+ >
60
+ {text}
61
+ </text>
62
+ </Fragment>
63
+ );
64
+ })()}
65
+ </g>
66
+ )}
47
67
  </g>
48
68
  ) : null;
49
69
 
@@ -69,23 +89,65 @@ const PieSvg: FC<{
69
89
  pieces: PiePiece[];
70
90
  selectedIndex: number;
71
91
  radiusRelative: number;
72
- }> = ({ pieces, selectedIndex, radiusRelative }) => (
73
- <svg
74
- className="position-absolute"
75
- style={{ inset: px(chartPadding) }}
76
- viewBox={
77
- `-${radiusRelative} -${radiusRelative} ` + `${radiusRelative * 2} ${radiusRelative * 2}`
78
- }
79
- >
80
- {pieces.map((p, index) =>
81
- p.path ? <PiePieceSvg piece={p} key={p.id} selected={index === selectedIndex} /> : null
82
- )}
92
+ }> = ({ pieces, selectedIndex, radiusRelative }) => {
93
+ const piePiece = pieces.find(p => p.points);
94
+ const innerR = piePiece ? Math.hypot(piePiece.points![3][0], piePiece.points![3][1]) : 0;
95
+ const outerR = piePiece ? Math.hypot(piePiece.points![1][0], piePiece.points![1][1]) : 0;
83
96
 
84
- {selectedIndex >= 0 && selectedIndex < pieces.length && (
85
- <use xlinkHref={pieces[selectedIndex].id} />
86
- )}
87
- </svg>
88
- );
97
+ const nonZeroPieces = pieces.filter(p => p.points && p.path);
98
+ const boundaries = nonZeroPieces.map(p => {
99
+ const [outerX, outerY] = p.points![1];
100
+ const len = Math.hypot(outerX, outerY) || 1;
101
+ return { x: outerX / len, y: outerY / len };
102
+ });
103
+
104
+ return (
105
+ <svg
106
+ className="position-absolute"
107
+ style={{ inset: `${chartPadding}px` }}
108
+ viewBox={`-${radiusRelative} -${radiusRelative} ${radiusRelative * 2} ${radiusRelative * 2}`}
109
+ >
110
+ {pieces.map((p, i) =>
111
+ p.path ? <PiePieceSvg piece={p} key={p.id} selected={i === selectedIndex} /> : null
112
+ )}
113
+ {nonZeroPieces.length > 1 && innerR > 0 && outerR > 0 && (
114
+ <Fragment>
115
+ <defs>
116
+ <mask id="ring-mask">
117
+ <rect
118
+ x={-outerR}
119
+ y={-outerR}
120
+ width={outerR * 2}
121
+ height={outerR * 2}
122
+ fill="black"
123
+ />
124
+ <circle cx="0" cy="0" r={outerR} fill="white" />
125
+ <circle cx="0" cy="0" r={innerR} fill="black" />
126
+ </mask>
127
+ </defs>
128
+ <g mask="url(#ring-mask)" pointerEvents="none">
129
+ {boundaries.map(boundary => (
130
+ <line
131
+ key={`sep-${boundary.x}-${boundary.y}`}
132
+ x1={boundary.x * innerR}
133
+ y1={boundary.y * innerR}
134
+ x2={boundary.x * outerR}
135
+ y2={boundary.y * outerR}
136
+ stroke="#fff"
137
+ strokeWidth={GAP_PX}
138
+ vectorEffect="non-scaling-stroke"
139
+ strokeLinecap="round"
140
+ />
141
+ ))}
142
+ </g>
143
+ </Fragment>
144
+ )}
145
+ {selectedIndex >= 0 && selectedIndex < pieces.length && (
146
+ <use xlinkHref={`#${pieces[selectedIndex].id}`} />
147
+ )}
148
+ </svg>
149
+ );
150
+ };
89
151
 
90
152
  const PieSvgHover: FC<{
91
153
  pieces: PiePiece[];
@@ -140,7 +202,6 @@ export const Pie: FC<{
140
202
  styles: height
141
203
  ? {
142
204
  width: px(Math.max(250, height)),
143
- overflow: 'hidden',
144
205
  }
145
206
  : {},
146
207
  };
@@ -170,7 +231,7 @@ export const Pie: FC<{
170
231
  );
171
232
 
172
233
  return (
173
- <div ref={ref} style={container.styles} className="position-relative">
234
+ <div ref={ref} style={container.styles} className="position-relative h-100">
174
235
  {pieces.every(p => !p.path) ? (
175
236
  <Stack className="h-100" justifyContent="center" alignItems="center">
176
237
  No Data
@@ -197,7 +258,10 @@ export const Pie: FC<{
197
258
  >
198
259
  {!hideTitles && (
199
260
  <Stack alignItems="center">
200
- <StatusLight color={pieces[ind].color} />
261
+ <ColorTag
262
+ label=""
263
+ color={pieces[ind].color}
264
+ />
201
265
  <BodyText size="small" bold>
202
266
  {pieces[ind].title}
203
267
  </BodyText>
@@ -10,7 +10,7 @@ export default {
10
10
  export const pieChart5AutoColor = () => (
11
11
  <PieChart
12
12
  title="Pie Chart"
13
- height={250}
13
+ height={220}
14
14
  sections={[
15
15
  { title: 'lorem', value: 99 },
16
16
  { title: 'ipsum', value: 88 },
@@ -25,7 +25,7 @@ export const pieChart5AutoColor = () => (
25
25
  export const pieChartWithContent = () => (
26
26
  <PieChart
27
27
  title="Pie Chart"
28
- height={300}
28
+ height={284}
29
29
  sections={[
30
30
  { title: 'New Customer', value: 61 },
31
31
  { title: 'Existing Customer', value: 90 },
@@ -36,8 +36,7 @@ export const pieChartWithContent = () => (
36
36
  <Eyebrow size="small">Total Leads</Eyebrow>
37
37
  </Stack>
38
38
  )}
39
- radiusRelative={45}
40
- hideTitles
39
+ radiusRelative={40}
41
40
  />
42
41
  );
43
42
 
@@ -4,7 +4,7 @@ import { formatNumber } from 'accounting';
4
4
  export const radiusRelativeDefault = 50;
5
5
  const radiusInt = 20;
6
6
  const getRadiusExt = (radiusRelative: number) => radiusRelative - 5; // need to have some space to stroke selected piece
7
- const getRadiusMid = (radiusRelative: number) => (3 * radiusRelative) / 4;
7
+ const getRadiusMid = (radiusRelative: number) => (2.75 * radiusRelative) / 4;
8
8
 
9
9
  const angleInitial = -0.5;
10
10
  const lowestOpacity = 0.1;
@@ -10,6 +10,9 @@ import {
10
10
  } from '@servicetitan/design-system';
11
11
  import * as Styles from './stat-card.module.less';
12
12
  import { formatValue, NumberFormatter } from '../../utils/formatters';
13
+ import { Icon } from '@servicetitan/anvil2';
14
+ import TrendingUpSVG from '@servicetitan/anvil2/assets/icons/material/round/trending_up.svg';
15
+ import TrendingDownSVG from '@servicetitan/anvil2/assets/icons/material/round/trending_down.svg';
13
16
 
14
17
  const calculateDiff = (
15
18
  value: number,
@@ -64,7 +67,26 @@ export const StatDiff: FC<StatDiffProps> = ({
64
67
  }) => {
65
68
  const percents = format === 'percent';
66
69
  const [absDiff, diffPercent, isIncrease] = calculateDiff(value ?? 0, prev ?? 0, percents);
67
- const diff = absDiff === 0 ? '' : isIncrease ? '▲ ' : '▼ ';
70
+ const diff =
71
+ absDiff === 0 ? (
72
+ ''
73
+ ) : (
74
+ <Icon
75
+ svg={isIncrease ? TrendingUpSVG : TrendingDownSVG}
76
+ color={
77
+ neutral
78
+ ? 'neutral-200'
79
+ : inverted
80
+ ? isIncrease
81
+ ? 'red'
82
+ : 'green'
83
+ : isIncrease
84
+ ? 'green'
85
+ : 'red'
86
+ }
87
+ className="m-r-half m-t-half"
88
+ />
89
+ );
68
90
  let text = '';
69
91
 
70
92
  if (percents) {
@@ -84,22 +106,28 @@ export const StatDiff: FC<StatDiffProps> = ({
84
106
  }
85
107
 
86
108
  return (
87
- <BodyText
88
- className={classNames(
89
- Styles.statDiff,
90
- {
91
- 'c-red-500': !neutral && (inverted ? isIncrease : !isIncrease),
92
- 'c-green-500': !neutral && (inverted ? !isIncrease : isIncrease),
93
- 'c-neutral-200': !!neutral,
94
- },
95
- className
96
- )}
97
- size={size ?? 'small'}
98
- data-cy="stat-diff-value"
109
+ <Stack
110
+ className={classNames(Styles.statDiff, className)}
111
+ justifyContent="center"
112
+ alignItems="center"
99
113
  >
100
- <span>{diff}</span>
101
- {value === undefined ? '\u00A0' : text}
102
- </BodyText>
114
+ <Stack.Item>
115
+ <span>{diff}</span>
116
+ </Stack.Item>
117
+ <Stack.Item>
118
+ <BodyText
119
+ size={size ?? 'small'}
120
+ data-cy="stat-diff-value"
121
+ className={classNames({
122
+ 'c-red-500': !neutral && (inverted ? isIncrease : !isIncrease),
123
+ 'c-green-500': !neutral && (inverted ? !isIncrease : isIncrease),
124
+ 'c-neutral-200': !!neutral,
125
+ })}
126
+ >
127
+ {value === undefined ? '\u00A0' : text}
128
+ </BodyText>
129
+ </Stack.Item>
130
+ </Stack>
103
131
  );
104
132
  };
105
133