@servicetitan/marketing-ui 7.1.0 → 7.1.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.
- package/dist/components/charts/funnel-chart/components/funnel-chart.module.less +1 -1
- package/dist/components/charts/line-chart/components/svg-bars.d.ts.map +1 -1
- package/dist/components/charts/line-chart/components/svg-bars.js +22 -5
- package/dist/components/charts/line-chart/components/svg-bars.js.map +1 -1
- package/package.json +2 -2
- package/src/components/charts/funnel-chart/components/funnel-chart.module.less +1 -1
- package/src/components/charts/line-chart/components/svg-bars.tsx +39 -5
- package/src/components/charts/line-chart/line-chart.stories.tsx +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"svg-bars.d.ts","sourceRoot":"","sources":["../../../../../src/components/charts/line-chart/components/svg-bars.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAG3B,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAI3D,UAAU,YAAY;IAClB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,OAAO,EAAE,EAAE,CAAC,YAAY,
|
|
1
|
+
{"version":3,"file":"svg-bars.d.ts","sourceRoot":"","sources":["../../../../../src/components/charts/line-chart/components/svg-bars.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAG3B,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAI3D,UAAU,YAAY;IAClB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,OAAO,EAAE,EAAE,CAAC,YAAY,CAuMpC,CAAC;AAEF,UAAU,iBAAiB;IACvB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAyB7C,CAAC"}
|
|
@@ -55,12 +55,23 @@ export const SvgBars = observer(({ metrics, isStackedBarChart, isGroupedBarChart
|
|
|
55
55
|
};
|
|
56
56
|
});
|
|
57
57
|
if (isStackedBarChart) {
|
|
58
|
-
|
|
58
|
+
// Use ORIGINAL calculations - keep all spacing/positioning unchanged
|
|
59
|
+
const spacingBetweenSegments = 1;
|
|
59
60
|
const totalSpacing = (values.length - 1) * spacingBetweenSegments;
|
|
60
61
|
let stackedBarHeight = values.reduce((sum, curr)=>sum + curr.val, 0) + totalSpacing;
|
|
62
|
+
// Find first/last non-zero indices for visual styling
|
|
63
|
+
const firstNonZeroIdx = values.findIndex((v)=>v.value > 0);
|
|
64
|
+
const lastNonZeroIdx = values.reduce((last, v, idx)=>v.value > 0 ? idx : last, -1);
|
|
65
|
+
// Count 0-value segments below first non-zero (for text position adjustment)
|
|
66
|
+
const zeroSegmentsBelowFirst = firstNonZeroIdx >= 0 ? values.slice(firstNonZeroIdx + 1).filter((v)=>v.value <= 0).length : 0;
|
|
61
67
|
const totalValue = values.reduce((sum, curr)=>sum + curr.value, 0);
|
|
62
68
|
if (totalValue > 0) {
|
|
63
|
-
|
|
69
|
+
/*
|
|
70
|
+
* Adjust text position to maintain consistent gap with first rendered segment:
|
|
71
|
+
* 1. Subtract spacing for skipped segments from fpy argument
|
|
72
|
+
* 2. Add pixel offset for zeros below first non-zero
|
|
73
|
+
*/ const textStackedBarHeight = stackedBarHeight - (firstNonZeroIdx > 0 ? firstNonZeroIdx : 0) * spacingBetweenSegments;
|
|
74
|
+
const yTop = +fpy(textStackedBarHeight) + zeroSegmentsBelowFirst * spacingBetweenSegments;
|
|
64
75
|
const scaleX = 0.3;
|
|
65
76
|
const scaleY = 1;
|
|
66
77
|
paths.push(/*#__PURE__*/ _jsx("g", {
|
|
@@ -87,10 +98,16 @@ export const SvgBars = observer(({ metrics, isStackedBarChart, isGroupedBarChart
|
|
|
87
98
|
stackedBarHeight -= spacingBetweenSegments;
|
|
88
99
|
const TOP_RADIUS = 1;
|
|
89
100
|
const xLeft = +fpx(x - barWidth / 2);
|
|
90
|
-
const yTop = +fpy(stackedBarHeight) + (values.length - 2);
|
|
91
|
-
const height = j === values.length - 1 ? +fpx(value.val - 2) : +fpx(value.val);
|
|
92
101
|
const width = +fpx(barWidth);
|
|
93
|
-
|
|
102
|
+
if (value.value <= 0) {
|
|
103
|
+
stackedBarHeight -= value.val;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const zeroSegments = values.slice(j + 1).filter((v)=>v.value <= 0).length;
|
|
107
|
+
// Adjust yTop: move down by the space 0-value segments would occupy
|
|
108
|
+
const yTop = +fpy(stackedBarHeight) + (values.length - 2) + zeroSegments * spacingBetweenSegments;
|
|
109
|
+
const height = j === lastNonZeroIdx ? +fpx(value.val - 2) : +fpx(value.val);
|
|
110
|
+
const r = j === firstNonZeroIdx ? TOP_RADIUS : 0;
|
|
94
111
|
const d = [
|
|
95
112
|
`M ${xLeft} ${yTop + height}`,
|
|
96
113
|
`L ${xLeft} ${yTop + r}`,
|
|
@@ -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 = 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
|
+
{"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 // Use ORIGINAL calculations - keep all spacing/positioning unchanged\n const spacingBetweenSegments = 1;\n const totalSpacing = (values.length - 1) * spacingBetweenSegments;\n let stackedBarHeight =\n values.reduce((sum, curr) => sum + curr.val, 0) + totalSpacing;\n\n // Find first/last non-zero indices for visual styling\n const firstNonZeroIdx = values.findIndex(v => v.value > 0);\n const lastNonZeroIdx = values.reduce(\n (last, v, idx) => (v.value > 0 ? idx : last),\n -1\n );\n\n // Count 0-value segments below first non-zero (for text position adjustment)\n const zeroSegmentsBelowFirst =\n firstNonZeroIdx >= 0\n ? values.slice(firstNonZeroIdx + 1).filter(v => v.value <= 0).length\n : 0;\n\n const totalValue = values.reduce((sum, curr) => sum + curr.value, 0);\n if (totalValue > 0) {\n /*\n * Adjust text position to maintain consistent gap with first rendered segment:\n * 1. Subtract spacing for skipped segments from fpy argument\n * 2. Add pixel offset for zeros below first non-zero\n */\n const textStackedBarHeight =\n stackedBarHeight -\n (firstNonZeroIdx > 0 ? firstNonZeroIdx : 0) * spacingBetweenSegments;\n const yTop =\n +fpy(textStackedBarHeight) +\n zeroSegmentsBelowFirst * spacingBetweenSegments;\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 width = +fpx(barWidth);\n\n if (value.value <= 0) {\n stackedBarHeight -= value.val;\n continue;\n }\n const zeroSegments = values.slice(j + 1).filter(v => v.value <= 0).length;\n\n // Adjust yTop: move down by the space 0-value segments would occupy\n const yTop =\n +fpy(stackedBarHeight) +\n (values.length - 2) +\n zeroSegments * spacingBetweenSegments;\n const height = j === lastNonZeroIdx ? +fpx(value.val - 2) : +fpx(value.val);\n const r = j === firstNonZeroIdx ? TOP_RADIUS : 0;\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","firstNonZeroIdx","findIndex","v","lastNonZeroIdx","last","idx","zeroSegmentsBelowFirst","slice","totalValue","textStackedBarHeight","yTop","scaleX","scaleY","push","g","transform","pointerEvents","text","y","textAnchor","dominantBaseline","fontSize","fontWeight","stroke","strokeWidth","paintOrder","fontFamily","round","j","TOP_RADIUS","xLeft","zeroSegments","r","d","join","path","vectorEffect","strokeLinejoin","groupedBarX","sort","a","b","defs","SvgBarsHover","onHover","onLeave","onMouseEnter","onMouseLeave","fillOpacity"],"mappings":";AACA,SAASA,QAAQ,QAAQ,aAAa;AACtC,SAASC,eAAe,QAAQ,0BAA0B;AAE1D,SAASC,MAAM,QAAQ,eAAe;AACtC,SAASC,QAAQ,QAAQ,sBAAsB;AAQ/C,OAAO,MAAMC,UAA4BJ,SACrC,CAAC,EAAEK,OAAO,EAAEC,iBAAiB,EAAEC,iBAAiB,EAAE;IAC9C,MAAM,CAACC,MAAM,GAAGP,gBAAgBE;IAChC,MAAM,EAAEM,GAAG,EAAEC,GAAG,EAAEC,QAAQ,EAAEC,MAAM,EAAE,GAAGJ;IACvC,MAAMK,eAAeF,WAAW;IAChC,MAAMG,QAAQ,EAAE;IAEhB,MAAMC,cAAcV,QACfW,MAAM,CAACC,CAAAA;YAAKA;eAAAA,EAAAA,UAAAA,EAAEC,IAAI,cAAND,8BAAAA,QAAQE,OAAO,MAAK;OAChCC,GAAG,CAACH,CAAAA;QACD,MAAMI,WAAW;QACjB,MAAMC,QAAQ;QACd,MAAMC,QAAQ;QACd,MAAMC,cAAcC,KAAKC,GAAG,CAAC,KAAKD,KAAKE,KAAK,CAACL,QAAQ;QACrD,MAAMM,cAAc;QAEpB,qBACI,MAACT;YAEGU,IAAI,CAAC,eAAe,EAAEZ,EAAEY,EAAE,EAAE;YAC5BC,cAAa;YACbC,OAAOT;YACPU,QAAQT;YACRU,kBAAkB,CAAC,OAAO,EAAEZ,SAAS,CAAC,CAAC;;8BAEvC,KAACa;oBAAKH,OAAOT;oBAAOU,QAAQT;oBAAOY,MAAMlB,EAAEmB,KAAK;oBAAEC,SAAST;;8BAC3D,KAACM;oBAAKH,OAAOP;oBAAaQ,QAAQT;oBAAOY,MAAMlB,EAAEmB,KAAK;;;WARjD,CAAC,QAAQ,EAAEnB,EAAEY,EAAE,EAAE;IAWlC;IAEJ,IAAK,IAAIS,IAAI,GAAGA,IAAI1B,QAAQ0B,IAAK;QAC7B,MAAMC,IAAI/B,MAAMgC,OAAO,CAACF;QACxB,MAAMG,SAASpC,QAAQe,GAAG,CAACH,CAAAA;gBAEhBA,iBAAAA,eAGEA,SACIA,UACCA;gBALPA;mBAFsB;gBAC7BY,IAAIZ,EAAEY,EAAE;gBACRO,OAAOnB,CAAAA,yBAAAA,gBAAAA,EAAEyB,UAAU,cAAZzB,qCAAAA,kBAAAA,aAAc,CAACqB,EAAE,cAAjBrB,sCAAAA,gBAAmBmB,KAAK,cAAxBnB,mCAAAA,wBAA4BA,EAAEmB,KAAK;gBAC1CC,SAASpB,EAAEoB,OAAO;gBAClBM,KAAKnC,MAAMoC,OAAO,CAAC3B,GAAGqB;gBACtBnB,OAAO,GAAEF,UAAAA,EAAEC,IAAI,cAAND,8BAAAA,QAAQE,OAAO;gBACxB0B,WAAW,GAAE5B,WAAAA,EAAEC,IAAI,cAAND,+BAAAA,SAAQ4B,WAAW;gBAChCC,YAAY,GAAE7B,WAAAA,EAAEC,IAAI,cAAND,+BAAAA,SAAQ6B,YAAY;gBAClCC,OAAO9B,EAAEwB,MAAM,CAACH,EAAE;YACtB;;QAEA,IAAIhC,mBAAmB;YACnB,qEAAqE;YACrE,MAAM0C,yBAAyB;YAC/B,MAAMC,eAAe,AAACR,CAAAA,OAAO7B,MAAM,GAAG,CAAA,IAAKoC;YAC3C,IAAIE,mBACAT,OAAOU,MAAM,CAAC,CAACC,KAAKC,OAASD,MAAMC,KAAKV,GAAG,EAAE,KAAKM;YAEtD,sDAAsD;YACtD,MAAMK,kBAAkBb,OAAOc,SAAS,CAACC,CAAAA,IAAKA,EAAET,KAAK,GAAG;YACxD,MAAMU,iBAAiBhB,OAAOU,MAAM,CAChC,CAACO,MAAMF,GAAGG,MAASH,EAAET,KAAK,GAAG,IAAIY,MAAMD,MACvC,CAAC;YAGL,6EAA6E;YAC7E,MAAME,yBACFN,mBAAmB,IACbb,OAAOoB,KAAK,CAACP,kBAAkB,GAAGtC,MAAM,CAACwC,CAAAA,IAAKA,EAAET,KAAK,IAAI,GAAGnC,MAAM,GAClE;YAEV,MAAMkD,aAAarB,OAAOU,MAAM,CAAC,CAACC,KAAKC,OAASD,MAAMC,KAAKN,KAAK,EAAE;YAClE,IAAIe,aAAa,GAAG;gBAChB;;;;qBAIC,GACD,MAAMC,uBACFb,mBACA,AAACI,CAAAA,kBAAkB,IAAIA,kBAAkB,CAAA,IAAKN;gBAClD,MAAMgB,OACF,CAACtD,IAAIqD,wBACLH,yBAAyBZ;gBAC7B,MAAMiB,SAAS;gBACf,MAAMC,SAAS;gBAEfpD,MAAMqD,IAAI,eACN,KAACC;oBAEGC,WAAW,CAAC,UAAU,EAAE9B,EAAE,CAAC,EAAEyB,KAAK,QAAQ,EAAEC,OAAO,CAAC,EAAEC,OAAO,CAAC,CAAC;oBAC/DI,eAAc;8BAEd,cAAA,KAACC;wBACGhC,GAAG;wBACHiC,GAAG;wBACHC,YAAW;wBACXC,kBAAiB;wBACjBC,UAAS;wBACTC,YAAY;wBACZzC,MAAK;wBACL0C,QAAO;wBACPC,aAAa;wBACbC,YAAW;wBACXC,YAAW;kCAEVvD,KAAKwD,KAAK,CAACnB;;mBAjBX,CAAC,MAAM,EAAExB,GAAG;YAqB7B;YAEA,IAAK,IAAI4C,IAAI,GAAGA,IAAIzC,OAAO7B,MAAM,EAAEsE,IAAK;gBACpC,MAAMnC,QAAQN,MAAM,CAACyC,EAAE;gBACvBhC,oBAAoBF;gBAEpB,MAAMmC,aAAa;gBACnB,MAAMC,QAAQ,CAAC3E,IAAI8B,IAAI5B,WAAW;gBAClC,MAAMoB,QAAQ,CAACtB,IAAIE;gBAEnB,IAAIoC,MAAMA,KAAK,IAAI,GAAG;oBAClBG,oBAAoBH,MAAMJ,GAAG;oBAC7B;gBACJ;gBACA,MAAM0C,eAAe5C,OAAOoB,KAAK,CAACqB,IAAI,GAAGlE,MAAM,CAACwC,CAAAA,IAAKA,EAAET,KAAK,IAAI,GAAGnC,MAAM;gBAEzE,oEAAoE;gBACpE,MAAMoD,OACF,CAACtD,IAAIwC,oBACJT,CAAAA,OAAO7B,MAAM,GAAG,CAAA,IACjByE,eAAerC;gBACnB,MAAMhB,SAASkD,MAAMzB,iBAAiB,CAAChD,IAAIsC,MAAMJ,GAAG,GAAG,KAAK,CAAClC,IAAIsC,MAAMJ,GAAG;gBAC1E,MAAM2C,IAAIJ,MAAM5B,kBAAkB6B,aAAa;gBAE/C,MAAMI,IAAI;oBACN,CAAC,EAAE,EAAEH,MAAM,CAAC,EAAEpB,OAAOhC,QAAQ;oBAC7B,CAAC,EAAE,EAAEoD,MAAM,CAAC,EAAEpB,OAAOsB,GAAG;oBACxB,CAAC,EAAE,EAAEF,MAAM,CAAC,EAAEpB,KAAK,CAAC,EAAEoB,QAAQE,IAAI,EAAE,CAAC,EAAEtB,MAAM;oBAC7C,CAAC,EAAE,EAAEoB,QAAQrD,QAAQuD,IAAI,EAAE,CAAC,EAAEtB,MAAM;oBACpC,CAAC,EAAE,EAAEoB,QAAQrD,MAAM,CAAC,EAAEiC,KAAK,CAAC,EAAEoB,QAAQrD,MAAM,CAAC,EAAEiC,OAAOsB,GAAG;oBACzD,CAAC,EAAE,EAAEF,QAAQrD,MAAM,CAAC,EAAEiC,OAAOhC,QAAQ;oBACrC;iBACH,CAACwD,IAAI,CAAC;oBAaYzC;gBAZnBjC,MAAMqD,IAAI,eACN,KAACsB;oBAEGF,GAAGA;oBACHpD,MACIY,MAAM5B,OAAO,KAAK,YACZ,CAAC,oBAAoB,EAAE4B,MAAMlB,EAAE,CAAC,CAAC,CAAC,GAClCkB,MAAMX,KAAK;oBAErByC,QACI9B,MAAM5B,OAAO,KAAK,YACZ4B,MAAMD,YAAY,GACjBC,CAAAA,qBAAAA,MAAMF,WAAW,cAAjBE,gCAAAA,qBAAqBA,MAAMX,KAAK;oBAE3C0C,aAAa;oBACbY,cAAa;oBACbC,gBAAe;mBAdVzF,OAAO6C,MAAMlB,EAAE,EAAES;gBAkB9BY,oBAAoBH,MAAMJ,GAAG;YACjC;QACJ,OAAO,IAAIpC,mBAAmB;YAC1B,IAAK,IAAI2E,IAAI,GAAGA,IAAIzC,OAAO7B,MAAM,EAAEsE,IAAK;gBACpC,MAAMU,cAAc,AAACV,IAAIvE,WAAY8B,OAAO7B,MAAM;gBAClD,MAAMmC,QAAQN,MAAM,CAACyC,EAAE;gBAEvBpE,MAAMqD,IAAI,eACN,KAACjC;oBAEGK,GAAGA,IAAIqD,cAAc/E;oBACrB2D,GAAG9D,IAAIqC,MAAMJ,GAAG;oBAChBZ,OAAOpB,WAAW8B,OAAO7B,MAAM,GAAG;oBAClCoB,QAAQvB,IAAIsC,MAAMJ,GAAG;oBACrBR,MAAMY,MAAMX,KAAK;oBACjBC,SAASU,MAAMV,OAAO;mBANjBnC,OAAO6C,MAAMlB,EAAE,EAAES;YASlC;QACJ,OAAO;YACHG,OAAOoD,IAAI,CAAC,CAACC,GAAGC,IAAMA,EAAEpD,GAAG,GAAGmD,EAAEnD,GAAG;YACnC,KAAK,MAAMI,SAASN,OAAQ;gBACxB3B,MAAMqD,IAAI,eACN,KAACjC;oBAEGK,GAAG9B,IAAI8B,IAAI1B;oBACX2D,GAAG9D,IAAIqC,MAAMJ,GAAG;oBAChBZ,OAAOtB,IAAIE;oBACXqB,QAAQvB,IAAIsC,MAAMJ,GAAG;oBACrBR,MAAMY,MAAMX,KAAK;mBALZlC,OAAO6C,MAAMlB,EAAE,EAAES;YAQlC;QACJ;IACJ;IAEA,qBACI,MAAC8B;;YACIrD,YAAYH,MAAM,GAAG,mBAAK,KAACoF;0BAAMjF;;YACjCD;;;AAGb,GACF;AAOF,OAAO,MAAMmF,eAAsCjG,SAAS,CAAC,EAAEkG,OAAO,EAAEC,OAAO,EAAE;IAC7E,MAAM,CAAC3F,MAAM,GAAGP,gBAAgBE;IAChC,MAAM,EAAEM,GAAG,EAAEC,GAAG,EAAEC,QAAQ,EAAEC,MAAM,EAAE,GAAGJ;IACvC,MAAMK,eAAeF,WAAW;IAChC,MAAMG,QAAQ,EAAE;IAEhB,IAAK,IAAIwB,IAAI,GAAGA,IAAI1B,QAAQ0B,IAAK;QAC7B,MAAMC,IAAI/B,MAAMgC,OAAO,CAACF;QAExBxB,MAAMqD,IAAI,eACN,KAACjC;YAEGkE,cAAc,IAAMF,QAAQ5D;YAC5B+D,cAAc,IAAMF,QAAQ7D;YAC5BC,GAAG9B,IAAI8B,IAAI1B;YACX2D,GAAG9D,IAAI;YACPqB,OAAOtB,IAAIE;YACXqB,QAAO;YACPG,MAAK;YACLmE,aAAY;WARPpG,OAAO,KAAKoC;IAW7B;IAEA,qBAAO,KAAC8B;kBAAGtD;;AACf,GAAG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/marketing-ui",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.1",
|
|
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": "62db4c6849a56047c7b182c2eae0490d61e1ab92"
|
|
57
57
|
}
|
|
@@ -56,14 +56,38 @@ export const SvgBars: FC<SvgBarsProps> = observer(
|
|
|
56
56
|
}));
|
|
57
57
|
|
|
58
58
|
if (isStackedBarChart) {
|
|
59
|
-
|
|
59
|
+
// Use ORIGINAL calculations - keep all spacing/positioning unchanged
|
|
60
|
+
const spacingBetweenSegments = 1;
|
|
60
61
|
const totalSpacing = (values.length - 1) * spacingBetweenSegments;
|
|
61
62
|
let stackedBarHeight =
|
|
62
63
|
values.reduce((sum, curr) => sum + curr.val, 0) + totalSpacing;
|
|
63
64
|
|
|
65
|
+
// Find first/last non-zero indices for visual styling
|
|
66
|
+
const firstNonZeroIdx = values.findIndex(v => v.value > 0);
|
|
67
|
+
const lastNonZeroIdx = values.reduce(
|
|
68
|
+
(last, v, idx) => (v.value > 0 ? idx : last),
|
|
69
|
+
-1
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Count 0-value segments below first non-zero (for text position adjustment)
|
|
73
|
+
const zeroSegmentsBelowFirst =
|
|
74
|
+
firstNonZeroIdx >= 0
|
|
75
|
+
? values.slice(firstNonZeroIdx + 1).filter(v => v.value <= 0).length
|
|
76
|
+
: 0;
|
|
77
|
+
|
|
64
78
|
const totalValue = values.reduce((sum, curr) => sum + curr.value, 0);
|
|
65
79
|
if (totalValue > 0) {
|
|
66
|
-
|
|
80
|
+
/*
|
|
81
|
+
* Adjust text position to maintain consistent gap with first rendered segment:
|
|
82
|
+
* 1. Subtract spacing for skipped segments from fpy argument
|
|
83
|
+
* 2. Add pixel offset for zeros below first non-zero
|
|
84
|
+
*/
|
|
85
|
+
const textStackedBarHeight =
|
|
86
|
+
stackedBarHeight -
|
|
87
|
+
(firstNonZeroIdx > 0 ? firstNonZeroIdx : 0) * spacingBetweenSegments;
|
|
88
|
+
const yTop =
|
|
89
|
+
+fpy(textStackedBarHeight) +
|
|
90
|
+
zeroSegmentsBelowFirst * spacingBetweenSegments;
|
|
67
91
|
const scaleX = 0.3;
|
|
68
92
|
const scaleY = 1;
|
|
69
93
|
|
|
@@ -98,11 +122,21 @@ export const SvgBars: FC<SvgBarsProps> = observer(
|
|
|
98
122
|
|
|
99
123
|
const TOP_RADIUS = 1;
|
|
100
124
|
const xLeft = +fpx(x - barWidth / 2);
|
|
101
|
-
const yTop = +fpy(stackedBarHeight) + (values.length - 2);
|
|
102
|
-
const height = j === values.length - 1 ? +fpx(value.val - 2) : +fpx(value.val);
|
|
103
125
|
const width = +fpx(barWidth);
|
|
104
126
|
|
|
105
|
-
|
|
127
|
+
if (value.value <= 0) {
|
|
128
|
+
stackedBarHeight -= value.val;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const zeroSegments = values.slice(j + 1).filter(v => v.value <= 0).length;
|
|
132
|
+
|
|
133
|
+
// Adjust yTop: move down by the space 0-value segments would occupy
|
|
134
|
+
const yTop =
|
|
135
|
+
+fpy(stackedBarHeight) +
|
|
136
|
+
(values.length - 2) +
|
|
137
|
+
zeroSegments * spacingBetweenSegments;
|
|
138
|
+
const height = j === lastNonZeroIdx ? +fpx(value.val - 2) : +fpx(value.val);
|
|
139
|
+
const r = j === firstNonZeroIdx ? TOP_RADIUS : 0;
|
|
106
140
|
|
|
107
141
|
const d = [
|
|
108
142
|
`M ${xLeft} ${yTop + height}`, // bottom-left
|
|
@@ -51,15 +51,15 @@ const stackedBarChartValues = (() => {
|
|
|
51
51
|
values: [
|
|
52
52
|
{
|
|
53
53
|
metricId: 2,
|
|
54
|
-
values: periods.map((_, index) => 15 + (index % 5) * 15),
|
|
54
|
+
values: periods.map((_, index) => (index % 3 === 0 ? 0 : 15 + (index % 5) * 15)),
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
metricId: 3,
|
|
58
|
-
values: periods.map((_, index) => 10 + (index % 5) * 10),
|
|
58
|
+
values: periods.map((_, index) => (index % 4 === 0 ? 0 : 10 + (index % 5) * 10)),
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
metricId: 4,
|
|
62
|
-
values: periods.map((_, index) => 5 + (index % 5) * 5),
|
|
62
|
+
values: periods.map((_, index) => (index % 5 === 0 ? 0 : 5 + (index % 5) * 5)),
|
|
63
63
|
},
|
|
64
64
|
],
|
|
65
65
|
};
|