diff --git a/Signum.React.Extensions/Chart/Chart.css b/Signum.React.Extensions/Chart/Chart.css index ea0eedf386..beb011f02a 100644 --- a/Signum.React.Extensions/Chart/Chart.css +++ b/Signum.React.Extensions/Chart/Chart.css @@ -137,7 +137,11 @@ body.rtl .sf-chart-token .sf-query-token { } .sf-chart-animable .sf-transition { - transition: all 0.3s cubic-bezier(0.92, 0.02, 0.21, 1.01) + transition: all 0.3s cubic-bezier(0.92, 0.02, 0.21, 1.01), filter 0.1s linear +} + +.shadow-group:hover .shadow { + filter: drop-shadow( 1px 1px 3px rgba(0, 0, 0, .4)); } text.sf-initial-message { diff --git a/Signum.React.Extensions/Chart/D3Scripts/Bars.tsx b/Signum.React.Extensions/Chart/D3Scripts/Bars.tsx index 096358a476..0ce316dfb5 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/Bars.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/Bars.tsx @@ -4,11 +4,12 @@ import * as ChartClient from '../ChartClient'; import * as ChartUtils from './Components/ChartUtils'; import { translate, scale, rotate, skewX, skewY, matrix, scaleFor } from './Components/ChartUtils'; import { ChartRow, ChartScriptProps } from '../ChartClient'; -import TextEllipsis from './Components/TextEllipsis'; import { XKeyTicks, YScaleTicks, YKeyTicks, XScaleTicks } from './Components/Ticks'; import { XAxis, YAxis } from './Components/Axis'; import { Rule } from './Components/Rule'; import InitialMessage from './Components/InitialMessage'; +import TextIfFits from './Components/TextIfFits'; +import TextEllipsis from './Components/TextEllipsis'; export default function renderBars({ data, width, height, parameters, loading, onDrillDown, initialLoad, chartRequest, memo, dashboardFilter }: ChartScriptProps): React.ReactElement { @@ -70,6 +71,8 @@ export default function renderBars({ data, width, height, parameters, loading, o var detector = dashboardFilter?.getActiveDetector(chartRequest); + const bandMargin = y.bandwidth() > 20 ? 2 : 0; + return ( @@ -79,98 +82,85 @@ export default function renderBars({ data, width, height, parameters, loading, o {/*PAINT GRAPH*/} - {orderedRows.map(r => { + {keyValues.map(k => { - var active = detector?.(r); + var key = keyColumn.getKey(k); - var key = keyColumn.getValueKey(r); + var row: ChartRow | undefined = rowsByKey[key]; + var active = detector?.(row); - return ( - 4 ? '#fff' : undefined} - strokeWidth={active == true ? 3 : undefined} - transform={translate(0, y(key)!) + (initialLoad ? scale(0, 1) : scale(1, 1))} - width={x(valueColumn.getValue(r))} - height={y.bandwidth()} - fill={keyColumn.getValueColor(r) ?? color(key)} - onClick={e => onDrillDown(r, e)} - cursor="pointer"> - - {keyColumn.getValueNiceName(r) + ': ' + valueColumn.getValueNiceName(r)} - - - ); - })} - + var posx = x(row ? valueColumn.getValue(row) : 0)!; - {y.bandwidth() > 15 && - (isMargin ? - - {(isAll ? keyValues : orderedRows.map(r => keyColumn.getValue(r))).map(k => onDrillDown({ c0: k }, e)} - cursor="pointer"> - {keyColumn.getNiceName(k)} - )} - : - isInside ? - - {(isAll ? keyValues : orderedRows.map(r => keyColumn.getValue(r))).map(k => { - - var row = rowsByKey[keyColumn.getKey(k)]; - - var posx = x(row ? valueColumn.getValue(row) : 0)!; - return ( - = size / 2 ? 0 : posx, y(keyColumn.getKey(k))!)} - maxWidth={posx >= size / 2 ? posx : size - posx} + return ( + + {row && onDrillDown(row!, e)} + cursor="pointer"> + + {keyColumn.getValueNiceName(row) + ': ' + valueColumn.getValueNiceName(row)} + + + } + {y.bandwidth() > 15 && (isAll || row != null) && + (isMargin ? + + onDrillDown({ c0: key }, e)} + cursor="pointer"> + {keyColumn.getNiceName(key)} + ) + : + isInside ? + + = size / 2 ? 0 : posx, y(keyColumn.getKey(key))!)} + maxWidth={posx >= size / 2 ? posx : size - posx} + padding={labelMargin} + className="y-label sf-transition" + fill={posx >= size / 2 ? '#fff' : (keyColumn.getColor(key) ?? color(keyColumn.getKey(key)))} + dominantBaseline="middle" + fontWeight="bold" + onClick={e => onDrillDown({ c0: key }, e)} + cursor="pointer"> + {keyColumn.getNiceName(key)} + + : null + )} + {y.bandwidth() > 15 && parseFloat(parameters["NumberOpacity"]) > 0 && row && + + = size / 2 ? '#fff' : (keyColumn.getColor(k) ?? color(keyColumn.getKey(k)))} + className="number-label sf-transition" + fill={parameters["NumberColor"] ?? "#000"} dominantBaseline="middle" + opacity={parameters["NumberOpacity"]} + textAnchor="middle" fontWeight="bold" - onClick={e => onDrillDown({ c0: k }, e)} + onClick={e => onDrillDown(row!, e)} cursor="pointer"> - {keyColumn.getNiceName(k)} - - ); - })} - : null - )} - - {y.bandwidth() > 15 && parseFloat(parameters["NumberOpacity"]) > 0 && - - {orderedRows - .filter(r => x(valueColumn.getValue(r))! > 20) - .map(r => { - var posx = x(valueColumn.getValue(r))!; - - return (= size / 2 ? posx : size - posx} - padding={labelMargin} - className="number-label sf-transition" - fill={parameters["NumberColor"] ?? "#000"} - dominantBaseline="middle" - opacity={parameters["NumberOpacity"]} - textAnchor="middle" - fontWeight="bold" - onClick={e => onDrillDown(r, e)} - cursor="pointer"> - {valueColumn.getValueNiceName(r)} - ); - })} - - } - + {valueColumn.getValueNiceName(row)} + + + } + + ); + })} + diff --git a/Signum.React.Extensions/Chart/D3Scripts/BubblePack.tsx b/Signum.React.Extensions/Chart/D3Scripts/BubblePack.tsx index e5d83d4ca4..895e96698f 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/BubblePack.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/BubblePack.tsx @@ -83,40 +83,40 @@ export default function renderBubblePack({ data, width, height, parameters, load nodes.orderByDescending(a => a.r).map(d => { const active = activeDetector?.(isFolder(d.data) ? ({ c2: d.data.folder }) : d.data); return ( - isFolder(d.data) ? onDrillDown({ c2: d.data.folder }, e) : onDrillDown(d.data, e)}> - - {!isFolder(d.data) && - numberSizeLimit ? "-0.5em" : undefined}> - {keyColumn.getValueNiceName(d.data as ChartRow)} - - } - {showNumber && d.r > numberSizeLimit && !isFolder(d.data) && - - {valueColumn.getValueNiceName(d.data as ChartRow)} - - } - - {isFolder(d.data) ? parentColumn!.getNiceName(d.data.folder) : - (keyColumn.getValueNiceName(d.data as ChartRow) + (parentColumn == null ? '' : (' (' + parentColumn.getValueNiceName(d.data as ChartRow) + ')')))}: - {isFolder(d.data) ? format(size.invert(d.value!)) : - (valueColumn.getValueNiceName(d.data) - + (colorScaleColumn == null ? '' : (' (' + colorScaleColumn.getValueNiceName(d.data) + ')')) - + (colorSchemeColumn == null ? '' : (' (' + colorSchemeColumn.getValueNiceName(d.data) + ')')) - )} - - ); + isFolder(d.data) ? onDrillDown({ c2: d.data.folder }, e) : onDrillDown(d.data, e)}> + + {!isFolder(d.data) && + numberSizeLimit ? "-0.5em" : undefined}> + {keyColumn.getValueNiceName(d.data as ChartRow)} + + } + {showNumber && d.r > numberSizeLimit && !isFolder(d.data) && + + {valueColumn.getValueNiceName(d.data as ChartRow)} + + } + + {isFolder(d.data) ? parentColumn!.getNiceName(d.data.folder) : + (keyColumn.getValueNiceName(d.data as ChartRow) + (parentColumn == null ? '' : (' (' + parentColumn.getValueNiceName(d.data as ChartRow) + ')')))}: + {isFolder(d.data) ? format(size.invert(d.value!)) : + (valueColumn.getValueNiceName(d.data) + + (colorScaleColumn == null ? '' : (' (' + colorScaleColumn.getValueNiceName(d.data) + ')')) + + (colorSchemeColumn == null ? '' : (' (' + colorSchemeColumn.getValueNiceName(d.data) + ')')) + )} + + ); }) } diff --git a/Signum.React.Extensions/Chart/D3Scripts/Bubbleplot.tsx b/Signum.React.Extensions/Chart/D3Scripts/Bubbleplot.tsx index 35cca8d7c6..77e9937fe7 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/Bubbleplot.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/Bubbleplot.tsx @@ -95,13 +95,13 @@ export default function renderBubbleplot({ data, width, height, parameters, load return ( c.getValueKey(r)).join("/")} - className="shape-serie sf-transition" + className="shape-serie sf-transition shadow-group" opacity={active == false ? .5 : undefined} transform={translate(x(horizontalColumn.getValue(r))!, -y(verticalColumn.getValue(r))!) + (initialLoad ? scale(0, 0) : scale(1, 1))} cursor="pointer" onClick={e => onDrillDown(r, e)} > - - {d3.utcDays(new Date(Date.UTC(year, 0, 1)), new Date(Date.UTC(year + 1, 0, 1))).map(d => { - const r: ChartRow | undefined = rowByDate[cleanDate(d)]; - const active = r && detector?.(r); - return r == undefined ? null : onDrillDown(r, e)}> - - {dateFormat(d) + (r == undefined ? "" : ("(" + valueColumn.getValueNiceName(r) + ")"))} - - - })} + {d3.utcDays(new Date(Date.UTC(year, 0, 1)), new Date(Date.UTC(year + 1, 0, 1))).map(d => { + const r: ChartRow | undefined = rowByDate[cleanDate(d)]; + const active = r && detector?.(r); + return ( + + r == undefined ? null : onDrillDown(r, e)}> + + {dateFormat(d) + (r == undefined ? "" : ("(" + valueColumn.getValueNiceName(r) + ")"))} + + + + ) + })} diff --git a/Signum.React.Extensions/Chart/D3Scripts/Columns.tsx b/Signum.React.Extensions/Chart/D3Scripts/Columns.tsx index ecbd595ad4..1d991107ab 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/Columns.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/Columns.tsx @@ -2,13 +2,14 @@ import * as React from 'react' import * as d3 from 'd3' import * as ChartUtils from './Components/ChartUtils'; import { translate, scale, rotate, skewX, skewY, matrix, scaleFor } from './Components/ChartUtils'; -import { ChartTable, ChartColumn, ChartScriptProps } from '../ChartClient'; +import { ChartTable, ChartColumn, ChartScriptProps, ChartRow } from '../ChartClient'; import TextEllipsis from './Components/TextEllipsis'; import { XKeyTicks, YScaleTicks, XTitle } from './Components/Ticks'; import { XAxis, YAxis } from './Components/Axis'; import { Rule } from './Components/Rule'; import InitialMessage from './Components/InitialMessage'; import type { ChartScriptHorizontalProps } from './Line'; +import TextIfFits from './Components/TextIfFits'; export default function renderColumns({ data, width, height, parameters, loading, onDrillDown, initialLoad, chartRequest, memo, dashboardFilter }: ChartScriptProps): React.ReactElement { @@ -21,7 +22,7 @@ export default function renderColumns({ data, width, height, parameters, loading _1: 5, title: 15, _2: 10, - labels: parseInt(parameters["UnitMargin"]), + labels: parseInt(parameters["UnitMargin"]), _3: 5, ticks: 4, content: '*', @@ -69,7 +70,7 @@ export default function renderColumns({ data, width, height, parameters, loading - + {paintColumns({ xRule, yRule, x, y, keyValues, data, parameters, initialLoad, onDrillDown, colIndex: 0, colCount: 1, memo, detector })} @@ -113,81 +114,78 @@ export function paintColumns({ xRule, yRule, x, y, keyValues, data, parameters, return ( <> - {orderedRows.map(r => { - var active = detector?.(r); - var key = keyColumn.getValueKey(r); + {keyValues.map(k => { + var key = keyColumn.getKey(k); + + var row: ChartRow | undefined = rowsByKey[key]; + + var active = detector?.(row); + + const posy = y(row ? valueColumn.getValue(row) : 0)!; return ( - 4 ? '#fff' : undefined} - strokeWidth={active == true ? 3 : undefined} - transform={(initialLoad ? scale(1, 0) : scale(1, 1)) + translate(x(key)!, -y(valueColumn.getValue(r))!)} - height={y(valueColumn.getValue(r))} - width={bandwidth} - fill={keyColumn.getValueColor(r) ?? color(key)} - cursor="pointer" - onClick={e => onDrillDown(r, e)}> - - {keyColumn.getValueNiceName(r) + ': ' + valueColumn.getValueNiceName(r)} - - + + {row && onDrillDown(row!, e)}> + + {keyColumn.getValueNiceName(row) + ': ' + valueColumn.getValueNiceName(row)} + + } + {bandwidth > 15 && + (isMargin ? + + onDrillDown({ c1: key }, e)}> + {keyColumn.getNiceName(key)} + + : + isInside ? + + = size / 2 ? posy : size - posy} padding={labelMargin} className="x-label sf-transition" + transform={translate(x(keyColumn.getKey(key))! + bandwidth / 2, -posy) + rotate(-90)} + dominantBaseline="middle" + fontWeight="bold" + fill={posy >= size / 2 ? '#fff' : (keyColumn.getColor(key) ?? color(keyColumn.getKey(key)))} + dx={posy >= size / 2 ? -labelMargin : labelMargin} + textAnchor={posy >= size / 2 ? 'end' : 'start'} + onClick={e => onDrillDown({ c0: key }, e)} + cursor="pointer"> + {keyColumn.getNiceName(key)} + + : null + )} + {parseFloat(parameters["NumberOpacity"]) > 0 && bandwidth > 15 && row && + + onDrillDown(row!, e)}> + {valueColumn.getValueNiceName(row)} + + } + ); })} - - {bandwidth > 15 && - (isMargin ? - - {(isAll ? keyValues : orderedRows.map(r => keyColumn.getValue(r))).map(k => < TextEllipsis key={keyColumn.getKey(k)} maxWidth={yRule.size('labels')} padding={labelMargin} className="x-label sf-transition" - transform={translate(x(keyColumn.getKey(k))! + bandwidth / 2, 0) + rotate(-90)} - dominantBaseline="middle" - fontWeight="bold" - fill={(keyColumn.getColor(k) ?? color(keyColumn.getKey(k)))} - textAnchor="end" - cursor="pointer" - onClick={e => onDrillDown({ c1: k }, e)}> - {keyColumn.getNiceName(k)} - )} - : - isInside ? - - {(isAll ? keyValues : orderedRows.map(r => keyColumn.getValue(r))).map(k => { - const row = rowsByKey[keyColumn.getKey(k)]; - const posy = y(row ? valueColumn.getValue(row) : 0)!; - return ( - = size / 2 ? posy : size - posy} padding={labelMargin} className="x-label sf-transition" - transform={translate(x(keyColumn.getKey(k))! + bandwidth / 2, -posy) + rotate(-90)} - dominantBaseline="middle" - fontWeight="bold" - fill={posy >= size / 2 ? '#fff' : (keyColumn.getColor(k) ?? color(keyColumn.getKey(k)))} - dx={posy >= size / 2 ? -labelMargin : labelMargin} - textAnchor={posy >= size / 2 ? 'end' : 'start'} - onClick={e => onDrillDown({ c0: k }, e)} - cursor="pointer"> - {keyColumn.getNiceName(k)} - ); - })} - : null - )} - - {parseFloat(parameters["NumberOpacity"]) > 0 && bandwidth > 15 && - - {orderedRows - .filter(r => y(valueColumn.getValue(r))! > 10) - .map(r => onDrillDown(r, e)}> - {valueColumn.getValueNiceName(r)} - )} - } - ); } diff --git a/Signum.React.Extensions/Chart/D3Scripts/Components/TextIfFits.tsx b/Signum.React.Extensions/Chart/D3Scripts/Components/TextIfFits.tsx new file mode 100644 index 0000000000..ae971bee5f --- /dev/null +++ b/Signum.React.Extensions/Chart/D3Scripts/Components/TextIfFits.tsx @@ -0,0 +1,35 @@ +import * as React from 'react' + +export interface TextIfFitsProps extends React.SVGProps{ + maxWidth: number; + padding?: number; + etcText?: string; +} + +export default function TextIfFits({ maxWidth, padding, children, etcText, ...atts } : TextIfFitsProps) { + + const txt = React.useRef(null); + + React.useEffect(() => { + var width = maxWidth; + if (padding) + width -= padding * 2; + + let txtElem = txt.current!; + txtElem.textContent = getString(children); + let textLength = txtElem.getComputedTextLength(); + console.log("Width:", width, " textLength:", textLength, " text: ", txtElem.textContent); + if (textLength > width) + txtElem.textContent = ""; + }, [maxWidth, padding, etcText, getString(children)]); + + return ( + + {children ?? ""} + + ); +} + +function getString(children: React.ReactNode) { + return React.Children.toArray(children)[0] as string; +} diff --git a/Signum.React.Extensions/Chart/D3Scripts/Components/Ticks.tsx b/Signum.React.Extensions/Chart/D3Scripts/Components/Ticks.tsx index 93f69b637f..4585edab49 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/Components/Ticks.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/Components/Ticks.tsx @@ -11,7 +11,7 @@ export function YScaleTicks({ xRule, yRule, valueColumn, y, format }: { xRule: R var availableHeight = yRule.size("content"); var yTicks = y.ticks(availableHeight / 50); - var yTickFormat = format ?? y.tickFormat(availableHeight / 50); + var yTickFormat = format ?? valueColumn.getNiceName; // y.tickFormat(availableHeight / 50); return ( <> @@ -30,12 +30,13 @@ export function YScaleTicks({ xRule, yRule, valueColumn, y, format }: { xRule: R - {yTicks.map(t => {yTickFormat(t)} - )} + )} @@ -91,7 +92,7 @@ export function XScaleTicks({ xRule, yRule, valueColumn, x, format }: { xRule: R var availableWidth = yRule.size("content"); var xTicks = x.ticks(availableWidth / 50); - var xTickFormat = format ?? x.tickFormat(availableWidth / 50); + var xTickFormat = format ?? valueColumn.getNiceName;// x.tickFormat(availableWidth / 50); return ( <> diff --git a/Signum.React.Extensions/Chart/D3Scripts/Line.tsx b/Signum.React.Extensions/Chart/D3Scripts/Line.tsx index 31acda7578..1b94900e09 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/Line.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/Line.tsx @@ -65,8 +65,9 @@ export default function renderLine({ data, width, height, parameters, loading, c 5} isActive={detector && (val => detector!({ c0: val }))} onDrillDown={(v, e) => onDrillDown({ c0: v }, e)} /> - + + {paintLine({ xRule, yRule, x, y, keyValues, data, parameters, onDrillDown, initialLoad, memo, detector })} @@ -168,47 +169,43 @@ export function paintLine({ xRule, yRule, x, y, keyValues, data, parameters, onD var row = rowByKey[key]; var active = detector?.(row); return ( - onDrillDown(row, e)} - cursor="pointer" - shapeRendering="initial"> - - {keyColumn.getValueNiceName(r) + ': ' + valueColumn.getValueNiceName(r)} - - + + onDrillDown(row, e)} + cursor="pointer" + shapeRendering="initial"> + + {keyColumn.getValueNiceName(r) + ': ' + valueColumn.getValueNiceName(r)} + + + { /*Point labels*/ + numberOpacity > 0 && + + onDrillDown(r, e)} + cursor="pointer" + shapeRendering="initial"> + {valueColumn.getValueNiceName(r)} + ) + + } + + ); })} } - - { /*Point labels*/ - numberOpacity > 0 && - - {orderedRows - .map(r => { - var key = keyColumn.getValueKey(r); - var row = rowByKey[key]; - var active = detector?.(row); - return ( onDrillDown(r, e)} - cursor="pointer" - shapeRendering="initial"> - {valueColumn.getValueNiceName(r)} - ) - })} - - } ); } diff --git a/Signum.React.Extensions/Chart/D3Scripts/MultiBars.tsx b/Signum.React.Extensions/Chart/D3Scripts/MultiBars.tsx index cca096dadb..3c236e27ad 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/MultiBars.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/MultiBars.tsx @@ -10,6 +10,7 @@ import { XScaleTicks, YKeyTicks } from './Components/Ticks'; import { XAxis, YAxis } from './Components/Axis'; import { Rule } from './Components/Rule'; import InitialMessage from './Components/InitialMessage'; +import TextIfFits from './Components/TextIfFits'; export default function renderMultiBars({ data, width, height, parameters, loading, onDrillDown, initialLoad, chartRequest, memo, dashboardFilter }: ChartScriptProps): React.ReactElement { @@ -102,48 +103,48 @@ export default function renderMultiBars({ data, width, height, parameters, loadi return ( - 4 ? '#fff' : undefined} - strokeWidth={active == true ? 3 : undefined} - fill={s.color || color(s.key)} - transform={translate(0, -y(key)! - ySubscale(s.key)! - ySubscale.bandwidth()) + (initialLoad ? scale(0, 1) : scale(1, 1))} - height={ySubscale.bandwidth()} - width={x(row.value)} - onClick={e => onDrillDown(row.rowClick, e)} - cursor="pointer"> - - {row.valueTitle} - - + + onDrillDown(row.rowClick, e)} + cursor="pointer"> + + {row.valueTitle} + + + { + ySubscale.bandwidth() > 15 && parseFloat(parameters["NumberOpacity"]) > 0 && + onDrillDown(r.values[s.key].rowClick, e)} + opacity={parameters["NumberOpacity"]} + fill={parameters["NumberColor"]} + dominantBaseline="middle" + textAnchor="middle" + fontWeight="bold"> + {r.values[s.key].valueNiceName} + + {r.values[s.key].valueTitle} + + + } + + ) }) } - { - ySubscale.bandwidth() > 15 && parseFloat(parameters["NumberOpacity"]) > 0 && - rowsInOrder - .filter(r => r.values[s.key] != undefined && x(r.values[s.key] && r.values[s.key].value)! > 16) - .map(r => onDrillDown(r.values[s.key].rowClick, e)} - opacity={parameters["NumberOpacity"]} - fill={parameters["NumberColor"]} - dominantBaseline="middle" - textAnchor="middle" - fontWeight="bold"> - {r.values[s.key].valueNiceName} - - {r.values[s.key].valueTitle} - - ) - } )} - detector!({ c1: row.value }))} onDrillDown={c.c1 && ((s, e) => onDrillDown({ c1: s.value }, e))}/> + detector!({ c1: row.value }))} onDrillDown={c.c1 && ((s, e) => onDrillDown({ c1: s.value }, e))} /> diff --git a/Signum.React.Extensions/Chart/D3Scripts/MultiColumns.tsx b/Signum.React.Extensions/Chart/D3Scripts/MultiColumns.tsx index c739d2b1a1..6293982240 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/MultiColumns.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/MultiColumns.tsx @@ -10,6 +10,7 @@ import Legend from './Components/Legend'; import { XAxis, YAxis } from './Components/Axis'; import { Rule } from './Components/Rule'; import InitialMessage from './Components/InitialMessage'; +import TextIfFits from './Components/TextIfFits'; export default function renderMultiColumns({ data, width, height, parameters, loading, onDrillDown, initialLoad, chartRequest, memo, dashboardFilter }: ChartClient.ChartScriptProps): React.ReactElement { @@ -99,48 +100,48 @@ export default function renderMultiColumns({ data, width, height, parameters, lo var key = keyColumn.getKey(r.rowValue); return ( - 4 ? '#fff' : undefined} - strokeWidth={active == true ? 3 : undefined} - fill={s.color || color(s.key)} - transform={(initialLoad ? scale(1, 0) : scale(1, 1)) + translate( - x(key)! + xSubscale(s.key)!, - - y(row.value)! - )} - width={xSubscale.bandwidth()} - height={y(row.value)} - onClick={e => onDrillDown(row.rowClick, e)} - cursor="pointer"> - - {row.valueTitle} - - + + onDrillDown(row.rowClick, e)} + cursor="pointer"> + + {row.valueTitle} + + + + {x.bandwidth() > 15 && parseFloat(parameters["NumberOpacity"]) > 0 && + onDrillDown(r.values[s.key].rowClick, e)} + opacity={parameters["NumberOpacity"]} + fill={parameters["NumberColor"]} + dominantBaseline="middle" + textAnchor="middle" + fontWeight="bold"> + {r.values[s.key].valueNiceName} + + {r.values[s.key].valueTitle} + + + } + + ); })} - {x.bandwidth() > 15 && parseFloat(parameters["NumberOpacity"]) > 0 && - rowsInOrder - .filter(r => r.values[s.key] != undefined && y(r.values[s.key].value)! > 10) - .map(r => onDrillDown(r.values[s.key].rowClick, e)} - opacity={parameters["NumberOpacity"]} - fill={parameters["NumberColor"]} - dominantBaseline="middle" - textAnchor="middle" - fontWeight="bold"> - {r.values[s.key].valueNiceName} - - {r.values[s.key].valueTitle} - - ) - } )} diff --git a/Signum.React.Extensions/Chart/D3Scripts/MultiLines.tsx b/Signum.React.Extensions/Chart/D3Scripts/MultiLines.tsx index e91efcd897..6977c3089c 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/MultiLines.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/MultiLines.tsx @@ -165,44 +165,35 @@ export default function renderMultiLines({ data, width, height, parameters, load var key = keyColumn.getKey(r.rowValue); return ( - onDrillDown(row.rowClick, e)} - cursor="pointer"> - - {row.valueTitle} - - + + onDrillDown(row.rowClick, e)} + cursor="pointer"> + + {row.valueTitle} + + + {numberOpacity > 0 && + onDrillDown(row.rowClick, e)} + cursor="pointer" + shapeRendering="initial"> + {row.valueNiceName} + + } + ); })} - {numberOpacity > 0 && - rowsInOrder - .map(r => { - - var row = r.values[s.key]; - if (row == null) - return undefined; - - var active = detector?.(row.rowClick); - return ( - onDrillDown(row.rowClick, e)} - cursor="pointer" - shapeRendering="initial"> - {row.valueNiceName} - - ); - }) - } ) } diff --git a/Signum.React.Extensions/Chart/D3Scripts/Pie.tsx b/Signum.React.Extensions/Chart/D3Scripts/Pie.tsx index 0a29666c47..1d85e3cdc5 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/Pie.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/Pie.tsx @@ -6,6 +6,7 @@ import { translate, scale, rotate, skewX, skewY, matrix, scaleFor } from './Comp import { ChartRow, ChartTable } from '../ChartClient'; import InitialMessage from './Components/InitialMessage'; import { KeyCodes } from '@framework/Components'; +import { TextRectangle } from './StackedLines'; export default function renderPie({ data, width, height, parameters, loading, onDrillDown, initialLoad, memo, chartRequest, dashboardFilter }: ChartClient.ChartScriptProps): React.ReactElement { @@ -41,9 +42,7 @@ export default function renderPie({ data, width, height, parameters, loading, on .outerRadius(outerRadious) .innerRadius(rInner); - var cx = (width / 2), - cy = (height / 2), - legendRadius = 1.2; + var legendRadius = 1.2; var detector = dashboardFilter?.getActiveDetector(chartRequest); @@ -53,10 +52,13 @@ export default function renderPie({ data, width, height, parameters, loading, on {orderedPie.map(slice => { + var active = detector?.(slice.data); + var m = (slice.endAngle + slice.startAngle) / 2; + var cuadr = Math.floor(12 * m / (2 * Math.PI)); var active = detector?.(slice.data); return ( - - + onDrillDown(slice.data, e)} cursor="pointer"> - +
{keyColumn.getValueNiceName(slice.data) + ': ' + valueColumn.getValueNiceName(slice.data)} - +
+ + onDrillDown(slice.data, e)} cursor="pointer"> + {((slice.endAngle - slice.startAngle) < (Math.PI / 16)) ? '' : pValueAsPercent == "Yes" ? + `${keyColumn.getValueNiceName(slice.data)} : ${Number(valueColumn.getValue(slice.data) / dataTotal).toLocaleString(undefined, { style: 'percent', minimumFractionDigits: 1 })}` : + keyColumn.getValueNiceName(slice.data)} + +
); })}
- - {orderedPie.orderBy(r => keyColumn.getValueKey(r.data)).map(slice => { - - var m = (slice.endAngle + slice.startAngle) / 2; - var cuadr = Math.floor(12 * m / (2 * Math.PI)); - var active = detector?.(slice.data); - return - onDrillDown(slice.data, e)} cursor="pointer"> - {((slice.endAngle - slice.startAngle) < (Math.PI / 16)) ? '' : pValueAsPercent == "Yes" ? - `${keyColumn.getValueNiceName(slice.data)} : ${Number(valueColumn.getValue(slice.data) / dataTotal).toLocaleString(undefined, { style: 'percent', minimumFractionDigits: 1 })}` : - keyColumn.getValueNiceName(slice.data)} - - ; - })} -
); diff --git a/Signum.React.Extensions/Chart/D3Scripts/Punchcard.tsx b/Signum.React.Extensions/Chart/D3Scripts/Punchcard.tsx index befb7a90cf..cec0a192b9 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/Punchcard.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/Punchcard.tsx @@ -132,7 +132,7 @@ export default function renderPunchcard({ data, width, height, parameters, loadi return { numberOpacity: n => { return area(n)! / (15 * 15); }, - renderer: r => sizeColumn ? sizeColumn.getValue(r) : 0, r => ({ - className:"punch sf-transition", + className:"punch sf-transition shadow", shapeRendering: "initial", fillOpacity: fillOpacity(r), fill: color == null ? (parameters["FillColor"] ?? 'black') : color(colorColumn!.getValue(r)), @@ -225,7 +225,7 @@ export default function renderPunchcard({ data, width, height, parameters, loadi .map(r => { const active = detector?.(r); return ( - onDrillDown(r, e)}> diff --git a/Signum.React.Extensions/Chart/D3Scripts/Scatterplot.tsx b/Signum.React.Extensions/Chart/D3Scripts/Scatterplot.tsx index 6f19810b87..5d770f76a8 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/Scatterplot.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/Scatterplot.tsx @@ -162,10 +162,10 @@ function SvgScatterplot({ data, keyColumns, xRule, yRule, initialLoad, y, x, const active = detector?.(r); return ( - c.getValueKey(r)).join("/")} className="shape-serie sf-transition" + c.getValueKey(r)).join("/")} className="shape-serie sf-transition shadow-group" opacity={active == false ? .5 : undefined} transform={translate(xRule.start('content'), yRule.end('content')) + (initialLoad ? scale(1, 0) : scale(1, 1))}> - { @@ -88,8 +89,8 @@ export default function renderStackedBars({ data, width, height, parameters, loa var colorByKey = pivot.columns.toObject(a => a.key, a => a.color); var format = pStack == "expand" ? d3.format(".0%") : - pStack == "zero" ? d3.format("") : - (n: number) => d3.format("")(n) + "?"; + pStack == "zero" ? valueColumn0.getNiceName : + (n: number) => valueColumn0.getNiceName(n) + "?"; var labelMargin = 5; @@ -119,47 +120,43 @@ export default function renderStackedBars({ data, width, height, parameters, loa var active = detector?.(row.rowClick); return ( - 4 ? '#fff' : undefined} - strokeWidth={active == true ? 3 : undefined} - fill={colorByKey[s.key] ?? color(s.key)} - height={y.bandwidth()} - width={x(r[1])! - x(r[0])!} - onClick={e => onDrillDown(row.rowClick, e)} - cursor="pointer"> - - {row.valueTitle} - - + + onDrillDown(row.rowClick, e)} + cursor="pointer"> + + {row.valueTitle} + + + {y.bandwidth() > 15 && parseFloat(parameters["NumberOpacity"]) > 0 && + onDrillDown(r.data.values[s.key].rowClick, e)} + fill={parameters["NumberColor"]} + dominantBaseline="middle" + opacity={parameters["NumberOpacity"]} + textAnchor="middle" + fontWeight="bold"> + {r.data.values[s.key].valueNiceName} + + {r.data.values[s.key].valueTitle} + + + } + + ); }).notNull()} - {y.bandwidth() > 15 && parseFloat(parameters["NumberOpacity"]) > 0 && - s.orderBy(r => keyColumn.getKey(r.data.rowValue)) - .filter(r => (x(r[1])! - x(r[0])!) > 20) - .map(r => { - return ( - onDrillDown(r.data.values[s.key].rowClick, e)} - fill={parameters["NumberColor"]} - dominantBaseline="middle" - opacity={parameters["NumberOpacity"]} - textAnchor="middle" - fontWeight="bold"> - {r.data.values[s.key].valueNiceName} - - {r.data.values[s.key].valueTitle} - - - ); - }) - } )} {y.bandwidth() > 15 && pivot.columns.length > 0 && ( diff --git a/Signum.React.Extensions/Chart/D3Scripts/StackedColumns.tsx b/Signum.React.Extensions/Chart/D3Scripts/StackedColumns.tsx index 292b371554..ced3b4d825 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/StackedColumns.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/StackedColumns.tsx @@ -11,6 +11,7 @@ import { XAxis, YAxis } from './Components/Axis'; import TextEllipsis from './Components/TextEllipsis'; import { Rule } from './Components/Rule'; import InitialMessage from './Components/InitialMessage'; +import TextIfFits from './Components/TextIfFits'; export default function renderStackedColumns({ data, width, height, parameters, loading, onDrillDown, initialLoad, chartRequest, memo, dashboardFilter }: ChartClient.ChartScriptProps): React.ReactElement { @@ -87,8 +88,8 @@ export default function renderStackedColumns({ data, width, height, parameters, var colorByKey = pivot.columns.toObject(a => a.key, a => a.color); var format = pStack == "expand" ? d3.format(".0%") : - pStack == "zero" ? d3.format("") : - (n: number) => d3.format("")(n) + "?"; + pStack == "zero" ? valueColumn0.getNiceName : + (n: number) => valueColumn0.getNiceName(n) + "?"; var labelMargin = 10; var size = yRule.size('content'); @@ -112,43 +113,41 @@ export default function renderStackedColumns({ data, width, height, parameters, var active = detector?.(row.rowClick); return ( - 4 ? '#fff' : undefined} - strokeWidth={active == true ? 3 : undefined} - fill={colorByKey[s.key] ?? color(s.key)} - width={x.bandwidth()} - height={y(r[1])! - y(r[0])!} - onClick={e => onDrillDown(row.rowClick, e)} - cursor="pointer"> - - {row.valueTitle} - - + + onDrillDown(row.rowClick, e)} + cursor="pointer"> + + {row.valueTitle} + + + {parseFloat(parameters["NumberOpacity"]) > 0 && x.bandwidth() > 15 && + onDrillDown(r.data.values[s.key].rowClick, e)} + fill={parameters["NumberColor"]} + dominantBaseline="middle" + opacity={parameters["NumberOpacity"]} + textAnchor="middle" + fontWeight="bold"> + {r.data.values[s.key].valueNiceName} + + {r.data.values[s.key].valueTitle} + + } + ); }).notNull()} - {parseFloat(parameters["NumberOpacity"]) > 0 && x.bandwidth() > 15 && - s.orderBy(r => keyColumn.getKey(r.data.rowValue)) - .filter(r => (y(r[1])! - y(r[0])!) > 10) - .map(r => onDrillDown(r.data.values[s.key].rowClick, e)} - fill={parameters["NumberColor"]} - dominantBaseline="middle" - opacity={parameters["NumberOpacity"]} - textAnchor="middle" - fontWeight="bold"> - {r.data.values[s.key].valueNiceName} - - {r.data.values[s.key].valueTitle} - - ) - } )} {x.bandwidth() > 15 && ( diff --git a/Signum.React.Extensions/Chart/D3Scripts/StackedLines.tsx b/Signum.React.Extensions/Chart/D3Scripts/StackedLines.tsx index e764e28cfa..ac656aa1ef 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/StackedLines.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/StackedLines.tsx @@ -97,8 +97,8 @@ export default function renderStackedLines({ data, width, height, parameters, lo var columnsByKey = pivot.columns.toObject(a => a.key); var format = pStack == "expand" ? d3.format(".0%") : - pStack == "zero" ? d3.format("") : - (n: number) => d3.format("")(n) + "?"; + pStack == "zero" ? valueColumn0.getNiceName : + (n: number) => valueColumn0.getNiceName(n) + "?";; var rectRadious = 2; @@ -129,60 +129,52 @@ export default function renderStackedLines({ data, width, height, parameters, lo if (row == undefined) return null; + if ((y(v[1])! - y(v[0])!)! <= 10) + return null; var active = detector?.(row.rowClick); return ( - onDrillDown(row.rowClick, e)} - cursor="pointer"> - - {row.valueTitle} - - + + onDrillDown(row.rowClick, e)} + cursor="pointer"> + + {row.valueTitle} + + + + {x.bandwidth() > 15 && parseFloat(parameters["NumberOpacity"]) > 0 && + onDrillDown(row.rowClick, e)} + textAnchor="middle" + fontWeight="bold"> + {row.valueNiceName} + + {row.valueTitle} + + + } + + ); })} - {x.bandwidth() > 15 && parseFloat(parameters["NumberOpacity"]) > 0 && - s.orderBy(v => keyColumn.getKey(v.data)) - .map(v => { - var dataKey = keyColumn.getKey(v.data); - var row = rowsByKey[dataKey]?.values[s.key]; - if (row == undefined) - return null; - - if ((y(v[1])! - y(v[0])!)! <= 10) - return null; - - const active = detector?.(row.rowClick); - - return ( - onDrillDown(row.rowClick, e)} - textAnchor="middle" - fontWeight="bold"> - {row.valueNiceName} - - {row.valueTitle} - - ) - }) - } )} detector!({c1: row.value }))} onDrillDown={c.c1 && ((s, e) => onDrillDown({ c1: s.value }, e))} /> diff --git a/Signum.React.Extensions/Chart/D3Scripts/TreeMap.tsx b/Signum.React.Extensions/Chart/D3Scripts/TreeMap.tsx index 13aac7c658..eb37c9ceb3 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/TreeMap.tsx +++ b/Signum.React.Extensions/Chart/D3Scripts/TreeMap.tsx @@ -94,7 +94,7 @@ export default function renderTreeMap({ data, width, height, parameters, loading {nodes.map((d, i) => { const active = activeDetector?.(isFolder(d.data) ? ({ c2: d.data.folder }) : d.data); - return ( + return ( {isFolder(d.data) && } {!isFolder(d.data) && -