@servicetitan/marketing-ui 6.0.2 → 6.1.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/charts/funnel-chart/components/funnel-svg.js +3 -3
- package/dist/components/charts/funnel-chart/components/funnel-svg.js.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/hover-popover.d.ts.map +1 -1
- package/dist/components/charts/line-chart/components/hover-popover.js +69 -44
- package/dist/components/charts/line-chart/components/hover-popover.js.map +1 -1
- package/dist/components/charts/line-chart/components/hover-popover.module.less +26 -1
- package/dist/components/charts/line-chart/components/hover-popover.module.less.d.ts +3 -0
- package/dist/components/charts/line-chart/components/stuff.js +2 -1
- package/dist/components/charts/line-chart/components/stuff.js.map +1 -1
- package/dist/components/charts/line-chart/components/svg-bars.js +1 -1
- package/dist/components/charts/line-chart/components/svg-bars.js.map +1 -1
- package/dist/components/charts/pie-chart/components/pie-chart.module.less +4 -0
- package/dist/components/charts/pie-chart/components/pie-chart.module.less.d.ts +1 -0
- package/dist/components/charts/pie-chart/components/pie.d.ts.map +1 -1
- package/dist/components/charts/pie-chart/components/pie.js +2 -0
- package/dist/components/charts/pie-chart/components/pie.js.map +1 -1
- package/package.json +2 -2
- package/src/components/charts/funnel-chart/components/funnel-svg.tsx +3 -3
- package/src/components/charts/line-chart/components/body.tsx +1 -1
- package/src/components/charts/line-chart/components/hover-popover.module.less +26 -1
- package/src/components/charts/line-chart/components/hover-popover.module.less.d.ts +3 -0
- package/src/components/charts/line-chart/components/hover-popover.tsx +85 -47
- package/src/components/charts/line-chart/components/stuff.tsx +1 -1
- package/src/components/charts/line-chart/components/svg-bars.tsx +1 -1
- package/src/components/charts/pie-chart/components/pie-chart.module.less +4 -0
- package/src/components/charts/pie-chart/components/pie-chart.module.less.d.ts +1 -0
- package/src/components/charts/pie-chart/components/pie.tsx +2 -0
|
@@ -18,8 +18,8 @@ export const FunnelPyramidSvg = ({ colors, topSideLength = defaultTopSideLength,
|
|
|
18
18
|
yt: yStep * ind,
|
|
19
19
|
yb: ind === len - 1 ? 100 : yStep * (ind + 1),
|
|
20
20
|
xtl: xOffset + lStep * ind,
|
|
21
|
-
xtr:
|
|
22
|
-
xbr:
|
|
21
|
+
xtr: 99 - lStep * ind,
|
|
22
|
+
xbr: 99 - lStep * (ind + 1),
|
|
23
23
|
xbl: xOffset + lStep * (ind + 1),
|
|
24
24
|
c: color
|
|
25
25
|
};
|
|
@@ -83,7 +83,7 @@ export const FunnelPyramidSvg = ({ colors, topSideLength = defaultTopSideLength,
|
|
|
83
83
|
sections.map((section, i)=>{
|
|
84
84
|
const isTop = i === 0;
|
|
85
85
|
const isBottom = i === sections.length - 1;
|
|
86
|
-
const d = roundedPath(section.xtl, section.xtr, section.xbr, section.xbl, section.yt, section.yb, isTop, isBottom, 2.5);
|
|
86
|
+
const d = roundedPath(section.xtl, section.xtr, section.xbr, section.xbl, section.yt + 1, section.yb, isTop, isBottom, 2.5);
|
|
87
87
|
return /*#__PURE__*/ _jsx("path", {
|
|
88
88
|
d: d,
|
|
89
89
|
fill: colors[i],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/charts/funnel-chart/components/funnel-svg.tsx"],"sourcesContent":["import { useMemo, FC, useEffect } from 'react';\nimport { tokens } from '@servicetitan/tokens/core';\nimport { defaultBottomSideLength, defaultTopSideLength } from '../utils/const';\nimport { roundedPath } from '../utils/svg-rounded-path';\n\nconst st = (v: number) => v.toFixed(2);\n\nexport interface FunnelPyramidSvgProps {\n colors: string[];\n topSideLength?: number;\n bottomSideLength?: number;\n outlineColors?: (string | undefined)[];\n onRowAnchors?: (ysPct: number[]) => void;\n}\n\nexport const FunnelPyramidSvg: FC<FunnelPyramidSvgProps> = ({\n colors,\n topSideLength = defaultTopSideLength,\n bottomSideLength = defaultBottomSideLength,\n outlineColors,\n onRowAnchors,\n}) => {\n const sections = useMemo(() => {\n if (!colors.length) {\n return [];\n }\n\n const len = colors.length;\n const yStep = 100 / len;\n const lStep = (topSideLength - Math.min(bottomSideLength, topSideLength)) / (len * 2);\n const xOffset = 100 - topSideLength;\n\n return colors.map((color, ind) => {\n return {\n yt: yStep * ind,\n yb: ind === len - 1 ? 100 : yStep * (ind + 1),\n xtl: xOffset + lStep * ind,\n xtr: 100 - lStep * ind,\n xbr: 100 - lStep * (ind + 1),\n xbl: xOffset + lStep * (ind + 1),\n c: color,\n };\n });\n }, [colors, topSideLength, bottomSideLength]);\n\n useEffect(() => {\n onRowAnchors?.(sections.map(s => (s.yt + s.yb) / 2));\n }, [onRowAnchors, sections]);\n\n const pointAlong = (start: number, end: number, t: number) => (1 - t) * start + t * end;\n const pxToViewBoxUnits = (px: number) => (px / 200) * 100;\n\n const GAP_PX = 4;\n const SEAM_PX = 1;\n\n const gapVU = pxToViewBoxUnits(GAP_PX);\n const seamVU = pxToViewBoxUnits(SEAM_PX);\n\n const lines = useMemo(() => {\n return sections.map((section, i) => {\n const y = (section.yt + section.yb) / 2;\n\n const height = section.yb - section.yt;\n const t = (y - section.yt) / height;\n const xLeftAtMid = pointAlong(section.xtl, section.xbl, t);\n const x2 = Math.max(0, xLeftAtMid - gapVU);\n\n return { id: i, y, x2 };\n });\n }, [sections, gapVU]);\n\n const seams = sections.slice(0, -1).map((s, i) => ({\n key: i,\n x1: s.xbl,\n x2: s.xbr,\n yTopEdge: s.yb - gapVU / 2 + seamVU / 2,\n color: outlineColors?.[i],\n }));\n\n return (\n <svg\n width=\"100%\"\n height=\"100%\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <svg\n width=\"100%\"\n height=\"100%\"\n x=\"0%\"\n y=\"0\"\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n {sections.map((section, i) => {\n const isTop = i === 0;\n const isBottom = i === sections.length - 1;\n\n const d = roundedPath(\n section.xtl,\n section.xtr,\n section.xbr,\n section.xbl,\n section.yt,\n section.yb,\n isTop,\n isBottom,\n 2.5\n );\n\n return (\n <path\n key={`section-${section.xbl}-${section.yb}-${section.xbr}-${section.yb}`}\n d={d}\n fill={colors[i]}\n stroke={outlineColors?.[i]}\n strokeWidth={1}\n vectorEffect=\"non-scaling-stroke\"\n strokeLinejoin=\"round\"\n />\n );\n })}\n {sections.slice(0, -1).map(section => (\n <line\n key={`gap-${section.xbl}-${section.yb}-${section.xbr}-${section.yb}`}\n x1={st(section.xbl)}\n y1={st(section.yb)}\n x2={st(section.xbr)}\n y2={st(section.yb)}\n stroke=\"#fff\"\n strokeWidth={GAP_PX}\n vectorEffect=\"non-scaling-stroke\"\n strokeLinecap=\"round\"\n />\n ))}\n {seams.map(({ key, x1, x2, yTopEdge, color }) =>\n color ? (\n <line\n key={`seam-${key}`}\n x1={st(x1)}\n y1={st(yTopEdge)}\n x2={st(x2)}\n y2={st(yTopEdge)}\n stroke={color}\n strokeWidth={SEAM_PX}\n vectorEffect=\"non-scaling-stroke\"\n strokeLinecap=\"round\"\n />\n ) : null\n )}\n </svg>\n <svg\n width=\"100%\"\n height=\"100%\"\n x=\"0\"\n y=\"0\"\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n {lines.map(({ id, x2, y }) => (\n <line\n key={id}\n x1=\"0\"\n y1={st(y)}\n x2={st(x2)}\n y2={st(y)}\n stroke={tokens.colorNeutral60}\n strokeWidth={0.5}\n />\n ))}\n </svg>\n </svg>\n );\n};\n"],"names":["useMemo","useEffect","tokens","defaultBottomSideLength","defaultTopSideLength","roundedPath","st","v","toFixed","FunnelPyramidSvg","colors","topSideLength","bottomSideLength","outlineColors","onRowAnchors","sections","length","len","yStep","lStep","Math","min","xOffset","map","color","ind","yt","yb","xtl","xtr","xbr","xbl","c","s","pointAlong","start","end","t","pxToViewBoxUnits","px","GAP_PX","SEAM_PX","gapVU","seamVU","lines","section","i","y","height","xLeftAtMid","x2","max","id","seams","slice","key","x1","yTopEdge","svg","width","preserveAspectRatio","xmlns","x","viewBox","isTop","isBottom","d","path","fill","stroke","strokeWidth","vectorEffect","strokeLinejoin","line","y1","y2","strokeLinecap","colorNeutral60"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components/charts/funnel-chart/components/funnel-svg.tsx"],"sourcesContent":["import { useMemo, FC, useEffect } from 'react';\nimport { tokens } from '@servicetitan/tokens/core';\nimport { defaultBottomSideLength, defaultTopSideLength } from '../utils/const';\nimport { roundedPath } from '../utils/svg-rounded-path';\n\nconst st = (v: number) => v.toFixed(2);\n\nexport interface FunnelPyramidSvgProps {\n colors: string[];\n topSideLength?: number;\n bottomSideLength?: number;\n outlineColors?: (string | undefined)[];\n onRowAnchors?: (ysPct: number[]) => void;\n}\n\nexport const FunnelPyramidSvg: FC<FunnelPyramidSvgProps> = ({\n colors,\n topSideLength = defaultTopSideLength,\n bottomSideLength = defaultBottomSideLength,\n outlineColors,\n onRowAnchors,\n}) => {\n const sections = useMemo(() => {\n if (!colors.length) {\n return [];\n }\n\n const len = colors.length;\n const yStep = 100 / len;\n const lStep = (topSideLength - Math.min(bottomSideLength, topSideLength)) / (len * 2);\n const xOffset = 100 - topSideLength;\n\n return colors.map((color, ind) => {\n return {\n yt: yStep * ind,\n yb: ind === len - 1 ? 100 : yStep * (ind + 1),\n xtl: xOffset + lStep * ind,\n xtr: 99 - lStep * ind,\n xbr: 99 - lStep * (ind + 1),\n xbl: xOffset + lStep * (ind + 1),\n c: color,\n };\n });\n }, [colors, topSideLength, bottomSideLength]);\n\n useEffect(() => {\n onRowAnchors?.(sections.map(s => (s.yt + s.yb) / 2));\n }, [onRowAnchors, sections]);\n\n const pointAlong = (start: number, end: number, t: number) => (1 - t) * start + t * end;\n const pxToViewBoxUnits = (px: number) => (px / 200) * 100;\n\n const GAP_PX = 4;\n const SEAM_PX = 1;\n\n const gapVU = pxToViewBoxUnits(GAP_PX);\n const seamVU = pxToViewBoxUnits(SEAM_PX);\n\n const lines = useMemo(() => {\n return sections.map((section, i) => {\n const y = (section.yt + section.yb) / 2;\n\n const height = section.yb - section.yt;\n const t = (y - section.yt) / height;\n const xLeftAtMid = pointAlong(section.xtl, section.xbl, t);\n const x2 = Math.max(0, xLeftAtMid - gapVU);\n\n return { id: i, y, x2 };\n });\n }, [sections, gapVU]);\n\n const seams = sections.slice(0, -1).map((s, i) => ({\n key: i,\n x1: s.xbl,\n x2: s.xbr,\n yTopEdge: s.yb - gapVU / 2 + seamVU / 2,\n color: outlineColors?.[i],\n }));\n\n return (\n <svg\n width=\"100%\"\n height=\"100%\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <svg\n width=\"100%\"\n height=\"100%\"\n x=\"0%\"\n y=\"0\"\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n {sections.map((section, i) => {\n const isTop = i === 0;\n const isBottom = i === sections.length - 1;\n\n const d = roundedPath(\n section.xtl,\n section.xtr,\n section.xbr,\n section.xbl,\n section.yt + 1,\n section.yb,\n isTop,\n isBottom,\n 2.5\n );\n\n return (\n <path\n key={`section-${section.xbl}-${section.yb}-${section.xbr}-${section.yb}`}\n d={d}\n fill={colors[i]}\n stroke={outlineColors?.[i]}\n strokeWidth={1}\n vectorEffect=\"non-scaling-stroke\"\n strokeLinejoin=\"round\"\n />\n );\n })}\n {sections.slice(0, -1).map(section => (\n <line\n key={`gap-${section.xbl}-${section.yb}-${section.xbr}-${section.yb}`}\n x1={st(section.xbl)}\n y1={st(section.yb)}\n x2={st(section.xbr)}\n y2={st(section.yb)}\n stroke=\"#fff\"\n strokeWidth={GAP_PX}\n vectorEffect=\"non-scaling-stroke\"\n strokeLinecap=\"round\"\n />\n ))}\n {seams.map(({ key, x1, x2, yTopEdge, color }) =>\n color ? (\n <line\n key={`seam-${key}`}\n x1={st(x1)}\n y1={st(yTopEdge)}\n x2={st(x2)}\n y2={st(yTopEdge)}\n stroke={color}\n strokeWidth={SEAM_PX}\n vectorEffect=\"non-scaling-stroke\"\n strokeLinecap=\"round\"\n />\n ) : null\n )}\n </svg>\n <svg\n width=\"100%\"\n height=\"100%\"\n x=\"0\"\n y=\"0\"\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n {lines.map(({ id, x2, y }) => (\n <line\n key={id}\n x1=\"0\"\n y1={st(y)}\n x2={st(x2)}\n y2={st(y)}\n stroke={tokens.colorNeutral60}\n strokeWidth={0.5}\n />\n ))}\n </svg>\n </svg>\n );\n};\n"],"names":["useMemo","useEffect","tokens","defaultBottomSideLength","defaultTopSideLength","roundedPath","st","v","toFixed","FunnelPyramidSvg","colors","topSideLength","bottomSideLength","outlineColors","onRowAnchors","sections","length","len","yStep","lStep","Math","min","xOffset","map","color","ind","yt","yb","xtl","xtr","xbr","xbl","c","s","pointAlong","start","end","t","pxToViewBoxUnits","px","GAP_PX","SEAM_PX","gapVU","seamVU","lines","section","i","y","height","xLeftAtMid","x2","max","id","seams","slice","key","x1","yTopEdge","svg","width","preserveAspectRatio","xmlns","x","viewBox","isTop","isBottom","d","path","fill","stroke","strokeWidth","vectorEffect","strokeLinejoin","line","y1","y2","strokeLinecap","colorNeutral60"],"mappings":"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"}
|
|
@@ -112,8 +112,8 @@ export const Body = provide({
|
|
|
112
112
|
}),
|
|
113
113
|
display.metricsTitlePosition === 'bottom' && /*#__PURE__*/ _jsx(Stack, {
|
|
114
114
|
direction: "row",
|
|
115
|
-
justifyContent: "
|
|
116
|
-
className: "p-3",
|
|
115
|
+
justifyContent: "flex-start",
|
|
116
|
+
className: "p-t-3 p-x-5",
|
|
117
117
|
children: /*#__PURE__*/ _jsx(MetricsTitle, {
|
|
118
118
|
metrics: metrics
|
|
119
119
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/charts/line-chart/components/body.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, FC, Fragment } from 'react';\nimport { observer } from 'mobx-react';\nimport classNames from 'classnames';\nimport { Stack } from '@servicetitan/design-system';\nimport { provide, useDependencies } from '@servicetitan/react-ioc';\n\nimport { useClientRect } from '../../../../utils/use-client-rect';\nimport { LineChartStore } from '../stores/line-chart.store';\nimport { SvgStore } from '../stores/svg.store';\nimport { getXLabels } from '../utils/labels';\n\nimport { Sidebar } from './sidebar';\nimport { MetricsTitle, XAxisLabels } from './stuff';\nimport { SvgBody, SvgBodyHover } from './svg-body';\nimport { HoverPopover } from './hover-popover';\nimport * as Styles from './body.module.less';\n\nexport const Body: FC<{ classNameTitle?: string }> = provide({ singletons: [SvgStore] })(\n observer(({ classNameTitle }) => {\n const [{ display, metrics, left, right, periods, resolution, setHoveredIndex }, svgStore] =\n useDependencies(LineChartStore, SvgStore);\n\n useEffect(() => {\n svgStore.init(periods.length, metrics, left?.maxValue ?? 0, right?.maxValue ?? 0);\n }, [svgStore, periods, metrics, left, right]);\n const [rect, ref] = useClientRect();\n\n const onBarHover = useCallback(\n (ind: number) => setHoveredIndex(ind, true),\n [setHoveredIndex]\n );\n\n const onBarLeave = useCallback(\n (ind: number) => setHoveredIndex(ind, false),\n [setHoveredIndex]\n );\n\n const labels = useMemo(\n () => (display.xLabels ? getXLabels(periods, resolution, rect?.width ?? 0) : []),\n [display, periods, resolution, rect]\n );\n\n return (\n <div className=\"d-f flex-column\">\n <Stack alignItems=\"stretch\">\n {left && <Sidebar settings={left} classNameTitle={classNameTitle} />}\n <Stack.Item fill>\n <div\n ref={ref}\n className={classNames(Styles.chartWrapper, 'border-bottom', {\n 'border-left': !!left && display.yLeft,\n 'border-right': !!right && display.yRight,\n })}\n >\n {periods.length ? (\n <Fragment>\n {periods.length !== labels.length && (\n <div className={Styles.labelBorders}>\n {labels\n .map(([text, flex], ind) => [flex, `${ind}${text}`])\n .map(([flex, key]) => (\n <div key={key} style={{ flex }} />\n ))}\n </div>\n )}\n\n <SvgBody horizontalGrid={display.yGrid} metrics={metrics} />\n <HoverPopover />\n <SvgBodyHover\n onValueHover={onBarHover}\n onValueLeave={onBarLeave}\n />\n </Fragment>\n ) : (\n <Stack\n className=\"h-100\"\n justifyContent=\"center\"\n alignItems=\"center\"\n >\n No Data\n </Stack>\n )}\n </div>\n </Stack.Item>\n {right && <Sidebar settings={right} right classNameTitle={classNameTitle} />}\n </Stack>\n {display.xLabels && (\n <XAxisLabels\n labels={labels}\n width={rect?.width ?? 0}\n left={left?.width ?? 0}\n right={right?.width ?? 0}\n labelsMerged={periods.length !== labels.length}\n hasBars={\n !!metrics.filter(\n m =>\n m.type === 'bar' ||\n m.type === 'stacked-bar' ||\n m.type === 'grouped-bar'\n ).length\n }\n />\n )}\n {display.metricsTitlePosition === 'bottom' && (\n <Stack direction=\"row\" justifyContent=\"
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components/charts/line-chart/components/body.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, FC, Fragment } from 'react';\nimport { observer } from 'mobx-react';\nimport classNames from 'classnames';\nimport { Stack } from '@servicetitan/design-system';\nimport { provide, useDependencies } from '@servicetitan/react-ioc';\n\nimport { useClientRect } from '../../../../utils/use-client-rect';\nimport { LineChartStore } from '../stores/line-chart.store';\nimport { SvgStore } from '../stores/svg.store';\nimport { getXLabels } from '../utils/labels';\n\nimport { Sidebar } from './sidebar';\nimport { MetricsTitle, XAxisLabels } from './stuff';\nimport { SvgBody, SvgBodyHover } from './svg-body';\nimport { HoverPopover } from './hover-popover';\nimport * as Styles from './body.module.less';\n\nexport const Body: FC<{ classNameTitle?: string }> = provide({ singletons: [SvgStore] })(\n observer(({ classNameTitle }) => {\n const [{ display, metrics, left, right, periods, resolution, setHoveredIndex }, svgStore] =\n useDependencies(LineChartStore, SvgStore);\n\n useEffect(() => {\n svgStore.init(periods.length, metrics, left?.maxValue ?? 0, right?.maxValue ?? 0);\n }, [svgStore, periods, metrics, left, right]);\n const [rect, ref] = useClientRect();\n\n const onBarHover = useCallback(\n (ind: number) => setHoveredIndex(ind, true),\n [setHoveredIndex]\n );\n\n const onBarLeave = useCallback(\n (ind: number) => setHoveredIndex(ind, false),\n [setHoveredIndex]\n );\n\n const labels = useMemo(\n () => (display.xLabels ? getXLabels(periods, resolution, rect?.width ?? 0) : []),\n [display, periods, resolution, rect]\n );\n\n return (\n <div className=\"d-f flex-column\">\n <Stack alignItems=\"stretch\">\n {left && <Sidebar settings={left} classNameTitle={classNameTitle} />}\n <Stack.Item fill>\n <div\n ref={ref}\n className={classNames(Styles.chartWrapper, 'border-bottom', {\n 'border-left': !!left && display.yLeft,\n 'border-right': !!right && display.yRight,\n })}\n >\n {periods.length ? (\n <Fragment>\n {periods.length !== labels.length && (\n <div className={Styles.labelBorders}>\n {labels\n .map(([text, flex], ind) => [flex, `${ind}${text}`])\n .map(([flex, key]) => (\n <div key={key} style={{ flex }} />\n ))}\n </div>\n )}\n\n <SvgBody horizontalGrid={display.yGrid} metrics={metrics} />\n <HoverPopover />\n <SvgBodyHover\n onValueHover={onBarHover}\n onValueLeave={onBarLeave}\n />\n </Fragment>\n ) : (\n <Stack\n className=\"h-100\"\n justifyContent=\"center\"\n alignItems=\"center\"\n >\n No Data\n </Stack>\n )}\n </div>\n </Stack.Item>\n {right && <Sidebar settings={right} right classNameTitle={classNameTitle} />}\n </Stack>\n {display.xLabels && (\n <XAxisLabels\n labels={labels}\n width={rect?.width ?? 0}\n left={left?.width ?? 0}\n right={right?.width ?? 0}\n labelsMerged={periods.length !== labels.length}\n hasBars={\n !!metrics.filter(\n m =>\n m.type === 'bar' ||\n m.type === 'stacked-bar' ||\n m.type === 'grouped-bar'\n ).length\n }\n />\n )}\n {display.metricsTitlePosition === 'bottom' && (\n <Stack direction=\"row\" justifyContent=\"flex-start\" className=\"p-t-3 p-x-5\">\n <MetricsTitle metrics={metrics} />\n </Stack>\n )}\n </div>\n );\n })\n);\n"],"names":["useCallback","useEffect","useMemo","Fragment","observer","classNames","Stack","provide","useDependencies","useClientRect","LineChartStore","SvgStore","getXLabels","Sidebar","MetricsTitle","XAxisLabels","SvgBody","SvgBodyHover","HoverPopover","Styles","Body","singletons","classNameTitle","display","metrics","left","right","periods","resolution","setHoveredIndex","svgStore","init","length","maxValue","rect","ref","onBarHover","ind","onBarLeave","labels","xLabels","width","div","className","alignItems","settings","Item","fill","chartWrapper","yLeft","yRight","labelBorders","map","text","flex","key","style","horizontalGrid","yGrid","onValueHover","onValueLeave","justifyContent","labelsMerged","hasBars","filter","m","type","metricsTitlePosition","direction"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hover-popover.d.ts","sourceRoot":"","sources":["../../../../../src/components/charts/line-chart/components/hover-popover.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"hover-popover.d.ts","sourceRoot":"","sources":["../../../../../src/components/charts/line-chart/components/hover-popover.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA2D,EAAE,EAAiB,MAAM,OAAO,CAAC;AAgBnG,eAAO,MAAM,YAAY,EAAE,EAuHzB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useMemo,
|
|
2
|
+
import { useCallback, useMemo, useRef, useLayoutEffect, useState } from 'react';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
4
|
import { observer } from 'mobx-react';
|
|
5
5
|
import { useDependencies } from '@servicetitan/react-ioc';
|
|
@@ -11,72 +11,97 @@ import { periodDateTitleFormatter } from '../utils/labels';
|
|
|
11
11
|
import * as Styles from './hover-popover.module.less';
|
|
12
12
|
import { ColorTag } from '../../common';
|
|
13
13
|
import { Text } from '@servicetitan/anvil2';
|
|
14
|
+
const CHART_HEIGHT_PX = 400;
|
|
15
|
+
const OFFSET_PX = 16;
|
|
14
16
|
export const HoverPopover = observer(()=>{
|
|
15
17
|
const [{ periods, resolution, hoveredIndex, metrics, display, formattedTotalAt, totalLabel, stackedTotals }, svgStore] = useDependencies(LineChartStore, SvgStore);
|
|
18
|
+
const isChartLeftSide = hoveredIndex < periods.length / 2;
|
|
16
19
|
const formatDateTitle = useMemo(()=>periodDateTitleFormatter[resolution], [
|
|
17
20
|
resolution
|
|
18
21
|
]);
|
|
19
22
|
const formatValue = useCallback((title, value, isRight)=>getFormatter(isRight ? display.metricsRightFormat : display.metricsLeftFormat)(value) + ' ' + title, [
|
|
20
23
|
display
|
|
21
24
|
]);
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const popRef = useRef(null);
|
|
26
|
+
const [popH, setPopH] = useState(0);
|
|
27
|
+
useLayoutEffect(()=>{
|
|
28
|
+
if (!popRef.current) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const rect = popRef.current.getBoundingClientRect();
|
|
32
|
+
if (rect.height && Math.abs(rect.height - popH) > 0.5) {
|
|
33
|
+
setPopH(rect.height);
|
|
28
34
|
}
|
|
35
|
+
}, [
|
|
36
|
+
hoveredIndex,
|
|
37
|
+
metrics,
|
|
38
|
+
display,
|
|
39
|
+
popH
|
|
40
|
+
]);
|
|
41
|
+
const popoverStyle = useMemo(()=>{
|
|
42
|
+
const posX = svgStore.periodX(hoveredIndex);
|
|
43
|
+
const yHeights = metrics.filter((m)=>m.values[hoveredIndex] !== undefined).map((m)=>svgStore.periodY(m, hoveredIndex));
|
|
44
|
+
const barHeight = yHeights.length ? stackedTotals ? yHeights.reduce((a, b)=>a + b, 0) : Math.max(...yHeights) : 0;
|
|
45
|
+
const barHeightPercentRaw = svgStore.fpy(Math.max(0, Math.min(100, barHeight)));
|
|
46
|
+
const barHeightPercent = Math.max(0, Math.min(100, Number(barHeightPercentRaw) || 0));
|
|
47
|
+
const barTopPositionPx = barHeightPercent / 100 * CHART_HEIGHT_PX;
|
|
48
|
+
const availableSpaceBelow = Math.max(0, CHART_HEIGHT_PX - barTopPositionPx - popH);
|
|
49
|
+
const popoverOffsetPx = Math.min(OFFSET_PX, availableSpaceBelow);
|
|
29
50
|
return {
|
|
30
|
-
|
|
51
|
+
top: `${barHeightPercent}%`,
|
|
52
|
+
transform: `translateY(${popoverOffsetPx}px)`,
|
|
53
|
+
position: 'absolute',
|
|
54
|
+
...isChartLeftSide ? {
|
|
55
|
+
left: `${svgStore.fpx(posX + 2)}%`
|
|
56
|
+
} : {
|
|
57
|
+
right: `${svgStore.fpx(102 - posX)}%`
|
|
58
|
+
}
|
|
31
59
|
};
|
|
32
60
|
}, [
|
|
33
61
|
svgStore,
|
|
34
62
|
hoveredIndex,
|
|
35
|
-
|
|
63
|
+
isChartLeftSide,
|
|
64
|
+
metrics,
|
|
65
|
+
stackedTotals,
|
|
66
|
+
popH
|
|
36
67
|
]);
|
|
37
68
|
if (hoveredIndex < 0 || hoveredIndex >= periods.length) {
|
|
38
69
|
return null;
|
|
39
70
|
}
|
|
40
71
|
const period = periods[hoveredIndex];
|
|
41
72
|
const partialWeek = !!period.partial;
|
|
42
|
-
return /*#__PURE__*/ _jsxs(
|
|
73
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
74
|
+
ref: popRef,
|
|
75
|
+
className: classNames(Styles.popover, 'border border-radius-1 p-1'),
|
|
76
|
+
style: popoverStyle,
|
|
43
77
|
children: [
|
|
44
78
|
/*#__PURE__*/ _jsx("div", {
|
|
45
|
-
className: Styles.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
79
|
+
className: classNames(Styles.arrow, isChartLeftSide ? Styles.arrowLeft : Styles.arrowRight)
|
|
80
|
+
}),
|
|
81
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
82
|
+
size: "small",
|
|
83
|
+
variant: "headline",
|
|
84
|
+
el: "h6",
|
|
85
|
+
children: stackedTotals ? `${formattedTotalAt(hoveredIndex)} ${totalLabel} | ${formatDateTitle(period)}` : formatDateTitle(period)
|
|
86
|
+
}),
|
|
87
|
+
partialWeek && /*#__PURE__*/ _jsx(BodyText, {
|
|
88
|
+
size: "xsmall",
|
|
89
|
+
subdued: true,
|
|
90
|
+
children: "Partial week"
|
|
49
91
|
}),
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
children: "Partial week"
|
|
64
|
-
}),
|
|
65
|
-
metrics.map((m)=>{
|
|
66
|
-
var _m_opts, _m_opts1, _m_opts2, _m_opts3, _m_opts4;
|
|
67
|
-
return m.values[hoveredIndex] !== undefined && /*#__PURE__*/ _jsx(ColorTag, {
|
|
68
|
-
small: true,
|
|
69
|
-
label: formatValue(m.title, m.values[hoveredIndex], m.isRight),
|
|
70
|
-
color: m.color,
|
|
71
|
-
className: "m-t-1",
|
|
72
|
-
dashed: (_m_opts = m.opts) === null || _m_opts === void 0 ? void 0 : _m_opts.dashed,
|
|
73
|
-
pattern: (_m_opts1 = m.opts) === null || _m_opts1 === void 0 ? void 0 : _m_opts1.pattern,
|
|
74
|
-
outlineColor: (_m_opts2 = m.opts) === null || _m_opts2 === void 0 ? void 0 : _m_opts2.outlineColor,
|
|
75
|
-
strokeColor: (_m_opts3 = m.opts) === null || _m_opts3 === void 0 ? void 0 : _m_opts3.strokeColor,
|
|
76
|
-
colorTagClassName: ((_m_opts4 = m.opts) === null || _m_opts4 === void 0 ? void 0 : _m_opts4.pattern) === 'outline' ? Styles.colorTagOutlined : Styles.colorTag
|
|
77
|
-
}, m.title);
|
|
78
|
-
})
|
|
79
|
-
]
|
|
92
|
+
metrics.map((m)=>{
|
|
93
|
+
var _m_opts, _m_opts1, _m_opts2, _m_opts3, _m_opts4;
|
|
94
|
+
return m.values[hoveredIndex] !== undefined && /*#__PURE__*/ _jsx(ColorTag, {
|
|
95
|
+
small: true,
|
|
96
|
+
label: formatValue(m.title, m.values[hoveredIndex], m.isRight),
|
|
97
|
+
color: m.color,
|
|
98
|
+
className: "m-t-1",
|
|
99
|
+
dashed: (_m_opts = m.opts) === null || _m_opts === void 0 ? void 0 : _m_opts.dashed,
|
|
100
|
+
pattern: (_m_opts1 = m.opts) === null || _m_opts1 === void 0 ? void 0 : _m_opts1.pattern,
|
|
101
|
+
outlineColor: (_m_opts2 = m.opts) === null || _m_opts2 === void 0 ? void 0 : _m_opts2.outlineColor,
|
|
102
|
+
strokeColor: (_m_opts3 = m.opts) === null || _m_opts3 === void 0 ? void 0 : _m_opts3.strokeColor,
|
|
103
|
+
colorTagClassName: ((_m_opts4 = m.opts) === null || _m_opts4 === void 0 ? void 0 : _m_opts4.pattern) === 'outline' ? Styles.colorTagOutlined : Styles.colorTag
|
|
104
|
+
}, m.title);
|
|
80
105
|
})
|
|
81
106
|
]
|
|
82
107
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/charts/line-chart/components/hover-popover.tsx"],"sourcesContent":["import { useCallback, useMemo, FC,
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components/charts/line-chart/components/hover-popover.tsx"],"sourcesContent":["import { useCallback, useMemo, useRef, useLayoutEffect, useState, FC, CSSProperties } from 'react';\nimport classNames from 'classnames';\nimport { observer } from 'mobx-react';\nimport { useDependencies } from '@servicetitan/react-ioc';\nimport { BodyText } from '@servicetitan/design-system';\nimport { LineChartStore } from '../stores/line-chart.store';\nimport { SvgStore } from '../stores/svg.store';\nimport { getFormatter } from '../utils/formatters';\nimport { periodDateTitleFormatter } from '../utils/labels';\nimport * as Styles from './hover-popover.module.less';\nimport { ColorTag } from '../../common';\nimport { Text } from '@servicetitan/anvil2';\n\nconst CHART_HEIGHT_PX = 400;\nconst OFFSET_PX = 16;\n\nexport const HoverPopover: FC = observer(() => {\n const [\n {\n periods,\n resolution,\n hoveredIndex,\n metrics,\n display,\n formattedTotalAt,\n totalLabel,\n stackedTotals,\n },\n svgStore,\n ] = useDependencies(LineChartStore, SvgStore);\n const isChartLeftSide = hoveredIndex < periods.length / 2;\n\n const formatDateTitle = useMemo(() => periodDateTitleFormatter[resolution], [resolution]);\n const formatValue = useCallback(\n (title: string, value: number, isRight: boolean) =>\n getFormatter(isRight ? display.metricsRightFormat : display.metricsLeftFormat)(value) +\n ' ' +\n title,\n [display]\n );\n\n const popRef = useRef<HTMLDivElement | null>(null);\n const [popH, setPopH] = useState(0);\n\n useLayoutEffect(() => {\n if (!popRef.current) {\n return;\n }\n const rect = popRef.current.getBoundingClientRect();\n if (rect.height && Math.abs(rect.height - popH) > 0.5) {\n setPopH(rect.height);\n }\n }, [hoveredIndex, metrics, display, popH]);\n\n const popoverStyle = useMemo<CSSProperties>(() => {\n const posX = svgStore.periodX(hoveredIndex);\n\n const yHeights = metrics\n .filter(m => m.values[hoveredIndex] !== undefined)\n .map(m => svgStore.periodY(m, hoveredIndex));\n\n const barHeight = yHeights.length\n ? stackedTotals\n ? yHeights.reduce((a, b) => a + b, 0)\n : Math.max(...yHeights)\n : 0;\n const barHeightPercentRaw = svgStore.fpy(Math.max(0, Math.min(100, barHeight)));\n const barHeightPercent = Math.max(0, Math.min(100, Number(barHeightPercentRaw) || 0));\n\n const barTopPositionPx = (barHeightPercent / 100) * CHART_HEIGHT_PX;\n const availableSpaceBelow = Math.max(0, CHART_HEIGHT_PX - barTopPositionPx - popH);\n const popoverOffsetPx = Math.min(OFFSET_PX, availableSpaceBelow);\n\n return {\n top: `${barHeightPercent}%`,\n transform: `translateY(${popoverOffsetPx}px)`,\n position: 'absolute',\n ...(isChartLeftSide\n ? { left: `${svgStore.fpx(posX + 2)}%` }\n : { right: `${svgStore.fpx(102 - posX)}%` }),\n };\n }, [svgStore, hoveredIndex, isChartLeftSide, metrics, stackedTotals, popH]);\n\n if (hoveredIndex < 0 || hoveredIndex >= periods.length) {\n return null;\n }\n\n const period = periods[hoveredIndex]!;\n const partialWeek = !!period.partial;\n\n return (\n <div\n ref={popRef}\n className={classNames(Styles.popover, 'border border-radius-1 p-1')}\n style={popoverStyle}\n >\n <div\n className={classNames(\n Styles.arrow,\n isChartLeftSide ? Styles.arrowLeft : Styles.arrowRight\n )}\n />\n <Text size=\"small\" variant=\"headline\" el=\"h6\">\n {stackedTotals\n ? `${formattedTotalAt(hoveredIndex)} ${totalLabel} | ${formatDateTitle(period)}`\n : formatDateTitle(period)}\n </Text>\n {partialWeek && (\n <BodyText size=\"xsmall\" subdued>\n Partial week\n </BodyText>\n )}\n {metrics.map(\n m =>\n m.values[hoveredIndex] !== undefined && (\n <ColorTag\n small\n label={formatValue(m.title, m.values[hoveredIndex], m.isRight)}\n color={m.color}\n key={m.title}\n className=\"m-t-1\"\n dashed={m.opts?.dashed}\n pattern={m.opts?.pattern}\n outlineColor={m.opts?.outlineColor}\n strokeColor={m.opts?.strokeColor}\n colorTagClassName={\n m.opts?.pattern === 'outline'\n ? Styles.colorTagOutlined\n : Styles.colorTag\n }\n />\n )\n )}\n </div>\n );\n});\n"],"names":["useCallback","useMemo","useRef","useLayoutEffect","useState","classNames","observer","useDependencies","BodyText","LineChartStore","SvgStore","getFormatter","periodDateTitleFormatter","Styles","ColorTag","Text","CHART_HEIGHT_PX","OFFSET_PX","HoverPopover","periods","resolution","hoveredIndex","metrics","display","formattedTotalAt","totalLabel","stackedTotals","svgStore","isChartLeftSide","length","formatDateTitle","formatValue","title","value","isRight","metricsRightFormat","metricsLeftFormat","popRef","popH","setPopH","current","rect","getBoundingClientRect","height","Math","abs","popoverStyle","posX","periodX","yHeights","filter","m","values","undefined","map","periodY","barHeight","reduce","a","b","max","barHeightPercentRaw","fpy","min","barHeightPercent","Number","barTopPositionPx","availableSpaceBelow","popoverOffsetPx","top","transform","position","left","fpx","right","period","partialWeek","partial","div","ref","className","popover","style","arrow","arrowLeft","arrowRight","size","variant","el","subdued","small","label","color","dashed","opts","pattern","outlineColor","strokeColor","colorTagClassName","colorTagOutlined","colorTag"],"mappings":"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"}
|
|
@@ -16,7 +16,32 @@
|
|
|
16
16
|
.popover {
|
|
17
17
|
position: absolute;
|
|
18
18
|
width: 200px;
|
|
19
|
-
|
|
19
|
+
transform: rotate(45deg);
|
|
20
|
+
top: 12px;
|
|
21
|
+
z-index: 1000;
|
|
22
|
+
background-color: @color-neutral-30;
|
|
23
|
+
border: 1px solid @color-neutral-60;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.arrow {
|
|
27
|
+
position: absolute;
|
|
28
|
+
width: 8px;
|
|
29
|
+
height: 8px;
|
|
30
|
+
background-color: @color-neutral-30;
|
|
31
|
+
border-left: 1px solid @color-neutral-60;
|
|
32
|
+
border-top: 1px solid @color-neutral-60;
|
|
33
|
+
top: 50%;
|
|
34
|
+
z-index: 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.arrow-left {
|
|
38
|
+
left: -6px;
|
|
39
|
+
transform: rotate(-45deg);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.arrow-right {
|
|
43
|
+
right: -6px;
|
|
44
|
+
transform: rotate(135deg);
|
|
20
45
|
}
|
|
21
46
|
|
|
22
47
|
.color-tag {
|
|
@@ -5,7 +5,8 @@ import { BodyText, Stack } from '@servicetitan/design-system';
|
|
|
5
5
|
import * as Styles from './stuff.module.less';
|
|
6
6
|
import { ColorTag } from '../../common';
|
|
7
7
|
export const MetricsTitle = ({ metrics })=>/*#__PURE__*/ _jsx(Stack, {
|
|
8
|
-
alignItems: "
|
|
8
|
+
alignItems: "flex-start",
|
|
9
|
+
className: "m-l-half",
|
|
9
10
|
children: metrics.map((m)=>{
|
|
10
11
|
var _m_opts, _m_opts1, _m_opts2, _m_opts3;
|
|
11
12
|
return /*#__PURE__*/ _jsx(ColorTag, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/charts/line-chart/components/stuff.tsx"],"sourcesContent":["import { CSSProperties, useMemo, FC } from 'react';\nimport classNames from 'classnames';\nimport { BodyText, Stack } from '@servicetitan/design-system';\nimport * as Styles from './stuff.module.less';\nimport { LineChartMetric } from '../utils/interfaces';\nimport { ChartXLabels } from '../utils/internal-interfaces';\nimport { ColorTag } from '../../common';\n\ninterface MetricsTitleProps {\n metrics: LineChartMetric[];\n}\n\nexport const MetricsTitle: FC<MetricsTitleProps> = ({ metrics }) => (\n <Stack alignItems=\"
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components/charts/line-chart/components/stuff.tsx"],"sourcesContent":["import { CSSProperties, useMemo, FC } from 'react';\nimport classNames from 'classnames';\nimport { BodyText, Stack } from '@servicetitan/design-system';\nimport * as Styles from './stuff.module.less';\nimport { LineChartMetric } from '../utils/interfaces';\nimport { ChartXLabels } from '../utils/internal-interfaces';\nimport { ColorTag } from '../../common';\n\ninterface MetricsTitleProps {\n metrics: LineChartMetric[];\n}\n\nexport const MetricsTitle: FC<MetricsTitleProps> = ({ metrics }) => (\n <Stack alignItems=\"flex-start\" className=\"m-l-half\">\n {metrics.map(m => (\n <ColorTag\n key={m.id}\n label={m.title}\n color={m.color}\n dashed={m.opts?.dashed}\n pattern={m.opts?.pattern}\n outlineColor={m.opts?.outlineColor}\n strokeColor={m.opts?.strokeColor}\n className=\"m-r-4\"\n />\n ))}\n </Stack>\n);\n\nexport const XAxisLabels: FC<{\n labels: ChartXLabels;\n width: number;\n left: number;\n right: number;\n labelsMerged: boolean;\n hasBars: boolean;\n}> = ({ labels, width, left, right, labelsMerged, hasBars }) => {\n const data = useMemo(() => {\n const styles: CSSProperties = {};\n const labelsMapped = labels.map(([text, flex], ind) => ({\n text,\n flex,\n key: `${ind}${text}`,\n className: '',\n }));\n\n if (hasBars) {\n // when we have bars, all labels will get space equally, so no need to calculate margins\n styles.marginLeft = `${left}px`;\n styles.marginRight = `${right}px`;\n } else if (labels.length && !!width && !labelsMerged) {\n // when we have only lines, we should calculate available space for first and last label\n\n const labelWidth = (width - left - right) / labels.length;\n const labelHalfWidth = labelWidth / 2;\n\n if (left) {\n if (left > labelHalfWidth) {\n styles.marginLeft = `${left - labelHalfWidth}px`;\n } else {\n labelsMapped[0].flex = (left + labelHalfWidth) / labelWidth;\n }\n } else {\n labelsMapped[0].flex = 0.5;\n labelsMapped[0].className = 't-truncate';\n }\n\n if (right) {\n if (right > labelHalfWidth) {\n styles.marginRight = `${right - labelHalfWidth}px`;\n } else {\n labelsMapped[labels.length - 1].flex = (right + labelHalfWidth) / labelWidth;\n }\n } else {\n labelsMapped[labels.length - 1].flex = 0.5;\n labelsMapped[labels.length - 1].className = 't-truncate';\n }\n }\n\n return {\n labels: labelsMapped,\n styles,\n };\n }, [labels, left, right, labelsMerged, width, hasBars]);\n\n return (\n <div style={data.styles}>\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"center\"\n className=\"m-t-1\"\n >\n {data.labels.map(({ text, flex, key, className }) => (\n <div key={key} style={{ flex }} className={className}>\n <BodyText\n size=\"xsmall\"\n className={classNames(Styles.xAxisLabel, 'ta-center')}\n >\n {text}\n </BodyText>\n </div>\n ))}\n </Stack>\n </div>\n );\n};\n"],"names":["useMemo","classNames","BodyText","Stack","Styles","ColorTag","MetricsTitle","metrics","alignItems","className","map","m","label","title","color","dashed","opts","pattern","outlineColor","strokeColor","id","XAxisLabels","labels","width","left","right","labelsMerged","hasBars","data","styles","labelsMapped","text","flex","ind","key","marginLeft","marginRight","length","labelWidth","labelHalfWidth","div","style","direction","justifyContent","size","xAxisLabel"],"mappings":"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"}
|
|
@@ -88,7 +88,7 @@ export const SvgBars = observer(({ metrics, isStackedBarChart, isGroupedBarChart
|
|
|
88
88
|
const TOP_RADIUS = 1;
|
|
89
89
|
const xLeft = +fpx(x - barWidth / 2);
|
|
90
90
|
const yTop = +fpy(stackedBarHeight) + (values.length - 2);
|
|
91
|
-
const height = +fpx(value.val);
|
|
91
|
+
const height = j === values.length - 1 ? +fpx(value.val - 2) : +fpx(value.val);
|
|
92
92
|
const width = +fpx(barWidth);
|
|
93
93
|
const r = j === 0 ? TOP_RADIUS : 0; // radius must be numeric
|
|
94
94
|
const d = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/charts/line-chart/components/svg-bars.tsx"],"sourcesContent":["import { FC } from 'react';\nimport { observer } from 'mobx-react';\nimport { useDependencies } from '@servicetitan/react-ioc';\nimport { ChartMetric } from '../utils/internal-interfaces';\nimport { keyVal } from '../utils/key';\nimport { SvgStore } from '../stores/svg.store';\n\ninterface SvgBarsProps {\n metrics: ChartMetric[];\n isStackedBarChart?: boolean;\n isGroupedBarChart?: boolean;\n}\n\nexport const SvgBars: FC<SvgBarsProps> = observer(\n ({ metrics, isStackedBarChart, isGroupedBarChart }) => {\n const [store] = useDependencies(SvgStore);\n const { fpx, fpy, barWidth, length } = store;\n const barWidthHalf = barWidth / 2;\n const paths = [];\n\n const patternDefs = metrics\n .filter(m => m.opts?.pattern === 'striped')\n .map(m => {\n const rotation = 20;\n const tileW = 0.6;\n const tileH = 0.6;\n const stripeWidth = Math.max(0.1, Math.floor(tileW / 20));\n const tintOpacity = 0.06;\n\n return (\n <pattern\n key={`pattern-${m.id}`}\n id={`stripe-pattern-${m.id}`}\n patternUnits=\"userSpaceOnUse\"\n width={tileW}\n height={tileH}\n patternTransform={`rotate(${rotation})`}\n >\n <rect width={tileW} height={tileH} fill={m.color} opacity={tintOpacity} />\n <rect width={stripeWidth} height={tileH} fill={m.color} />\n </pattern>\n );\n });\n\n for (let i = 0; i < length; i++) {\n const x = store.periodX(i);\n const values = metrics.map(m => ({\n id: m.id,\n color: m.valuesOpts?.[i]?.color ?? m.color,\n opacity: m.opacity,\n val: store.periodY(m, i),\n pattern: m.opts?.pattern,\n strokeColor: m.opts?.strokeColor,\n outlineColor: m.opts?.outlineColor,\n value: m.values[i],\n }));\n\n if (isStackedBarChart) {\n const spacingBetweenSegments = 1; // Approximately 4px in SVG coordinates\n const totalSpacing = (values.length - 1) * spacingBetweenSegments;\n let stackedBarHeight =\n values.reduce((sum, curr) => sum + curr.val, 0) + totalSpacing;\n\n const totalValue = values.reduce((sum, curr) => sum + curr.value, 0);\n if (totalValue > 0) {\n const yTop = +fpy(stackedBarHeight);\n const scaleX = 0.3;\n const scaleY = 1;\n\n paths.push(\n <g\n key={`total-${i}`}\n transform={`translate(${x},${yTop}) scale(${scaleX},${scaleY})`}\n pointerEvents=\"none\"\n >\n <text\n x={0}\n y={0}\n textAnchor=\"middle\"\n dominantBaseline=\"alphabetic\"\n fontSize=\"2.5\"\n fontWeight={600}\n fill=\"#111827\"\n stroke=\"white\"\n strokeWidth={0.8}\n paintOrder=\"stroke\"\n fontFamily=\"Nunito Sans\"\n >\n {Math.round(totalValue)}\n </text>\n </g>\n );\n }\n\n for (let j = 0; j < values.length; j++) {\n const value = values[j];\n stackedBarHeight -= spacingBetweenSegments;\n\n const TOP_RADIUS = 1;\n const xLeft = +fpx(x - barWidth / 2);\n const yTop = +fpy(stackedBarHeight) + (values.length - 2);\n const height = +fpx(value.val);\n const width = +fpx(barWidth);\n\n const r = j === 0 ? TOP_RADIUS : 0; // radius must be numeric\n\n const d = [\n `M ${xLeft} ${yTop + height}`, // bottom-left\n `L ${xLeft} ${yTop + r}`, // up left edge\n `Q ${xLeft} ${yTop} ${xLeft + r / 2} ${yTop}`, // top-left corner\n `L ${xLeft + width - r / 2} ${yTop}`, // across top\n `Q ${xLeft + width} ${yTop} ${xLeft + width} ${yTop + r}`, // top-right corner\n `L ${xLeft + width} ${yTop + height}`, // down right edge\n 'Z',\n ].join(' ');\n paths.push(\n <path\n key={keyVal(value.id, i)}\n d={d}\n fill={\n value.pattern === 'striped'\n ? `url(#stripe-pattern-${value.id})`\n : value.color\n }\n stroke={\n value.pattern === 'outline'\n ? value.outlineColor\n : (value.strokeColor ?? value.color)\n }\n strokeWidth={1}\n vectorEffect=\"non-scaling-stroke\"\n strokeLinejoin=\"round\"\n />\n );\n\n stackedBarHeight -= value.val;\n }\n } else if (isGroupedBarChart) {\n for (let j = 0; j < values.length; j++) {\n const groupedBarX = (j * barWidth) / values.length;\n const value = values[j];\n\n paths.push(\n <rect\n key={keyVal(value.id, i)}\n x={x + groupedBarX - barWidthHalf}\n y={fpy(value.val)}\n width={barWidth / values.length - 0.1}\n height={fpx(value.val)}\n fill={value.color}\n opacity={value.opacity}\n />\n );\n }\n } else {\n values.sort((a, b) => b.val - a.val);\n for (const value of values) {\n paths.push(\n <rect\n key={keyVal(value.id, i)}\n x={fpx(x - barWidthHalf)}\n y={fpy(value.val)}\n width={fpx(barWidth)}\n height={fpx(value.val)}\n fill={value.color}\n />\n );\n }\n }\n }\n\n return (\n <g>\n {patternDefs.length > 0 && <defs>{patternDefs}</defs>}\n {paths}\n </g>\n );\n }\n);\n\ninterface SvgBarsHoverProps {\n onHover(ind: number): void;\n onLeave(ind: number): void;\n}\n\nexport const SvgBarsHover: FC<SvgBarsHoverProps> = observer(({ onHover, onLeave }) => {\n const [store] = useDependencies(SvgStore);\n const { fpx, fpy, barWidth, length } = store;\n const barWidthHalf = barWidth / 2;\n const paths = [];\n\n for (let i = 0; i < length; i++) {\n const x = store.periodX(i);\n\n paths.push(\n <rect\n key={keyVal('_', i)}\n onMouseEnter={() => onHover(i)}\n onMouseLeave={() => onLeave(i)}\n x={fpx(x - barWidthHalf)}\n y={fpy(100)}\n width={fpx(barWidth)}\n height=\"100%\"\n fill=\"white\"\n fillOpacity=\"0\"\n />\n );\n }\n\n return <g>{paths}</g>;\n});\n"],"names":["observer","useDependencies","keyVal","SvgStore","SvgBars","metrics","isStackedBarChart","isGroupedBarChart","store","fpx","fpy","barWidth","length","barWidthHalf","paths","patternDefs","filter","m","opts","pattern","map","rotation","tileW","tileH","stripeWidth","Math","max","floor","tintOpacity","id","patternUnits","width","height","patternTransform","rect","fill","color","opacity","i","x","periodX","values","valuesOpts","val","periodY","strokeColor","outlineColor","value","spacingBetweenSegments","totalSpacing","stackedBarHeight","reduce","sum","curr","totalValue","yTop","scaleX","scaleY","push","g","transform","pointerEvents","text","y","textAnchor","dominantBaseline","fontSize","fontWeight","stroke","strokeWidth","paintOrder","fontFamily","round","j","TOP_RADIUS","xLeft","r","d","join","path","vectorEffect","strokeLinejoin","groupedBarX","sort","a","b","defs","SvgBarsHover","onHover","onLeave","onMouseEnter","onMouseLeave","fillOpacity"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components/charts/line-chart/components/svg-bars.tsx"],"sourcesContent":["import { FC } from 'react';\nimport { observer } from 'mobx-react';\nimport { useDependencies } from '@servicetitan/react-ioc';\nimport { ChartMetric } from '../utils/internal-interfaces';\nimport { keyVal } from '../utils/key';\nimport { SvgStore } from '../stores/svg.store';\n\ninterface SvgBarsProps {\n metrics: ChartMetric[];\n isStackedBarChart?: boolean;\n isGroupedBarChart?: boolean;\n}\n\nexport const SvgBars: FC<SvgBarsProps> = observer(\n ({ metrics, isStackedBarChart, isGroupedBarChart }) => {\n const [store] = useDependencies(SvgStore);\n const { fpx, fpy, barWidth, length } = store;\n const barWidthHalf = barWidth / 2;\n const paths = [];\n\n const patternDefs = metrics\n .filter(m => m.opts?.pattern === 'striped')\n .map(m => {\n const rotation = 20;\n const tileW = 0.6;\n const tileH = 0.6;\n const stripeWidth = Math.max(0.1, Math.floor(tileW / 20));\n const tintOpacity = 0.06;\n\n return (\n <pattern\n key={`pattern-${m.id}`}\n id={`stripe-pattern-${m.id}`}\n patternUnits=\"userSpaceOnUse\"\n width={tileW}\n height={tileH}\n patternTransform={`rotate(${rotation})`}\n >\n <rect width={tileW} height={tileH} fill={m.color} opacity={tintOpacity} />\n <rect width={stripeWidth} height={tileH} fill={m.color} />\n </pattern>\n );\n });\n\n for (let i = 0; i < length; i++) {\n const x = store.periodX(i);\n const values = metrics.map(m => ({\n id: m.id,\n color: m.valuesOpts?.[i]?.color ?? m.color,\n opacity: m.opacity,\n val: store.periodY(m, i),\n pattern: m.opts?.pattern,\n strokeColor: m.opts?.strokeColor,\n outlineColor: m.opts?.outlineColor,\n value: m.values[i],\n }));\n\n if (isStackedBarChart) {\n const spacingBetweenSegments = 1; // Approximately 4px in SVG coordinates\n const totalSpacing = (values.length - 1) * spacingBetweenSegments;\n let stackedBarHeight =\n values.reduce((sum, curr) => sum + curr.val, 0) + totalSpacing;\n\n const totalValue = values.reduce((sum, curr) => sum + curr.value, 0);\n if (totalValue > 0) {\n const yTop = +fpy(stackedBarHeight);\n const scaleX = 0.3;\n const scaleY = 1;\n\n paths.push(\n <g\n key={`total-${i}`}\n transform={`translate(${x},${yTop}) scale(${scaleX},${scaleY})`}\n pointerEvents=\"none\"\n >\n <text\n x={0}\n y={0}\n textAnchor=\"middle\"\n dominantBaseline=\"alphabetic\"\n fontSize=\"2.5\"\n fontWeight={600}\n fill=\"#111827\"\n stroke=\"white\"\n strokeWidth={0.8}\n paintOrder=\"stroke\"\n fontFamily=\"Nunito Sans\"\n >\n {Math.round(totalValue)}\n </text>\n </g>\n );\n }\n\n for (let j = 0; j < values.length; j++) {\n const value = values[j];\n stackedBarHeight -= spacingBetweenSegments;\n\n const TOP_RADIUS = 1;\n const xLeft = +fpx(x - barWidth / 2);\n const yTop = +fpy(stackedBarHeight) + (values.length - 2);\n const height = j === values.length - 1 ? +fpx(value.val - 2) : +fpx(value.val);\n const width = +fpx(barWidth);\n\n const r = j === 0 ? TOP_RADIUS : 0; // radius must be numeric\n\n const d = [\n `M ${xLeft} ${yTop + height}`, // bottom-left\n `L ${xLeft} ${yTop + r}`, // up left edge\n `Q ${xLeft} ${yTop} ${xLeft + r / 2} ${yTop}`, // top-left corner\n `L ${xLeft + width - r / 2} ${yTop}`, // across top\n `Q ${xLeft + width} ${yTop} ${xLeft + width} ${yTop + r}`, // top-right corner\n `L ${xLeft + width} ${yTop + height}`, // down right edge\n 'Z',\n ].join(' ');\n paths.push(\n <path\n key={keyVal(value.id, i)}\n d={d}\n fill={\n value.pattern === 'striped'\n ? `url(#stripe-pattern-${value.id})`\n : value.color\n }\n stroke={\n value.pattern === 'outline'\n ? value.outlineColor\n : (value.strokeColor ?? value.color)\n }\n strokeWidth={1}\n vectorEffect=\"non-scaling-stroke\"\n strokeLinejoin=\"round\"\n />\n );\n\n stackedBarHeight -= value.val;\n }\n } else if (isGroupedBarChart) {\n for (let j = 0; j < values.length; j++) {\n const groupedBarX = (j * barWidth) / values.length;\n const value = values[j];\n\n paths.push(\n <rect\n key={keyVal(value.id, i)}\n x={x + groupedBarX - barWidthHalf}\n y={fpy(value.val)}\n width={barWidth / values.length - 0.1}\n height={fpx(value.val)}\n fill={value.color}\n opacity={value.opacity}\n />\n );\n }\n } else {\n values.sort((a, b) => b.val - a.val);\n for (const value of values) {\n paths.push(\n <rect\n key={keyVal(value.id, i)}\n x={fpx(x - barWidthHalf)}\n y={fpy(value.val)}\n width={fpx(barWidth)}\n height={fpx(value.val)}\n fill={value.color}\n />\n );\n }\n }\n }\n\n return (\n <g>\n {patternDefs.length > 0 && <defs>{patternDefs}</defs>}\n {paths}\n </g>\n );\n }\n);\n\ninterface SvgBarsHoverProps {\n onHover(ind: number): void;\n onLeave(ind: number): void;\n}\n\nexport const SvgBarsHover: FC<SvgBarsHoverProps> = observer(({ onHover, onLeave }) => {\n const [store] = useDependencies(SvgStore);\n const { fpx, fpy, barWidth, length } = store;\n const barWidthHalf = barWidth / 2;\n const paths = [];\n\n for (let i = 0; i < length; i++) {\n const x = store.periodX(i);\n\n paths.push(\n <rect\n key={keyVal('_', i)}\n onMouseEnter={() => onHover(i)}\n onMouseLeave={() => onLeave(i)}\n x={fpx(x - barWidthHalf)}\n y={fpy(100)}\n width={fpx(barWidth)}\n height=\"100%\"\n fill=\"white\"\n fillOpacity=\"0\"\n />\n );\n }\n\n return <g>{paths}</g>;\n});\n"],"names":["observer","useDependencies","keyVal","SvgStore","SvgBars","metrics","isStackedBarChart","isGroupedBarChart","store","fpx","fpy","barWidth","length","barWidthHalf","paths","patternDefs","filter","m","opts","pattern","map","rotation","tileW","tileH","stripeWidth","Math","max","floor","tintOpacity","id","patternUnits","width","height","patternTransform","rect","fill","color","opacity","i","x","periodX","values","valuesOpts","val","periodY","strokeColor","outlineColor","value","spacingBetweenSegments","totalSpacing","stackedBarHeight","reduce","sum","curr","totalValue","yTop","scaleX","scaleY","push","g","transform","pointerEvents","text","y","textAnchor","dominantBaseline","fontSize","fontWeight","stroke","strokeWidth","paintOrder","fontFamily","round","j","TOP_RADIUS","xLeft","r","d","join","path","vectorEffect","strokeLinejoin","groupedBarX","sort","a","b","defs","SvgBarsHover","onHover","onLeave","onMouseEnter","onMouseLeave","fillOpacity"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pie.d.ts","sourceRoot":"","sources":["../../../../../src/components/charts/pie-chart/components/pie.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkC,EAAE,EAAY,MAAM,OAAO,CAAC;AAKrE,OAAO,EAAE,0BAA0B,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"pie.d.ts","sourceRoot":"","sources":["../../../../../src/components/charts/pie-chart/components/pie.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkC,EAAE,EAAY,MAAM,OAAO,CAAC;AAKrE,OAAO,EAAE,0BAA0B,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAqK5F,eAAO,MAAM,GAAG,EAAE,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,EAAE,CAAC;IACb,cAAc,CAAC,EAAE,0BAA0B,CAAC;IAC5C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACvC,CA4HA,CAAC"}
|
|
@@ -4,6 +4,7 @@ import { tokens } from '@servicetitan/tokens/core';
|
|
|
4
4
|
import { BodyText, Popover, Stack } from '@servicetitan/design-system';
|
|
5
5
|
import { useClientRect } from '../../../../utils/use-client-rect';
|
|
6
6
|
import { ColorTag } from '../../common';
|
|
7
|
+
import * as Styles from './pie-chart.module.less';
|
|
7
8
|
const chartPadding = 8;
|
|
8
9
|
const px = (value)=>`${value !== null && value !== void 0 ? value : 0}px`;
|
|
9
10
|
const GAP_PX = 4;
|
|
@@ -219,6 +220,7 @@ export const Pie = ({ pieces, popoverContent: PopoverContent, content: PieConten
|
|
|
219
220
|
direction: popoverDirection,
|
|
220
221
|
padding: "s",
|
|
221
222
|
width: "auto",
|
|
223
|
+
className: Styles.popover,
|
|
222
224
|
children: selectedIndex === ind && /*#__PURE__*/ _jsxs(Stack, {
|
|
223
225
|
direction: "column",
|
|
224
226
|
"data-cy": `customer-lead-rate-section-${ts.key}-popover`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/charts/pie-chart/components/pie.tsx"],"sourcesContent":["import { useCallback, useMemo, useState, FC, Fragment } from 'react';\nimport { tokens } from '@servicetitan/tokens/core';\nimport { BodyText, Popover, Stack } from '@servicetitan/design-system';\n\nimport { useClientRect } from '../../../../utils/use-client-rect';\nimport { PieChartPopoverContentType, PiePiece, PopoverDirection } from '../utils/interface';\nimport { ColorTag } from '../../common';\n\nconst chartPadding = 8;\nconst px = (value?: number) => `${value ?? 0}px`;\nconst GAP_PX = 4;\n\nconst PiePieceSvg: FC<{\n piece: PiePiece;\n selected?: boolean;\n}> = ({ piece: { id, color, points, text, path }, selected }) =>\n points && path ? (\n <g id={id}>\n {selected && (\n <path\n d={path}\n fill={tokens.colorWhite}\n stroke={tokens.colorBlue200}\n strokeOpacity=\"50%\"\n strokeWidth=\"3\"\n paintOrder=\"stroke\"\n />\n )}\n <path d={path} fill={color} />\n {points[4] && (\n <g transform={`translate(${points[4][0]}, ${points[4][1]})`} pointerEvents=\"none\">\n {(() => {\n const fontSize = 3;\n const height = 6;\n const radius = height / 2;\n\n const width = Math.max(8, text.length);\n\n return (\n <Fragment>\n <rect\n x={-width / 2}\n y={-height / 2}\n width={width}\n height={height}\n rx={radius}\n ry={radius}\n fill=\"rgba(255,255,255,0.80)\"\n strokeWidth={0.6}\n />\n <text\n x=\"0\"\n y=\"0\"\n fontSize={fontSize}\n fontWeight={600}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n fill={tokens.colorBlack}\n >\n {text}\n </text>\n </Fragment>\n );\n })()}\n </g>\n )}\n </g>\n ) : null;\n\nconst PiePieceHover: FC<{\n piece: PiePiece;\n onMouse(id: string, isEnter: boolean): void;\n}> = ({ piece, onMouse }) => {\n const onMouseEnter = useCallback(() => onMouse(piece.id, true), [onMouse, piece.id]);\n const onMouseLeave = useCallback(() => onMouse(piece.id, false), [onMouse, piece.id]);\n\n return (\n <path\n d={piece.path}\n fill=\"white\"\n opacity=\"0\"\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n />\n );\n};\n\nconst PieSvg: FC<{\n pieces: PiePiece[];\n selectedIndex: number;\n radiusRelative: number;\n}> = ({ pieces, selectedIndex, radiusRelative }) => {\n const piePiece = pieces.find(p => p.points);\n const innerR = piePiece ? Math.hypot(piePiece.points![3][0], piePiece.points![3][1]) : 0;\n const outerR = piePiece ? Math.hypot(piePiece.points![1][0], piePiece.points![1][1]) : 0;\n\n const nonZeroPieces = pieces.filter(p => p.points && p.path);\n const boundaries = nonZeroPieces.map(p => {\n const [outerX, outerY] = p.points![1];\n const len = Math.hypot(outerX, outerY) || 1;\n return { x: outerX / len, y: outerY / len };\n });\n\n return (\n <svg\n className=\"position-absolute\"\n style={{ inset: `${chartPadding}px` }}\n viewBox={`-${radiusRelative} -${radiusRelative} ${radiusRelative * 2} ${radiusRelative * 2}`}\n >\n {pieces.map((p, i) =>\n p.path ? <PiePieceSvg piece={p} key={p.id} selected={i === selectedIndex} /> : null\n )}\n {nonZeroPieces.length > 1 && innerR > 0 && outerR > 0 && (\n <Fragment>\n <defs>\n <mask id=\"ring-mask\">\n <rect\n x={-outerR}\n y={-outerR}\n width={outerR * 2}\n height={outerR * 2}\n fill=\"black\"\n />\n <circle cx=\"0\" cy=\"0\" r={outerR} fill=\"white\" />\n <circle cx=\"0\" cy=\"0\" r={innerR} fill=\"black\" />\n </mask>\n </defs>\n <g mask=\"url(#ring-mask)\" pointerEvents=\"none\">\n {boundaries.map(boundary => (\n <line\n key={`sep-${boundary.x}-${boundary.y}`}\n x1={boundary.x * innerR}\n y1={boundary.y * innerR}\n x2={boundary.x * outerR}\n y2={boundary.y * outerR}\n stroke=\"#fff\"\n strokeWidth={GAP_PX}\n vectorEffect=\"non-scaling-stroke\"\n strokeLinecap=\"round\"\n />\n ))}\n </g>\n </Fragment>\n )}\n {selectedIndex >= 0 && selectedIndex < pieces.length && (\n <use xlinkHref={`#${pieces[selectedIndex].id}`} />\n )}\n </svg>\n );\n};\n\nconst PieSvgHover: FC<{\n pieces: PiePiece[];\n radiusRelative: number;\n onMouse(id: string, isEnter: boolean): void;\n}> = ({ pieces, onMouse, radiusRelative }) => (\n <svg\n className=\"position-absolute z-global-nav\"\n style={{ inset: px(chartPadding) }}\n viewBox={\n `-${radiusRelative} -${radiusRelative} ` + `${radiusRelative * 2} ${radiusRelative * 2}`\n }\n >\n {pieces.map(p =>\n p.path ? <PiePieceHover piece={p} key={p.id} onMouse={onMouse} /> : null\n )}\n </svg>\n);\n\nexport const Pie: FC<{\n title: string;\n pieces: PiePiece[];\n radiusRelative: number;\n content?: FC;\n popoverContent?: PieChartPopoverContentType;\n hideTitles?: boolean;\n popoverDirection?: PopoverDirection;\n}> = ({\n pieces,\n popoverContent: PopoverContent,\n content: PieContent,\n radiusRelative,\n hideTitles,\n popoverDirection,\n}) => {\n const [selectedIndex, setSelectedIndex] = useState(-1);\n const [rect, ref] = useClientRect();\n\n const onMouse = useCallback(\n (id: string, isEnter: boolean) => {\n setSelectedIndex(isEnter ? pieces.findIndex(p => p.id === id) : -1);\n },\n [pieces, setSelectedIndex]\n );\n\n const container = useMemo(() => {\n const height = rect?.height ?? 0;\n\n return {\n height,\n internal: height ? height - chartPadding * 2 : 0,\n styles: height\n ? {\n width: px(Math.max(250, height)),\n }\n : {},\n };\n }, [rect]);\n\n const triggersStyles = useMemo(\n () =>\n container.height\n ? pieces.map(p =>\n p.points\n ? {\n key: p.key,\n top: px(\n (container.height * (radiusRelative + p.points[4][1])) /\n (radiusRelative * 2)\n ),\n left: px(\n (container.height * (radiusRelative + p.points[4][0])) /\n (radiusRelative * 2) +\n 20\n ),\n }\n : { top: '', left: '' }\n )\n : [],\n [pieces, container, radiusRelative]\n );\n\n return (\n <div ref={ref} style={container.styles} className=\"position-relative h-100\">\n {pieces.every(p => !p.path) ? (\n <Stack className=\"h-100\" justifyContent=\"center\" alignItems=\"center\">\n No Data\n </Stack>\n ) : (\n <Fragment>\n {triggersStyles\n .filter(ts => !!ts.left && !!ts.top)\n .map((ts, ind) => (\n <div key={ts.left + ts.top} style={ts} className=\"position-absolute\">\n {(!!PopoverContent || !hideTitles) && (\n <Popover\n portal\n trigger={<span> </span>}\n open={selectedIndex === ind}\n direction={popoverDirection}\n padding=\"s\"\n width=\"auto\"\n >\n {selectedIndex === ind && (\n <Stack\n direction=\"column\"\n data-cy={`customer-lead-rate-section-${ts.key}-popover`}\n >\n {!hideTitles && (\n <Stack alignItems=\"center\">\n <ColorTag\n label=\"\"\n color={pieces[ind].color}\n />\n <BodyText size=\"small\" bold>\n {pieces[ind].title}\n </BodyText>\n </Stack>\n )}\n {!!PopoverContent && (\n <Stack.Item className=\"m-l-1\">\n <PopoverContent\n index={ind}\n data={pieces[ind]?.data}\n text={pieces[ind]?.text}\n value={pieces[ind]?.value}\n />\n </Stack.Item>\n )}\n </Stack>\n )}\n </Popover>\n )}\n </div>\n ))}\n {!!PieContent && <PieContent />}\n <PieSvg\n pieces={pieces}\n selectedIndex={selectedIndex}\n radiusRelative={radiusRelative}\n />\n <PieSvgHover\n pieces={pieces}\n onMouse={onMouse}\n radiusRelative={radiusRelative}\n />\n </Fragment>\n )}\n </div>\n );\n};\n"],"names":["useCallback","useMemo","useState","Fragment","tokens","BodyText","Popover","Stack","useClientRect","ColorTag","chartPadding","px","value","GAP_PX","PiePieceSvg","piece","id","color","points","text","path","selected","g","d","fill","colorWhite","stroke","colorBlue200","strokeOpacity","strokeWidth","paintOrder","transform","pointerEvents","fontSize","height","radius","width","Math","max","length","rect","x","y","rx","ry","fontWeight","textAnchor","dominantBaseline","colorBlack","PiePieceHover","onMouse","onMouseEnter","onMouseLeave","opacity","PieSvg","pieces","selectedIndex","radiusRelative","piePiece","find","p","innerR","hypot","outerR","nonZeroPieces","filter","boundaries","map","outerX","outerY","len","svg","className","style","inset","viewBox","i","defs","mask","circle","cx","cy","r","boundary","line","x1","y1","x2","y2","vectorEffect","strokeLinecap","use","xlinkHref","PieSvgHover","Pie","popoverContent","PopoverContent","content","PieContent","hideTitles","popoverDirection","setSelectedIndex","ref","isEnter","findIndex","container","internal","styles","triggersStyles","key","top","left","div","every","justifyContent","alignItems","ts","ind","portal","trigger","span","open","direction","padding","data-cy","label","size","bold","title","Item","index","data"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components/charts/pie-chart/components/pie.tsx"],"sourcesContent":["import { useCallback, useMemo, useState, FC, Fragment } from 'react';\nimport { tokens } from '@servicetitan/tokens/core';\nimport { BodyText, Popover, Stack } from '@servicetitan/design-system';\n\nimport { useClientRect } from '../../../../utils/use-client-rect';\nimport { PieChartPopoverContentType, PiePiece, PopoverDirection } from '../utils/interface';\nimport { ColorTag } from '../../common';\nimport * as Styles from './pie-chart.module.less';\n\nconst chartPadding = 8;\nconst px = (value?: number) => `${value ?? 0}px`;\nconst GAP_PX = 4;\n\nconst PiePieceSvg: FC<{\n piece: PiePiece;\n selected?: boolean;\n}> = ({ piece: { id, color, points, text, path }, selected }) =>\n points && path ? (\n <g id={id}>\n {selected && (\n <path\n d={path}\n fill={tokens.colorWhite}\n stroke={tokens.colorBlue200}\n strokeOpacity=\"50%\"\n strokeWidth=\"3\"\n paintOrder=\"stroke\"\n />\n )}\n <path d={path} fill={color} />\n {points[4] && (\n <g transform={`translate(${points[4][0]}, ${points[4][1]})`} pointerEvents=\"none\">\n {(() => {\n const fontSize = 3;\n const height = 6;\n const radius = height / 2;\n\n const width = Math.max(8, text.length);\n\n return (\n <Fragment>\n <rect\n x={-width / 2}\n y={-height / 2}\n width={width}\n height={height}\n rx={radius}\n ry={radius}\n fill=\"rgba(255,255,255,0.80)\"\n strokeWidth={0.6}\n />\n <text\n x=\"0\"\n y=\"0\"\n fontSize={fontSize}\n fontWeight={600}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n fill={tokens.colorBlack}\n >\n {text}\n </text>\n </Fragment>\n );\n })()}\n </g>\n )}\n </g>\n ) : null;\n\nconst PiePieceHover: FC<{\n piece: PiePiece;\n onMouse(id: string, isEnter: boolean): void;\n}> = ({ piece, onMouse }) => {\n const onMouseEnter = useCallback(() => onMouse(piece.id, true), [onMouse, piece.id]);\n const onMouseLeave = useCallback(() => onMouse(piece.id, false), [onMouse, piece.id]);\n\n return (\n <path\n d={piece.path}\n fill=\"white\"\n opacity=\"0\"\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n />\n );\n};\n\nconst PieSvg: FC<{\n pieces: PiePiece[];\n selectedIndex: number;\n radiusRelative: number;\n}> = ({ pieces, selectedIndex, radiusRelative }) => {\n const piePiece = pieces.find(p => p.points);\n const innerR = piePiece ? Math.hypot(piePiece.points![3][0], piePiece.points![3][1]) : 0;\n const outerR = piePiece ? Math.hypot(piePiece.points![1][0], piePiece.points![1][1]) : 0;\n\n const nonZeroPieces = pieces.filter(p => p.points && p.path);\n const boundaries = nonZeroPieces.map(p => {\n const [outerX, outerY] = p.points![1];\n const len = Math.hypot(outerX, outerY) || 1;\n return { x: outerX / len, y: outerY / len };\n });\n\n return (\n <svg\n className=\"position-absolute\"\n style={{ inset: `${chartPadding}px` }}\n viewBox={`-${radiusRelative} -${radiusRelative} ${radiusRelative * 2} ${radiusRelative * 2}`}\n >\n {pieces.map((p, i) =>\n p.path ? <PiePieceSvg piece={p} key={p.id} selected={i === selectedIndex} /> : null\n )}\n {nonZeroPieces.length > 1 && innerR > 0 && outerR > 0 && (\n <Fragment>\n <defs>\n <mask id=\"ring-mask\">\n <rect\n x={-outerR}\n y={-outerR}\n width={outerR * 2}\n height={outerR * 2}\n fill=\"black\"\n />\n <circle cx=\"0\" cy=\"0\" r={outerR} fill=\"white\" />\n <circle cx=\"0\" cy=\"0\" r={innerR} fill=\"black\" />\n </mask>\n </defs>\n <g mask=\"url(#ring-mask)\" pointerEvents=\"none\">\n {boundaries.map(boundary => (\n <line\n key={`sep-${boundary.x}-${boundary.y}`}\n x1={boundary.x * innerR}\n y1={boundary.y * innerR}\n x2={boundary.x * outerR}\n y2={boundary.y * outerR}\n stroke=\"#fff\"\n strokeWidth={GAP_PX}\n vectorEffect=\"non-scaling-stroke\"\n strokeLinecap=\"round\"\n />\n ))}\n </g>\n </Fragment>\n )}\n {selectedIndex >= 0 && selectedIndex < pieces.length && (\n <use xlinkHref={`#${pieces[selectedIndex].id}`} />\n )}\n </svg>\n );\n};\n\nconst PieSvgHover: FC<{\n pieces: PiePiece[];\n radiusRelative: number;\n onMouse(id: string, isEnter: boolean): void;\n}> = ({ pieces, onMouse, radiusRelative }) => (\n <svg\n className=\"position-absolute z-global-nav\"\n style={{ inset: px(chartPadding) }}\n viewBox={\n `-${radiusRelative} -${radiusRelative} ` + `${radiusRelative * 2} ${radiusRelative * 2}`\n }\n >\n {pieces.map(p =>\n p.path ? <PiePieceHover piece={p} key={p.id} onMouse={onMouse} /> : null\n )}\n </svg>\n);\n\nexport const Pie: FC<{\n title: string;\n pieces: PiePiece[];\n radiusRelative: number;\n content?: FC;\n popoverContent?: PieChartPopoverContentType;\n hideTitles?: boolean;\n popoverDirection?: PopoverDirection;\n}> = ({\n pieces,\n popoverContent: PopoverContent,\n content: PieContent,\n radiusRelative,\n hideTitles,\n popoverDirection,\n}) => {\n const [selectedIndex, setSelectedIndex] = useState(-1);\n const [rect, ref] = useClientRect();\n\n const onMouse = useCallback(\n (id: string, isEnter: boolean) => {\n setSelectedIndex(isEnter ? pieces.findIndex(p => p.id === id) : -1);\n },\n [pieces, setSelectedIndex]\n );\n\n const container = useMemo(() => {\n const height = rect?.height ?? 0;\n\n return {\n height,\n internal: height ? height - chartPadding * 2 : 0,\n styles: height\n ? {\n width: px(Math.max(250, height)),\n }\n : {},\n };\n }, [rect]);\n\n const triggersStyles = useMemo(\n () =>\n container.height\n ? pieces.map(p =>\n p.points\n ? {\n key: p.key,\n top: px(\n (container.height * (radiusRelative + p.points[4][1])) /\n (radiusRelative * 2)\n ),\n left: px(\n (container.height * (radiusRelative + p.points[4][0])) /\n (radiusRelative * 2) +\n 20\n ),\n }\n : { top: '', left: '' }\n )\n : [],\n [pieces, container, radiusRelative]\n );\n\n return (\n <div ref={ref} style={container.styles} className=\"position-relative h-100\">\n {pieces.every(p => !p.path) ? (\n <Stack className=\"h-100\" justifyContent=\"center\" alignItems=\"center\">\n No Data\n </Stack>\n ) : (\n <Fragment>\n {triggersStyles\n .filter(ts => !!ts.left && !!ts.top)\n .map((ts, ind) => (\n <div key={ts.left + ts.top} style={ts} className=\"position-absolute\">\n {(!!PopoverContent || !hideTitles) && (\n <Popover\n portal\n trigger={<span> </span>}\n open={selectedIndex === ind}\n direction={popoverDirection}\n padding=\"s\"\n width=\"auto\"\n className={Styles.popover}\n >\n {selectedIndex === ind && (\n <Stack\n direction=\"column\"\n data-cy={`customer-lead-rate-section-${ts.key}-popover`}\n >\n {!hideTitles && (\n <Stack alignItems=\"center\">\n <ColorTag\n label=\"\"\n color={pieces[ind].color}\n />\n <BodyText size=\"small\" bold>\n {pieces[ind].title}\n </BodyText>\n </Stack>\n )}\n {!!PopoverContent && (\n <Stack.Item className=\"m-l-1\">\n <PopoverContent\n index={ind}\n data={pieces[ind]?.data}\n text={pieces[ind]?.text}\n value={pieces[ind]?.value}\n />\n </Stack.Item>\n )}\n </Stack>\n )}\n </Popover>\n )}\n </div>\n ))}\n {!!PieContent && <PieContent />}\n <PieSvg\n pieces={pieces}\n selectedIndex={selectedIndex}\n radiusRelative={radiusRelative}\n />\n <PieSvgHover\n pieces={pieces}\n onMouse={onMouse}\n radiusRelative={radiusRelative}\n />\n </Fragment>\n )}\n </div>\n );\n};\n"],"names":["useCallback","useMemo","useState","Fragment","tokens","BodyText","Popover","Stack","useClientRect","ColorTag","Styles","chartPadding","px","value","GAP_PX","PiePieceSvg","piece","id","color","points","text","path","selected","g","d","fill","colorWhite","stroke","colorBlue200","strokeOpacity","strokeWidth","paintOrder","transform","pointerEvents","fontSize","height","radius","width","Math","max","length","rect","x","y","rx","ry","fontWeight","textAnchor","dominantBaseline","colorBlack","PiePieceHover","onMouse","onMouseEnter","onMouseLeave","opacity","PieSvg","pieces","selectedIndex","radiusRelative","piePiece","find","p","innerR","hypot","outerR","nonZeroPieces","filter","boundaries","map","outerX","outerY","len","svg","className","style","inset","viewBox","i","defs","mask","circle","cx","cy","r","boundary","line","x1","y1","x2","y2","vectorEffect","strokeLinecap","use","xlinkHref","PieSvgHover","Pie","popoverContent","PopoverContent","content","PieContent","hideTitles","popoverDirection","setSelectedIndex","ref","isEnter","findIndex","container","internal","styles","triggersStyles","key","top","left","div","every","justifyContent","alignItems","ts","ind","portal","trigger","span","open","direction","padding","popover","data-cy","label","size","bold","title","Item","index","data"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/marketing-ui",
|
|
3
|
-
"version": "6.0
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "Marketing UI component and utils",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"less": true,
|
|
54
54
|
"webpack": false
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "b2f96c9cebfe80c20a790d4a9490d2642224bc64"
|
|
57
57
|
}
|
|
@@ -35,8 +35,8 @@ export const FunnelPyramidSvg: FC<FunnelPyramidSvgProps> = ({
|
|
|
35
35
|
yt: yStep * ind,
|
|
36
36
|
yb: ind === len - 1 ? 100 : yStep * (ind + 1),
|
|
37
37
|
xtl: xOffset + lStep * ind,
|
|
38
|
-
xtr:
|
|
39
|
-
xbr:
|
|
38
|
+
xtr: 99 - lStep * ind,
|
|
39
|
+
xbr: 99 - lStep * (ind + 1),
|
|
40
40
|
xbl: xOffset + lStep * (ind + 1),
|
|
41
41
|
c: color,
|
|
42
42
|
};
|
|
@@ -102,7 +102,7 @@ export const FunnelPyramidSvg: FC<FunnelPyramidSvgProps> = ({
|
|
|
102
102
|
section.xtr,
|
|
103
103
|
section.xbr,
|
|
104
104
|
section.xbl,
|
|
105
|
-
section.yt,
|
|
105
|
+
section.yt + 1,
|
|
106
106
|
section.yb,
|
|
107
107
|
isTop,
|
|
108
108
|
isBottom,
|
|
@@ -102,7 +102,7 @@ export const Body: FC<{ classNameTitle?: string }> = provide({ singletons: [SvgS
|
|
|
102
102
|
/>
|
|
103
103
|
)}
|
|
104
104
|
{display.metricsTitlePosition === 'bottom' && (
|
|
105
|
-
<Stack direction="row" justifyContent="
|
|
105
|
+
<Stack direction="row" justifyContent="flex-start" className="p-t-3 p-x-5">
|
|
106
106
|
<MetricsTitle metrics={metrics} />
|
|
107
107
|
</Stack>
|
|
108
108
|
)}
|
|
@@ -16,7 +16,32 @@
|
|
|
16
16
|
.popover {
|
|
17
17
|
position: absolute;
|
|
18
18
|
width: 200px;
|
|
19
|
-
|
|
19
|
+
transform: rotate(45deg);
|
|
20
|
+
top: 12px;
|
|
21
|
+
z-index: 1000;
|
|
22
|
+
background-color: @color-neutral-30;
|
|
23
|
+
border: 1px solid @color-neutral-60;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.arrow {
|
|
27
|
+
position: absolute;
|
|
28
|
+
width: 8px;
|
|
29
|
+
height: 8px;
|
|
30
|
+
background-color: @color-neutral-30;
|
|
31
|
+
border-left: 1px solid @color-neutral-60;
|
|
32
|
+
border-top: 1px solid @color-neutral-60;
|
|
33
|
+
top: 50%;
|
|
34
|
+
z-index: 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.arrow-left {
|
|
38
|
+
left: -6px;
|
|
39
|
+
transform: rotate(-45deg);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.arrow-right {
|
|
43
|
+
right: -6px;
|
|
44
|
+
transform: rotate(135deg);
|
|
20
45
|
}
|
|
21
46
|
|
|
22
47
|
.color-tag {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useMemo, FC,
|
|
1
|
+
import { useCallback, useMemo, useRef, useLayoutEffect, useState, FC, CSSProperties } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { observer } from 'mobx-react';
|
|
4
4
|
import { useDependencies } from '@servicetitan/react-ioc';
|
|
@@ -11,6 +11,9 @@ import * as Styles from './hover-popover.module.less';
|
|
|
11
11
|
import { ColorTag } from '../../common';
|
|
12
12
|
import { Text } from '@servicetitan/anvil2';
|
|
13
13
|
|
|
14
|
+
const CHART_HEIGHT_PX = 400;
|
|
15
|
+
const OFFSET_PX = 16;
|
|
16
|
+
|
|
14
17
|
export const HoverPopover: FC = observer(() => {
|
|
15
18
|
const [
|
|
16
19
|
{
|
|
@@ -25,6 +28,7 @@ export const HoverPopover: FC = observer(() => {
|
|
|
25
28
|
},
|
|
26
29
|
svgStore,
|
|
27
30
|
] = useDependencies(LineChartStore, SvgStore);
|
|
31
|
+
const isChartLeftSide = hoveredIndex < periods.length / 2;
|
|
28
32
|
|
|
29
33
|
const formatDateTitle = useMemo(() => periodDateTitleFormatter[resolution], [resolution]);
|
|
30
34
|
const formatValue = useCallback(
|
|
@@ -34,15 +38,48 @@ export const HoverPopover: FC = observer(() => {
|
|
|
34
38
|
title,
|
|
35
39
|
[display]
|
|
36
40
|
);
|
|
37
|
-
const popoverStyle = useMemo(() => {
|
|
38
|
-
const pos = svgStore.periodX(hoveredIndex);
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
const popRef = useRef<HTMLDivElement | null>(null);
|
|
43
|
+
const [popH, setPopH] = useState(0);
|
|
44
|
+
|
|
45
|
+
useLayoutEffect(() => {
|
|
46
|
+
if (!popRef.current) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const rect = popRef.current.getBoundingClientRect();
|
|
50
|
+
if (rect.height && Math.abs(rect.height - popH) > 0.5) {
|
|
51
|
+
setPopH(rect.height);
|
|
42
52
|
}
|
|
53
|
+
}, [hoveredIndex, metrics, display, popH]);
|
|
54
|
+
|
|
55
|
+
const popoverStyle = useMemo<CSSProperties>(() => {
|
|
56
|
+
const posX = svgStore.periodX(hoveredIndex);
|
|
57
|
+
|
|
58
|
+
const yHeights = metrics
|
|
59
|
+
.filter(m => m.values[hoveredIndex] !== undefined)
|
|
60
|
+
.map(m => svgStore.periodY(m, hoveredIndex));
|
|
61
|
+
|
|
62
|
+
const barHeight = yHeights.length
|
|
63
|
+
? stackedTotals
|
|
64
|
+
? yHeights.reduce((a, b) => a + b, 0)
|
|
65
|
+
: Math.max(...yHeights)
|
|
66
|
+
: 0;
|
|
67
|
+
const barHeightPercentRaw = svgStore.fpy(Math.max(0, Math.min(100, barHeight)));
|
|
68
|
+
const barHeightPercent = Math.max(0, Math.min(100, Number(barHeightPercentRaw) || 0));
|
|
43
69
|
|
|
44
|
-
|
|
45
|
-
|
|
70
|
+
const barTopPositionPx = (barHeightPercent / 100) * CHART_HEIGHT_PX;
|
|
71
|
+
const availableSpaceBelow = Math.max(0, CHART_HEIGHT_PX - barTopPositionPx - popH);
|
|
72
|
+
const popoverOffsetPx = Math.min(OFFSET_PX, availableSpaceBelow);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
top: `${barHeightPercent}%`,
|
|
76
|
+
transform: `translateY(${popoverOffsetPx}px)`,
|
|
77
|
+
position: 'absolute',
|
|
78
|
+
...(isChartLeftSide
|
|
79
|
+
? { left: `${svgStore.fpx(posX + 2)}%` }
|
|
80
|
+
: { right: `${svgStore.fpx(102 - posX)}%` }),
|
|
81
|
+
};
|
|
82
|
+
}, [svgStore, hoveredIndex, isChartLeftSide, metrics, stackedTotals, popH]);
|
|
46
83
|
|
|
47
84
|
if (hoveredIndex < 0 || hoveredIndex >= periods.length) {
|
|
48
85
|
return null;
|
|
@@ -52,47 +89,48 @@ export const HoverPopover: FC = observer(() => {
|
|
|
52
89
|
const partialWeek = !!period.partial;
|
|
53
90
|
|
|
54
91
|
return (
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
92
|
+
<div
|
|
93
|
+
ref={popRef}
|
|
94
|
+
className={classNames(Styles.popover, 'border border-radius-1 p-1')}
|
|
95
|
+
style={popoverStyle}
|
|
96
|
+
>
|
|
60
97
|
<div
|
|
61
|
-
className={classNames(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
<Text size="small" variant="headline" el="h6">
|
|
65
|
-
{stackedTotals
|
|
66
|
-
? `${formattedTotalAt(hoveredIndex)} ${totalLabel} | ${formatDateTitle(period)}`
|
|
67
|
-
: formatDateTitle(period)}
|
|
68
|
-
</Text>
|
|
69
|
-
{partialWeek && (
|
|
70
|
-
<BodyText size="xsmall" subdued>
|
|
71
|
-
Partial week
|
|
72
|
-
</BodyText>
|
|
98
|
+
className={classNames(
|
|
99
|
+
Styles.arrow,
|
|
100
|
+
isChartLeftSide ? Styles.arrowLeft : Styles.arrowRight
|
|
73
101
|
)}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
102
|
+
/>
|
|
103
|
+
<Text size="small" variant="headline" el="h6">
|
|
104
|
+
{stackedTotals
|
|
105
|
+
? `${formattedTotalAt(hoveredIndex)} ${totalLabel} | ${formatDateTitle(period)}`
|
|
106
|
+
: formatDateTitle(period)}
|
|
107
|
+
</Text>
|
|
108
|
+
{partialWeek && (
|
|
109
|
+
<BodyText size="xsmall" subdued>
|
|
110
|
+
Partial week
|
|
111
|
+
</BodyText>
|
|
112
|
+
)}
|
|
113
|
+
{metrics.map(
|
|
114
|
+
m =>
|
|
115
|
+
m.values[hoveredIndex] !== undefined && (
|
|
116
|
+
<ColorTag
|
|
117
|
+
small
|
|
118
|
+
label={formatValue(m.title, m.values[hoveredIndex], m.isRight)}
|
|
119
|
+
color={m.color}
|
|
120
|
+
key={m.title}
|
|
121
|
+
className="m-t-1"
|
|
122
|
+
dashed={m.opts?.dashed}
|
|
123
|
+
pattern={m.opts?.pattern}
|
|
124
|
+
outlineColor={m.opts?.outlineColor}
|
|
125
|
+
strokeColor={m.opts?.strokeColor}
|
|
126
|
+
colorTagClassName={
|
|
127
|
+
m.opts?.pattern === 'outline'
|
|
128
|
+
? Styles.colorTagOutlined
|
|
129
|
+
: Styles.colorTag
|
|
130
|
+
}
|
|
131
|
+
/>
|
|
132
|
+
)
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
97
135
|
);
|
|
98
136
|
});
|
|
@@ -99,7 +99,7 @@ export const SvgBars: FC<SvgBarsProps> = observer(
|
|
|
99
99
|
const TOP_RADIUS = 1;
|
|
100
100
|
const xLeft = +fpx(x - barWidth / 2);
|
|
101
101
|
const yTop = +fpy(stackedBarHeight) + (values.length - 2);
|
|
102
|
-
const height = +fpx(value.val);
|
|
102
|
+
const height = j === values.length - 1 ? +fpx(value.val - 2) : +fpx(value.val);
|
|
103
103
|
const width = +fpx(barWidth);
|
|
104
104
|
|
|
105
105
|
const r = j === 0 ? TOP_RADIUS : 0; // radius must be numeric
|
|
@@ -5,6 +5,7 @@ import { BodyText, Popover, Stack } from '@servicetitan/design-system';
|
|
|
5
5
|
import { useClientRect } from '../../../../utils/use-client-rect';
|
|
6
6
|
import { PieChartPopoverContentType, PiePiece, PopoverDirection } from '../utils/interface';
|
|
7
7
|
import { ColorTag } from '../../common';
|
|
8
|
+
import * as Styles from './pie-chart.module.less';
|
|
8
9
|
|
|
9
10
|
const chartPadding = 8;
|
|
10
11
|
const px = (value?: number) => `${value ?? 0}px`;
|
|
@@ -250,6 +251,7 @@ export const Pie: FC<{
|
|
|
250
251
|
direction={popoverDirection}
|
|
251
252
|
padding="s"
|
|
252
253
|
width="auto"
|
|
254
|
+
className={Styles.popover}
|
|
253
255
|
>
|
|
254
256
|
{selectedIndex === ind && (
|
|
255
257
|
<Stack
|