@galaxy-io/dls 1.5.13 → 1.5.15
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/charts/BarChart.d.ts +26 -1
- package/dist/charts/BarChart.js +5 -3
- package/dist/charts/barChartGeometry.d.ts +7 -1
- package/dist/charts/barChartGeometry.js +84 -5
- package/dist/table/InfiniteSpreadsheet.js +1 -1
- package/dist/table/InfiniteTable.js +1 -1
- package/dist/table/VirtualizedInfiniteTable.js +1 -1
- package/package.json +1 -1
|
@@ -16,6 +16,31 @@ export type BarChartProps<TMetric extends string, TComponentKey extends string =
|
|
|
16
16
|
/** Tint the active category's full band, marking the hover position even
|
|
17
17
|
* where there is no data. @default true */
|
|
18
18
|
showCursor?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Floor, in px, for how short a *stack segment* may render — so a series
|
|
21
|
+
* that is a rounding error next to its neighbours still leaves a visible
|
|
22
|
+
* mark instead of disappearing. `0`, the default, draws segments at their
|
|
23
|
+
* true size.
|
|
24
|
+
*
|
|
25
|
+
* Opt-in because it is a lie, and a small one is worth being deliberate
|
|
26
|
+
* about: the pixels a sliver gains are taken from the other segments in the
|
|
27
|
+
* same stack, in proportion to how much slack each has. The stack's total
|
|
28
|
+
* height, its cap position and every value in the tooltip stay exactly
|
|
29
|
+
* right — only the split between segments shifts, by at most a couple of
|
|
30
|
+
* pixels per rescued sliver. Reach for it when presence/absence is the
|
|
31
|
+
* thing being read (an error series, a small cohort); leave it off when the
|
|
32
|
+
* chart is being read for proportion.
|
|
33
|
+
*
|
|
34
|
+
* Ignored on unstacked bars, which already have a 2px floor — there is no
|
|
35
|
+
* neighbour to take from, so it costs nothing there. Ignored, too, when a
|
|
36
|
+
* stack is so crowded that honouring it would pin every segment to the
|
|
37
|
+
* minimum: at that point the bar would stop describing its data, and the
|
|
38
|
+
* chart is better off saying so. Fold the tail into an "Other" component
|
|
39
|
+
* instead.
|
|
40
|
+
*
|
|
41
|
+
* @default 0
|
|
42
|
+
*/
|
|
43
|
+
minSegmentLength?: number;
|
|
19
44
|
/**
|
|
20
45
|
* Fires when a bar segment or a category is clicked. Supplying it is what
|
|
21
46
|
* makes the plot clickable.
|
|
@@ -48,5 +73,5 @@ export type BarChartProps<TMetric extends string, TComponentKey extends string =
|
|
|
48
73
|
* `bars` in a group render side by side, several `components` in a bar stack,
|
|
49
74
|
* and a chart doing both at once needs no extra configuration.
|
|
50
75
|
*/
|
|
51
|
-
declare const BarChart: <TMetric extends string, TComponentKey extends string = string>({ series, groups, width, height, fillWidth, fillHeight, aspectRatio, margin, normalization, showCursor, onSelect, selection, valueUnit, valueFormatter, labelFormatter, valueDomain, categoryLabelMode, categoryAxisTitle, valueAxisTitle, showGrid, showLegend, legendMaxItems, noTooltip, tooltipRenderer, tooltipMaxItems, isLoading, contentWhenEmpty, ariaLabel, ariaDescription, }: BarChartProps<TMetric, TComponentKey>) => import("react").JSX.Element;
|
|
76
|
+
declare const BarChart: <TMetric extends string, TComponentKey extends string = string>({ series, groups, width, height, fillWidth, fillHeight, aspectRatio, margin, normalization, showCursor, minSegmentLength, onSelect, selection, valueUnit, valueFormatter, labelFormatter, valueDomain, categoryLabelMode, categoryAxisTitle, valueAxisTitle, showGrid, showLegend, legendMaxItems, noTooltip, tooltipRenderer, tooltipMaxItems, isLoading, contentWhenEmpty, ariaLabel, ariaDescription, }: BarChartProps<TMetric, TComponentKey>) => import("react").JSX.Element;
|
|
52
77
|
export default BarChart;
|
package/dist/charts/BarChart.js
CHANGED
|
@@ -17,7 +17,7 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
17
17
|
* `bars` in a group render side by side, several `components` in a bar stack,
|
|
18
18
|
* and a chart doing both at once needs no extra configuration.
|
|
19
19
|
*/
|
|
20
|
-
var BarChart = ({ series, groups, width, height, fillWidth, fillHeight, aspectRatio, margin, normalization = BarChartNormalization.NONE, showCursor = true, onSelect, selection, valueUnit, valueFormatter, labelFormatter, valueDomain, categoryLabelMode, categoryAxisTitle, valueAxisTitle, showGrid = true, showLegend = true, legendMaxItems, noTooltip = false, tooltipRenderer, tooltipMaxItems, isLoading = false, contentWhenEmpty, ariaLabel, ariaDescription }) => {
|
|
20
|
+
var BarChart = ({ series, groups, width, height, fillWidth, fillHeight, aspectRatio, margin, normalization = BarChartNormalization.NONE, showCursor = true, minSegmentLength = 0, onSelect, selection, valueUnit, valueFormatter, labelFormatter, valueDomain, categoryLabelMode, categoryAxisTitle, valueAxisTitle, showGrid = true, showLegend = true, legendMaxItems, noTooltip = false, tooltipRenderer, tooltipMaxItems, isLoading = false, contentWhenEmpty, ariaLabel, ariaDescription }) => {
|
|
21
21
|
const containerRef = useRef(null);
|
|
22
22
|
const colors = useSeriesColorResolver(series);
|
|
23
23
|
/**
|
|
@@ -78,7 +78,8 @@ var BarChart = ({ series, groups, width, height, fillWidth, fillHeight, aspectRa
|
|
|
78
78
|
normalization,
|
|
79
79
|
isStacked,
|
|
80
80
|
domain,
|
|
81
|
-
resolveColor
|
|
81
|
+
resolveColor,
|
|
82
|
+
minSegmentLength
|
|
82
83
|
}), [
|
|
83
84
|
groups,
|
|
84
85
|
plotWidth,
|
|
@@ -86,7 +87,8 @@ var BarChart = ({ series, groups, width, height, fillWidth, fillHeight, aspectRa
|
|
|
86
87
|
normalization,
|
|
87
88
|
isStacked,
|
|
88
89
|
domain,
|
|
89
|
-
resolveColor
|
|
90
|
+
resolveColor,
|
|
91
|
+
minSegmentLength
|
|
90
92
|
]);
|
|
91
93
|
const isEmpty = groups.length === 0;
|
|
92
94
|
const legendItems = useMemo(() => getBarLegendItems({
|
|
@@ -106,6 +106,12 @@ interface BuildBarChartGeometryArgs<TMetric extends string, TComponentKey extend
|
|
|
106
106
|
* record to answer, which this module has no business importing.
|
|
107
107
|
*/
|
|
108
108
|
resolveColor: (args: ResolveBarColorArgs<TMetric>) => string;
|
|
109
|
+
/**
|
|
110
|
+
* Minimum rendered length for a stack segment, in px. `0` disables it.
|
|
111
|
+
*
|
|
112
|
+
* See the prop of the same name on `BarChart` for what it costs.
|
|
113
|
+
*/
|
|
114
|
+
minSegmentLength: number;
|
|
109
115
|
}
|
|
110
116
|
export interface ResolveBarColorArgs<TMetric extends string> {
|
|
111
117
|
metric: TMetric;
|
|
@@ -116,5 +122,5 @@ export interface ResolveBarColorArgs<TMetric extends string> {
|
|
|
116
122
|
/** The component's own palette override, if it declared one. */
|
|
117
123
|
explicit?: ChartPalette;
|
|
118
124
|
}
|
|
119
|
-
export declare function buildBarChartGeometry<TMetric extends string, TComponentKey extends string = string>({ groups, plotWidth, plotHeight, normalization, isStacked, domain, resolveColor, }: BuildBarChartGeometryArgs<TMetric, TComponentKey>): BarChartGeometry<TMetric, TComponentKey>;
|
|
125
|
+
export declare function buildBarChartGeometry<TMetric extends string, TComponentKey extends string = string>({ groups, plotWidth, plotHeight, normalization, isStacked, domain, resolveColor, minSegmentLength, }: BuildBarChartGeometryArgs<TMetric, TComponentKey>): BarChartGeometry<TMetric, TComponentKey>;
|
|
120
126
|
export {};
|
|
@@ -111,7 +111,86 @@ function getBarValueDomain(groups, normalization) {
|
|
|
111
111
|
}
|
|
112
112
|
return [min, max];
|
|
113
113
|
}
|
|
114
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Grow every under-sized segment to its minimum, paying for it out of the
|
|
116
|
+
* segments that have room to spare.
|
|
117
|
+
*
|
|
118
|
+
* `lengths` and `minima` are one side of one stack, in baseline-outward order.
|
|
119
|
+
* Returns `null` when nothing needs doing, and — deliberately — also when the
|
|
120
|
+
* bill cannot be paid: if the surplus across the whole side is smaller than the
|
|
121
|
+
* total deficit, every segment would end up pinned at its minimum and the bar
|
|
122
|
+
* would no longer be a picture of its data. A stack that crowded is telling the
|
|
123
|
+
* caller to drop the minimum or fold the tail into an "Other" component, and
|
|
124
|
+
* silently redrawing it as a ladder of equal blocks would hide that.
|
|
125
|
+
*/
|
|
126
|
+
function distributeMinSegmentLengths(lengths, minima) {
|
|
127
|
+
const result = [...lengths];
|
|
128
|
+
let deficit = 0;
|
|
129
|
+
let surplus = 0;
|
|
130
|
+
lengths.forEach((length, index) => {
|
|
131
|
+
if (length < minima[index]) {
|
|
132
|
+
deficit += minima[index] - length;
|
|
133
|
+
result[index] = minima[index];
|
|
134
|
+
} else surplus += length - minima[index];
|
|
135
|
+
});
|
|
136
|
+
if (deficit === 0 || surplus < deficit) return null;
|
|
137
|
+
const donors = result.map((length, index) => ({
|
|
138
|
+
index,
|
|
139
|
+
slack: length - minima[index]
|
|
140
|
+
})).filter((donor) => donor.slack > 0).sort((a, b) => b.slack - a.slack);
|
|
141
|
+
let remainingSlack = surplus;
|
|
142
|
+
for (const donor of donors) {
|
|
143
|
+
if (deficit <= 0) break;
|
|
144
|
+
const share = Math.min(donor.slack, Math.ceil(donor.slack / remainingSlack * deficit));
|
|
145
|
+
result[donor.index] -= share;
|
|
146
|
+
remainingSlack -= donor.slack;
|
|
147
|
+
deficit -= share;
|
|
148
|
+
}
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Snapped pixel spans for one bar's segments, with {@link minSegmentLength}
|
|
153
|
+
* enforced.
|
|
154
|
+
*
|
|
155
|
+
* The enforcement re-lays the affected side of the stack *cumulatively* from the
|
|
156
|
+
* baseline outward, rather than nudging each segment's own edges. That is the
|
|
157
|
+
* only way to grow a sliver without breaking the invariant the rounding above
|
|
158
|
+
* exists to protect: each segment starts exactly where the previous one ended,
|
|
159
|
+
* so there is still one shared edge per seam and no overlap. And because the
|
|
160
|
+
* redistribution is zero-sum, the side's total extent is unchanged — the cap
|
|
161
|
+
* still lands on the same axis value it did before.
|
|
162
|
+
*/
|
|
163
|
+
function resolveSegmentExtents(intervals, valueScale, minSegmentLength) {
|
|
164
|
+
const extents = intervals.map(({ from, to }) => ({
|
|
165
|
+
fromY: Math.round(valueScale(from)),
|
|
166
|
+
toY: Math.round(valueScale(to))
|
|
167
|
+
}));
|
|
168
|
+
if (minSegmentLength <= 0) return extents;
|
|
169
|
+
const adjustSide = (sign) => {
|
|
170
|
+
const indices = intervals.map((interval, index) => ({
|
|
171
|
+
interval,
|
|
172
|
+
index
|
|
173
|
+
})).filter(({ interval }) => Math.sign(interval.value) === sign).map(({ index }) => index);
|
|
174
|
+
if (indices.length === 0) return;
|
|
175
|
+
const adjusted = distributeMinSegmentLengths(indices.map((index) => Math.abs(extents[index].fromY - extents[index].toY)), indices.map((_, position) => position === 0 ? minSegmentLength : minSegmentLength + 2));
|
|
176
|
+
if (!adjusted) return;
|
|
177
|
+
const direction = sign > 0 ? -1 : 1;
|
|
178
|
+
const first = extents[indices[0]];
|
|
179
|
+
let cursor = sign > 0 ? first.fromY : first.toY;
|
|
180
|
+
indices.forEach((index, position) => {
|
|
181
|
+
const next = cursor + direction * adjusted[position];
|
|
182
|
+
extents[index] = {
|
|
183
|
+
fromY: cursor,
|
|
184
|
+
toY: next
|
|
185
|
+
};
|
|
186
|
+
cursor = next;
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
adjustSide(1);
|
|
190
|
+
adjustSide(-1);
|
|
191
|
+
return extents;
|
|
192
|
+
}
|
|
193
|
+
function buildBarChartGeometry({ groups, plotWidth, plotHeight, normalization, isStacked, domain, resolveColor, minSegmentLength }) {
|
|
115
194
|
const valueScale = buildValueScale(domain, [plotHeight, 0]);
|
|
116
195
|
const baselineY = valueScale(0);
|
|
117
196
|
const rects = [];
|
|
@@ -139,6 +218,8 @@ function buildBarChartGeometry({ groups, plotWidth, plotHeight, normalization, i
|
|
|
139
218
|
const barX = barsStartX + barIndex * (barWidth + effectiveBarGap);
|
|
140
219
|
const values = bar.components.map((component) => sanitizeValue(component.value));
|
|
141
220
|
const intervals = stackComponents(values, normalization);
|
|
221
|
+
const isStackedBar = bar.components.length > 1;
|
|
222
|
+
const extents = resolveSegmentExtents(intervals, valueScale, isStackedBar ? minSegmentLength : 0);
|
|
142
223
|
let firstPositive = -1;
|
|
143
224
|
let lastPositive = -1;
|
|
144
225
|
let firstNegative = -1;
|
|
@@ -154,15 +235,13 @@ function buildBarChartGeometry({ groups, plotWidth, plotHeight, normalization, i
|
|
|
154
235
|
}
|
|
155
236
|
});
|
|
156
237
|
bar.components.forEach((component, componentIndex) => {
|
|
157
|
-
const { value
|
|
238
|
+
const { value } = intervals[componentIndex];
|
|
158
239
|
if (value === 0) return;
|
|
159
240
|
const isNegative = value < 0;
|
|
160
241
|
const isCap = componentIndex === (isNegative ? lastNegative : lastPositive);
|
|
161
|
-
const fromY =
|
|
162
|
-
const toY = Math.round(valueScale(to));
|
|
242
|
+
const { fromY, toY } = extents[componentIndex];
|
|
163
243
|
const topY = Math.min(fromY, toY);
|
|
164
244
|
const rawHeight = Math.abs(fromY - toY);
|
|
165
|
-
const isStackedBar = bar.components.length > 1;
|
|
166
245
|
const shave = componentIndex === (isNegative ? firstNegative : firstPositive) ? 0 : 2;
|
|
167
246
|
const height = Math.max(isStackedBar ? 0 : 2, rawHeight - shave);
|
|
168
247
|
const y = isNegative ? topY + shave : topY;
|
|
@@ -129,7 +129,7 @@ function InfiniteSpreadsheet(props) {
|
|
|
129
129
|
const showEmpty = totalRowCount === 0 && !isLoading && !isError && Boolean(contentWhenEmpty);
|
|
130
130
|
const showRows = !showLoading && !showError && !showEmpty;
|
|
131
131
|
const trailingLoadingHeight = isFetchingNextPage ? loadingRowCount * rowHeight : 0;
|
|
132
|
-
const statusAvailableHeight = viewportHeight !== void 0 ? Math.max(0, viewportHeight - (headerHeight ?? 0)) : void 0;
|
|
132
|
+
const statusAvailableHeight = (height !== void 0 || Boolean(fillHeight)) && viewportHeight !== void 0 ? Math.max(0, viewportHeight - (headerHeight ?? 0)) : void 0;
|
|
133
133
|
const columnCount = columns.length + (showActionColumn ? 1 : 0);
|
|
134
134
|
return /* @__PURE__ */ jsx(TableOuterContainer, {
|
|
135
135
|
$width: width,
|
|
@@ -288,7 +288,7 @@ function InfiniteTable(props) {
|
|
|
288
288
|
const showLoading = isLoading && rows.length === 0;
|
|
289
289
|
const showError = isError && rows.length === 0;
|
|
290
290
|
const showEmpty = rows.length === 0 && !isLoading && !isError && Boolean(contentWhenEmpty);
|
|
291
|
-
const statusAvailableHeight = viewportHeight !== void 0 ? Math.max(0, viewportHeight - (headerHeight ?? 0)) : void 0;
|
|
291
|
+
const statusAvailableHeight = (height !== void 0 || minHeight !== void 0 || maxHeight !== void 0 || Boolean(fillHeight)) && viewportHeight !== void 0 ? Math.max(0, viewportHeight - (headerHeight ?? 0)) : void 0;
|
|
292
292
|
return /* @__PURE__ */ jsx(TableOuterContainer, {
|
|
293
293
|
$width: width,
|
|
294
294
|
$height: height,
|
|
@@ -125,7 +125,7 @@ function VirtualizedInfiniteTable(props) {
|
|
|
125
125
|
const showError = isError && totalRowCount === 0;
|
|
126
126
|
const showEmpty = totalRowCount === 0 && !isLoading && !isError && Boolean(contentWhenEmpty);
|
|
127
127
|
const showRows = !showLoading && !showError && !showEmpty;
|
|
128
|
-
const statusAvailableHeight = viewportHeight !== void 0 ? Math.max(0, viewportHeight - (headerHeight ?? 0)) : void 0;
|
|
128
|
+
const statusAvailableHeight = (height !== void 0 || minHeight !== void 0 || maxHeight !== void 0 || Boolean(fillHeight)) && viewportHeight !== void 0 ? Math.max(0, viewportHeight - (headerHeight ?? 0)) : void 0;
|
|
129
129
|
const virtualTotalSize = virtualizer.getTotalSize();
|
|
130
130
|
const trailingLoadingHeight = isFetchingNextPage ? loadingRowCount * rowHeight : 0;
|
|
131
131
|
return /* @__PURE__ */ jsx(TableOuterContainer, {
|