@pallasoft/ps-mobile-components 1.13.0 → 1.13.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/ps-chart-bar/ps-chart-bar.js +11 -4
- package/dist/components/ps-donut-chart/ps-donut-chart.js +5 -4
- package/dist/components/ps-donut-reveal/index.d.ts +1 -0
- package/dist/components/ps-donut-reveal/index.js +1 -0
- package/dist/components/ps-donut-reveal/ps-donut-reveal-props.d.ts +6 -0
- package/dist/components/ps-donut-reveal/ps-donut-reveal-props.js +1 -0
- package/dist/components/ps-donut-reveal/ps-donut-reveal.d.ts +2 -0
- package/dist/components/ps-donut-reveal/ps-donut-reveal.js +14 -0
- package/dist/components/ps-donut-slice/ps-donut-slice-props.d.ts +0 -4
- package/dist/components/ps-donut-slice/ps-donut-slice.d.ts +1 -1
- package/dist/components/ps-donut-slice/ps-donut-slice.js +2 -14
- package/dist/components/ps-sortable-grid/ps-sortable-grid.js +3 -3
- package/dist/components/ps-sortable-grid/ps-sortable-item-props.d.ts +1 -1
- package/dist/components/ps-sortable-grid/ps-sortable-item.js +19 -5
- package/dist/constants/ps-chart-bar-corner.d.ts +6 -0
- package/dist/constants/ps-chart-bar-corner.js +5 -0
- package/dist/constants/ps-chart-config.d.ts +4 -1
- package/dist/constants/ps-chart-config.js +4 -1
- package/dist/constants/ps-sortable-grid-config.d.ts +2 -0
- package/dist/constants/ps-sortable-grid-config.js +2 -0
- package/dist/interfaces/ps-chart-arc-data.d.ts +9 -0
- package/dist/interfaces/ps-chart-arc-data.js +1 -0
- package/dist/interfaces/ps-chart-bar-rect.d.ts +2 -0
- package/dist/interfaces/ps-donut-slice-data.d.ts +1 -3
- package/dist/interfaces/ps-sortable-measure-data.d.ts +4 -0
- package/dist/interfaces/ps-sortable-measure-data.js +1 -0
- package/dist/services/ps-chart-arc-service.d.ts +14 -0
- package/dist/services/ps-chart-arc-service.js +83 -0
- package/dist/services/ps-chart-bar-service.d.ts +2 -0
- package/dist/services/ps-chart-bar-service.js +18 -3
- package/dist/services/ps-chart-donut-service.d.ts +2 -0
- package/dist/services/ps-chart-donut-service.js +42 -12
- package/dist/services/ps-waterfall-service.js +2 -0
- package/package.json +1 -1
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import Animated, { useAnimatedProps } from 'react-native-reanimated';
|
|
3
|
-
import { Rect } from 'react-native-svg';
|
|
4
|
-
import {
|
|
3
|
+
import { G, Rect } from 'react-native-svg';
|
|
4
|
+
import { PsChartBarCorner } from '../../constants/ps-chart-bar-corner';
|
|
5
|
+
import { PsChartBarService } from '../../services/ps-chart-bar-service';
|
|
5
6
|
const AnimatedRect = Animated.createAnimatedComponent(Rect);
|
|
6
7
|
export const PsChartBar = ({ progress, rect }) => {
|
|
8
|
+
const radius = PsChartBarService.radius(rect);
|
|
9
|
+
const hasFlatBase = rect.corner === PsChartBarCorner.Value && radius > 0;
|
|
7
10
|
const animatedProps = useAnimatedProps(() => ({
|
|
8
11
|
height: rect.height * progress.value,
|
|
9
12
|
y: rect.isNegative ? rect.y : rect.y + rect.height * (1 - progress.value),
|
|
10
13
|
}));
|
|
11
|
-
|
|
14
|
+
const baseProps = useAnimatedProps(() => {
|
|
15
|
+
const height = Math.min(radius, rect.height * progress.value);
|
|
16
|
+
return { height, y: rect.isNegative ? rect.y : rect.y + rect.height - height };
|
|
17
|
+
});
|
|
18
|
+
return (_jsxs(G, { children: [_jsx(AnimatedRect, { animatedProps: animatedProps, fill: rect.color, height: rect.height, rx: radius, width: rect.width, x: rect.x, y: rect.y }), hasFlatBase ? (_jsx(AnimatedRect, { animatedProps: baseProps, fill: rect.color, height: Math.min(radius, rect.height), width: rect.width, x: rect.x, y: rect.isNegative ? rect.y : rect.y + rect.height - Math.min(radius, rect.height) })) : null] }));
|
|
12
19
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useId, useState } from 'react';
|
|
3
3
|
import { View } from 'react-native';
|
|
4
|
-
import Svg from 'react-native-svg';
|
|
4
|
+
import Svg, { G } from 'react-native-svg';
|
|
5
5
|
import { PsChartConfig } from '../../constants/ps-chart-config';
|
|
6
6
|
import { PsChartSkeletonVariant } from '../../constants/ps-chart-skeleton-variant';
|
|
7
7
|
import { usePsChartReveal } from '../../hooks/use-ps-chart-reveal';
|
|
@@ -12,6 +12,7 @@ import { PsChartDonutService } from '../../services/ps-chart-donut-service';
|
|
|
12
12
|
import { PsChartEmptyState } from '../ps-chart-empty-state/ps-chart-empty-state';
|
|
13
13
|
import { PsChartLegend } from '../ps-chart-legend/ps-chart-legend';
|
|
14
14
|
import { PsChartSkeleton } from '../ps-chart-skeleton/ps-chart-skeleton';
|
|
15
|
+
import { PsDonutReveal } from '../ps-donut-reveal/ps-donut-reveal';
|
|
15
16
|
import { PsDonutSlice } from '../ps-donut-slice/ps-donut-slice';
|
|
16
17
|
import { PsText } from '../ps-text/ps-text';
|
|
17
18
|
export const PsDonutChart = ({ centerLabel, centerValue, data, emptyLabel, formatShare, height = PsChartConfig.donutHeight, isLoading = false, revealKey, }) => {
|
|
@@ -19,6 +20,7 @@ export const PsDonutChart = ({ centerLabel, centerValue, data, emptyLabel, forma
|
|
|
19
20
|
const { onLayout, width } = usePsChartWidth();
|
|
20
21
|
const fill = usePsWidgetFill();
|
|
21
22
|
const [selected, setSelected] = useState(null);
|
|
23
|
+
const maskId = `donut${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
|
|
22
24
|
const progress = usePsChartReveal(!isLoading && width > 0, revealKey);
|
|
23
25
|
if (isLoading)
|
|
24
26
|
return _jsx(PsChartSkeleton, { height: height, variant: PsChartSkeletonVariant.Donut });
|
|
@@ -27,7 +29,6 @@ export const PsDonutChart = ({ centerLabel, centerValue, data, emptyLabel, forma
|
|
|
27
29
|
const slices = PsChartDonutService.slices(data, theme.chart, size);
|
|
28
30
|
if (slices.length === 0)
|
|
29
31
|
return _jsx(PsChartEmptyState, { height: height, label: emptyLabel });
|
|
30
|
-
const circumference = PsChartDonutService.circumference(size);
|
|
31
32
|
const chosen = selected === null ? null : data[selected];
|
|
32
33
|
const readValue = chosen === null
|
|
33
34
|
? centerValue
|
|
@@ -47,7 +48,7 @@ export const PsDonutChart = ({ centerLabel, centerValue, data, emptyLabel, forma
|
|
|
47
48
|
justifyContent: 'center',
|
|
48
49
|
minHeight: height + fill,
|
|
49
50
|
rowGap: PsChartConfig.donutGap,
|
|
50
|
-
}, testID: 'ps-donut-chart', children: [_jsxs(View, { style: { height: size, width: size }, children: [
|
|
51
|
+
}, testID: 'ps-donut-chart', children: [_jsxs(View, { style: { height: size, width: size }, children: [_jsxs(Svg, { height: size, width: size, children: [_jsx(PsDonutReveal, { id: maskId, progress: progress, size: size }), _jsx(G, { mask: `url(#${maskId})`, children: slices.map((slice, index) => slice.path === '' ? null : (_jsx(PsDonutSlice, { isDimmed: selected !== null && selected !== index, onPress: () => setSelected(selected === index ? null : index), slice: slice }, slice.id))) })] }), _jsxs(View, { pointerEvents: 'none', style: {
|
|
51
52
|
alignItems: 'center',
|
|
52
53
|
bottom: 0,
|
|
53
54
|
justifyContent: 'center',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ps-donut-reveal';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ps-donut-reveal';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import Animated, { useAnimatedProps } from 'react-native-reanimated';
|
|
3
|
+
import { Circle, Defs, Mask } from 'react-native-svg';
|
|
4
|
+
import { PsChartConfig } from '../../constants/ps-chart-config';
|
|
5
|
+
import { PsChartDonutService } from '../../services/ps-chart-donut-service';
|
|
6
|
+
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
|
|
7
|
+
export const PsDonutReveal = ({ id, progress, size }) => {
|
|
8
|
+
const centre = size / 2;
|
|
9
|
+
const circumference = PsChartDonutService.circumference(size);
|
|
10
|
+
const animatedProps = useAnimatedProps(() => ({
|
|
11
|
+
strokeDasharray: [progress.value * circumference, circumference],
|
|
12
|
+
}));
|
|
13
|
+
return (_jsx(Defs, { children: _jsx(Mask, { height: size, id: id, maskUnits: 'userSpaceOnUse', width: size, x: 0, y: 0, children: _jsx(AnimatedCircle, { animatedProps: animatedProps, cx: centre, cy: centre, fill: 'none', origin: `${centre}, ${centre}`, r: PsChartDonutService.radius(size), rotation: -90, stroke: 'white', strokeDasharray: [0, circumference], strokeWidth: PsChartConfig.donutThickness + PsChartConfig.donutRevealOverlap }) }) }));
|
|
14
|
+
};
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import type { SharedValue } from 'react-native-reanimated';
|
|
2
1
|
import type { PsDonutSliceData } from '../../interfaces/ps-donut-slice-data';
|
|
3
2
|
export interface PsDonutSliceProps {
|
|
4
|
-
circumference: number;
|
|
5
3
|
isDimmed: boolean;
|
|
6
4
|
onPress: () => void;
|
|
7
|
-
progress: SharedValue<number>;
|
|
8
|
-
size: number;
|
|
9
5
|
slice: PsDonutSliceData;
|
|
10
6
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { PsDonutSliceProps } from './ps-donut-slice-props';
|
|
2
|
-
export declare const PsDonutSlice: ({
|
|
2
|
+
export declare const PsDonutSlice: ({ isDimmed, onPress, slice }: PsDonutSliceProps) => import("react").JSX.Element;
|
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
3
|
-
import { Circle } from 'react-native-svg';
|
|
2
|
+
import { Path } from 'react-native-svg';
|
|
4
3
|
import { PsChartConfig } from '../../constants/ps-chart-config';
|
|
5
|
-
|
|
6
|
-
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
|
|
7
|
-
export const PsDonutSlice = ({ circumference, isDimmed, onPress, progress, size, slice, }) => {
|
|
8
|
-
const centre = size / 2;
|
|
9
|
-
const animatedProps = useAnimatedProps(() => ({
|
|
10
|
-
strokeDasharray: [
|
|
11
|
-
Math.max(0, Math.min(slice.dashLength, progress.value * circumference - slice.start)),
|
|
12
|
-
circumference,
|
|
13
|
-
],
|
|
14
|
-
}));
|
|
15
|
-
return (_jsx(AnimatedCircle, { animatedProps: animatedProps, cx: centre, cy: centre, fill: 'none', onPress: onPress, opacity: isDimmed ? 0.4 : 1, origin: `${centre}, ${centre}`, r: PsChartDonutService.radius(size), rotation: -90, stroke: slice.color, strokeDasharray: [slice.dashLength, circumference], strokeDashoffset: slice.dashOffset, strokeWidth: PsChartConfig.donutThickness }));
|
|
16
|
-
};
|
|
4
|
+
export const PsDonutSlice = ({ isDimmed, onPress, slice }) => (_jsx(Path, { d: slice.path, fill: slice.color, fillRule: 'evenodd', onPress: onPress, opacity: isDimmed ? PsChartConfig.donutDimmedOpacity : 1 }));
|
|
@@ -135,12 +135,12 @@ export const PsSortableGrid = ({ getItemSpan, isDisabled = false, items, labels,
|
|
|
135
135
|
notify(next);
|
|
136
136
|
PsHapticService.play('selection', hasHaptics);
|
|
137
137
|
}, [hasHaptics]);
|
|
138
|
-
const measure = useCallback((id, total,
|
|
139
|
-
const natural = PsSortableGridService.natural(total,
|
|
138
|
+
const measure = useCallback((id, total, fill) => {
|
|
139
|
+
const natural = PsSortableGridService.natural(total, fill);
|
|
140
140
|
setMeasured((current) => current[id] !== undefined &&
|
|
141
141
|
Math.abs(current[id] - natural) < PsSortableGridConfig.measureTolerance
|
|
142
142
|
? current
|
|
143
143
|
: { ...current, [id]: natural });
|
|
144
144
|
}, []);
|
|
145
|
-
return (_jsx(View, { onLayout: (event) => setWidth(event.nativeEvent.layout.width), style: { height: PsSortableGridService.contentHeight(rects) }, children: order.map((item, index) => (_jsx(PsSortableItem, { canMoveDown: index < order.length - 1, canMoveUp: index > 0, dragX: dragX, dragY: dragY, extra: extras[index], id: item.id, isActive: item.id === activeId, isDisabled: isDisabled, isReady: isReady, label: item.label, labels: labels, onEnd: end, onMeasure: measure, onMove: moveBy, onStart: start, onUpdate: update, rect: rects[index], children: renderItem({ isActive: item.id === activeId, item }) }, item.id))) }));
|
|
145
|
+
return (_jsx(View, { onLayout: (event) => setWidth(event.nativeEvent.layout.width), style: { height: PsSortableGridService.contentHeight(rects) }, testID: PsSortableGridConfig.testId, children: order.map((item, index) => (_jsx(PsSortableItem, { canMoveDown: index < order.length - 1, canMoveUp: index > 0, dragX: dragX, dragY: dragY, extra: extras[index], id: item.id, isActive: item.id === activeId, isDisabled: isDisabled, isReady: isReady, label: item.label, labels: labels, onEnd: end, onMeasure: measure, onMove: moveBy, onStart: start, onUpdate: update, rect: rects[index], children: renderItem({ isActive: item.id === activeId, item }) }, item.id))) }));
|
|
146
146
|
};
|
|
@@ -16,7 +16,7 @@ export interface PsSortableItemProps {
|
|
|
16
16
|
label: string;
|
|
17
17
|
labels: PsSortableGridLabels;
|
|
18
18
|
onEnd: () => void;
|
|
19
|
-
onMeasure: (id: string, height: number,
|
|
19
|
+
onMeasure: (id: string, height: number, fill: number) => void;
|
|
20
20
|
onMove: (id: string, delta: number) => void;
|
|
21
21
|
onStart: (id: string, absoluteY: number) => void;
|
|
22
22
|
onUpdate: (translationX: number, translationY: number, absoluteY: number) => void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef } from 'react';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
3
|
+
import { View } from 'react-native';
|
|
3
4
|
import { GestureDetector, usePanGesture } from 'react-native-gesture-handler';
|
|
4
5
|
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
|
|
5
6
|
import { PsSortableGridConfig } from '../../constants/ps-sortable-grid-config';
|
|
@@ -11,6 +12,13 @@ export const PsSortableItem = ({ canMoveDown, canMoveUp, children, dragX, dragY,
|
|
|
11
12
|
const top = useSharedValue(rect.y);
|
|
12
13
|
const scale = useSharedValue(1);
|
|
13
14
|
const wasActive = useRef(false);
|
|
15
|
+
const laidOut = useRef({ fill: 0, total: null });
|
|
16
|
+
const fill = isReady ? extra : 0;
|
|
17
|
+
const report = useCallback(() => {
|
|
18
|
+
const { fill: laidOutFill, total } = laidOut.current;
|
|
19
|
+
if (total !== null)
|
|
20
|
+
onMeasure(id, total, laidOutFill);
|
|
21
|
+
}, [id, onMeasure]);
|
|
14
22
|
useEffect(() => {
|
|
15
23
|
if (isActive) {
|
|
16
24
|
wasActive.current = true;
|
|
@@ -49,12 +57,15 @@ export const PsSortableItem = ({ canMoveDown, canMoveUp, children, dragX, dragY,
|
|
|
49
57
|
top: isActive ? dragY.value : top.value,
|
|
50
58
|
transform: [{ scale: scale.value }],
|
|
51
59
|
}));
|
|
52
|
-
return (_jsx(GestureDetector, { gesture: pan, children:
|
|
60
|
+
return (_jsx(GestureDetector, { gesture: pan, children: _jsxs(Animated.View, { accessibilityActions: isDisabled
|
|
53
61
|
? undefined
|
|
54
62
|
: [
|
|
55
63
|
...(canMoveUp ? [{ label: labels.moveUp, name: 'moveUp' }] : []),
|
|
56
64
|
...(canMoveDown ? [{ label: labels.moveDown, name: 'moveDown' }] : []),
|
|
57
|
-
], accessibilityLabel: label, accessible: !isDisabled, collapsable: false, onAccessibilityAction: (event) => onMove(id, event.nativeEvent.actionName === 'moveUp' ? -1 : 1), onLayout: (event) =>
|
|
65
|
+
], accessibilityLabel: label, accessible: !isDisabled, collapsable: false, onAccessibilityAction: (event) => onMove(id, event.nativeEvent.actionName === 'moveUp' ? -1 : 1), onLayout: (event) => {
|
|
66
|
+
laidOut.current.total = event.nativeEvent.layout.height;
|
|
67
|
+
report();
|
|
68
|
+
}, style: [
|
|
58
69
|
{
|
|
59
70
|
borderRadius: PsSortableGridConfig.radius,
|
|
60
71
|
opacity: isReady ? 1 : 0,
|
|
@@ -71,5 +82,8 @@ export const PsSortableItem = ({ canMoveDown, canMoveUp, children, dragX, dragY,
|
|
|
71
82
|
}
|
|
72
83
|
: undefined,
|
|
73
84
|
animatedStyle,
|
|
74
|
-
], children: _jsx(
|
|
85
|
+
], children: [_jsx(View, { onLayout: (event) => {
|
|
86
|
+
laidOut.current.fill = event.nativeEvent.layout.height;
|
|
87
|
+
report();
|
|
88
|
+
}, pointerEvents: 'none', style: { height: fill, left: 0, position: 'absolute', top: 0, width: 0 }, testID: PsSortableGridConfig.fillTestId }), _jsx(PsWidgetFillContext.Provider, { value: fill, children: children })] }) }));
|
|
75
89
|
};
|
|
@@ -6,6 +6,7 @@ export declare const PsChartConfig: {
|
|
|
6
6
|
readonly axisTickGap: 6;
|
|
7
7
|
readonly barGapRatio: 0.3;
|
|
8
8
|
readonly barRadius: 4;
|
|
9
|
+
readonly barRadiusWidthRatio: number;
|
|
9
10
|
readonly charWidthRatio: 0.62;
|
|
10
11
|
readonly compactBarGapRatio: 0.15;
|
|
11
12
|
readonly compactSlot: 24;
|
|
@@ -18,9 +19,11 @@ export declare const PsChartConfig: {
|
|
|
18
19
|
readonly donutCenterFontMax: 18;
|
|
19
20
|
readonly donutCenterFontMin: 11;
|
|
20
21
|
readonly donutCenterPadding: 8;
|
|
22
|
+
readonly donutDimmedOpacity: 0.4;
|
|
21
23
|
readonly donutGap: 16;
|
|
22
24
|
readonly donutMinLegendWidth: 120;
|
|
23
|
-
readonly
|
|
25
|
+
readonly donutRevealOverlap: 2;
|
|
26
|
+
readonly donutSliceGap: 3;
|
|
24
27
|
readonly drawMs: 700;
|
|
25
28
|
readonly ellipsis: "…";
|
|
26
29
|
readonly gridLineCount: 3;
|
|
@@ -6,6 +6,7 @@ export const PsChartConfig = {
|
|
|
6
6
|
axisTickGap: 6,
|
|
7
7
|
barGapRatio: 0.3,
|
|
8
8
|
barRadius: 4,
|
|
9
|
+
barRadiusWidthRatio: 1 / 3,
|
|
9
10
|
charWidthRatio: 0.62,
|
|
10
11
|
compactBarGapRatio: 0.15,
|
|
11
12
|
compactSlot: 24,
|
|
@@ -18,9 +19,11 @@ export const PsChartConfig = {
|
|
|
18
19
|
donutCenterFontMax: 18,
|
|
19
20
|
donutCenterFontMin: 11,
|
|
20
21
|
donutCenterPadding: 8,
|
|
22
|
+
donutDimmedOpacity: 0.4,
|
|
21
23
|
donutGap: 16,
|
|
22
24
|
donutMinLegendWidth: 120,
|
|
23
|
-
|
|
25
|
+
donutRevealOverlap: 2,
|
|
26
|
+
donutSliceGap: 3,
|
|
24
27
|
drawMs: 700,
|
|
25
28
|
ellipsis: '…',
|
|
26
29
|
gridLineCount: 3,
|
|
@@ -4,6 +4,7 @@ export declare const PsSortableGridConfig: {
|
|
|
4
4
|
readonly columnGap: 12;
|
|
5
5
|
readonly compactUnits: 2;
|
|
6
6
|
readonly edgeRatio: 0.14;
|
|
7
|
+
readonly fillTestId: "ps-sortable-fill";
|
|
7
8
|
readonly fullUnits: 2;
|
|
8
9
|
readonly holdTolerance: 8;
|
|
9
10
|
readonly longPressMs: 400;
|
|
@@ -17,4 +18,5 @@ export declare const PsSortableGridConfig: {
|
|
|
17
18
|
readonly scaleMs: 140;
|
|
18
19
|
readonly scrollRamp: 0.25;
|
|
19
20
|
readonly scrollTickMs: 16;
|
|
21
|
+
readonly testId: "ps-sortable-grid";
|
|
20
22
|
};
|
|
@@ -6,6 +6,7 @@ export const PsSortableGridConfig = {
|
|
|
6
6
|
columnGap: PsChartConfig.kpiGap,
|
|
7
7
|
compactUnits: 2,
|
|
8
8
|
edgeRatio: 0.14,
|
|
9
|
+
fillTestId: 'ps-sortable-fill',
|
|
9
10
|
fullUnits: 2,
|
|
10
11
|
holdTolerance: 8,
|
|
11
12
|
longPressMs: 400,
|
|
@@ -19,4 +20,5 @@ export const PsSortableGridConfig = {
|
|
|
19
20
|
scaleMs: 140,
|
|
20
21
|
scrollRamp: 0.25,
|
|
21
22
|
scrollTickMs: 16,
|
|
23
|
+
testId: 'ps-sortable-grid',
|
|
22
24
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PsChartArcData } from '../interfaces/ps-chart-arc-data';
|
|
2
|
+
export declare class PsChartArcService {
|
|
3
|
+
static sector(arc: PsChartArcData): string;
|
|
4
|
+
static ring(centre: number, inner: number, outer: number): string;
|
|
5
|
+
static minimumAngle(gap: number, radius: number, inner: number): number;
|
|
6
|
+
private static largeFlag;
|
|
7
|
+
private static angleOf;
|
|
8
|
+
private static cornerRadius;
|
|
9
|
+
private static corner;
|
|
10
|
+
private static edge;
|
|
11
|
+
private static at;
|
|
12
|
+
private static scale;
|
|
13
|
+
private static text;
|
|
14
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export class PsChartArcService {
|
|
2
|
+
static sector(arc) {
|
|
3
|
+
const half = arc.gap / 2;
|
|
4
|
+
const radius = PsChartArcService.cornerRadius(arc);
|
|
5
|
+
const outerStart = PsChartArcService.corner(arc, arc.start, 1, arc.outer - radius, radius, half);
|
|
6
|
+
const outerEnd = PsChartArcService.corner(arc, arc.end, -1, arc.outer - radius, radius, half);
|
|
7
|
+
const innerEnd = PsChartArcService.corner(arc, arc.end, -1, arc.inner + radius, radius, half);
|
|
8
|
+
const innerStart = PsChartArcService.corner(arc, arc.start, 1, arc.inner + radius, radius, half);
|
|
9
|
+
const outerStartRim = PsChartArcService.scale(arc, outerStart, arc.outer / (arc.outer - radius));
|
|
10
|
+
const outerEndRim = PsChartArcService.scale(arc, outerEnd, arc.outer / (arc.outer - radius));
|
|
11
|
+
const innerEndRim = PsChartArcService.scale(arc, innerEnd, arc.inner / (arc.inner + radius));
|
|
12
|
+
const innerStartRim = PsChartArcService.scale(arc, innerStart, arc.inner / (arc.inner + radius));
|
|
13
|
+
const startOuterEdge = PsChartArcService.edge(arc, arc.start, 1, outerStart, half);
|
|
14
|
+
const endOuterEdge = PsChartArcService.edge(arc, arc.end, -1, outerEnd, half);
|
|
15
|
+
const endInnerEdge = PsChartArcService.edge(arc, arc.end, -1, innerEnd, half);
|
|
16
|
+
const startInnerEdge = PsChartArcService.edge(arc, arc.start, 1, innerStart, half);
|
|
17
|
+
return [
|
|
18
|
+
`M${PsChartArcService.text(startOuterEdge)}`,
|
|
19
|
+
`A${radius} ${radius} 0 0 1 ${PsChartArcService.text(outerStartRim)}`,
|
|
20
|
+
`A${arc.outer} ${arc.outer} 0 ${PsChartArcService.largeFlag(arc, outerStartRim, outerEndRim)} 1 ${PsChartArcService.text(outerEndRim)}`,
|
|
21
|
+
`A${radius} ${radius} 0 0 1 ${PsChartArcService.text(endOuterEdge)}`,
|
|
22
|
+
`L${PsChartArcService.text(endInnerEdge)}`,
|
|
23
|
+
`A${radius} ${radius} 0 0 1 ${PsChartArcService.text(innerEndRim)}`,
|
|
24
|
+
`A${arc.inner} ${arc.inner} 0 ${PsChartArcService.largeFlag(arc, innerStartRim, innerEndRim)} 0 ${PsChartArcService.text(innerStartRim)}`,
|
|
25
|
+
`A${radius} ${radius} 0 0 1 ${PsChartArcService.text(startInnerEdge)}`,
|
|
26
|
+
'Z',
|
|
27
|
+
].join(' ');
|
|
28
|
+
}
|
|
29
|
+
static ring(centre, inner, outer) {
|
|
30
|
+
return [
|
|
31
|
+
`M${centre} ${centre - outer}`,
|
|
32
|
+
`A${outer} ${outer} 0 1 1 ${centre} ${centre + outer}`,
|
|
33
|
+
`A${outer} ${outer} 0 1 1 ${centre} ${centre - outer}`,
|
|
34
|
+
`M${centre} ${centre - inner}`,
|
|
35
|
+
`A${inner} ${inner} 0 1 0 ${centre} ${centre + inner}`,
|
|
36
|
+
`A${inner} ${inner} 0 1 0 ${centre} ${centre - inner}`,
|
|
37
|
+
'Z',
|
|
38
|
+
].join(' ');
|
|
39
|
+
}
|
|
40
|
+
static minimumAngle(gap, radius, inner) {
|
|
41
|
+
return inner > 0 ? (gap + 2 * radius + 1) / inner : 0;
|
|
42
|
+
}
|
|
43
|
+
static largeFlag(arc, from, to) {
|
|
44
|
+
const turn = 2 * Math.PI;
|
|
45
|
+
const span = (((PsChartArcService.angleOf(arc, to) - PsChartArcService.angleOf(arc, from)) % turn) +
|
|
46
|
+
turn) %
|
|
47
|
+
turn;
|
|
48
|
+
return span > Math.PI ? 1 : 0;
|
|
49
|
+
}
|
|
50
|
+
static angleOf(arc, point) {
|
|
51
|
+
return Math.atan2(point.x - arc.centre, arc.centre - point.y);
|
|
52
|
+
}
|
|
53
|
+
static cornerRadius(arc) {
|
|
54
|
+
const room = ((arc.end - arc.start) * arc.inner - arc.gap) / 2;
|
|
55
|
+
return Math.max(0, Math.min(arc.radius, (arc.outer - arc.inner) / 2, room));
|
|
56
|
+
}
|
|
57
|
+
static corner(arc, angle, side, distance, radius, half) {
|
|
58
|
+
const offset = half + radius;
|
|
59
|
+
const along = Math.sqrt(Math.max(distance * distance - offset * offset, 0));
|
|
60
|
+
return PsChartArcService.at(arc, angle, side, along, offset);
|
|
61
|
+
}
|
|
62
|
+
static edge(arc, angle, side, centre, half) {
|
|
63
|
+
const dx = centre.x - arc.centre;
|
|
64
|
+
const dy = centre.y - arc.centre;
|
|
65
|
+
const along = dx * Math.sin(angle) - dy * Math.cos(angle);
|
|
66
|
+
return PsChartArcService.at(arc, angle, side, along, half);
|
|
67
|
+
}
|
|
68
|
+
static at(arc, angle, side, along, across) {
|
|
69
|
+
return {
|
|
70
|
+
x: arc.centre + along * Math.sin(angle) + side * across * Math.cos(angle),
|
|
71
|
+
y: arc.centre - along * Math.cos(angle) + side * across * Math.sin(angle),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
static scale(arc, point, ratio) {
|
|
75
|
+
return {
|
|
76
|
+
x: arc.centre + (point.x - arc.centre) * ratio,
|
|
77
|
+
y: arc.centre + (point.y - arc.centre) * ratio,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
static text(point) {
|
|
81
|
+
return `${point.x.toFixed(2)} ${point.y.toFixed(2)}`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -9,5 +9,7 @@ export declare class PsChartBarService {
|
|
|
9
9
|
static stacked(data: PsAreaChartDatum[], series: PsCartesianChartSeries[], colors: string[], domain: PsChartDomain, plot: PsChartPlot): PsChartBarRect[];
|
|
10
10
|
static slot(count: number, plot: PsChartPlot): number;
|
|
11
11
|
static group(slot: number, size: number): PsChartBarGroup;
|
|
12
|
+
static lastIndex(values: number[], sign: number): number;
|
|
13
|
+
static radius(rect: PsChartBarRect): number;
|
|
12
14
|
private static rect;
|
|
13
15
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PsChartBarCorner } from '../constants/ps-chart-bar-corner';
|
|
1
2
|
import { PsChartConfig } from '../constants/ps-chart-config';
|
|
2
3
|
import { PsChartScaleService } from './ps-chart-scale-service';
|
|
3
4
|
import { PsChartSeriesService } from './ps-chart-series-service';
|
|
@@ -10,7 +11,7 @@ export class PsChartBarService {
|
|
|
10
11
|
const value = PsChartSeriesService.value(datum, entry.dataKey);
|
|
11
12
|
const top = PsChartScaleService.y(value, domain, plot);
|
|
12
13
|
const left = plot.left + row * slot + offset + column * width;
|
|
13
|
-
return PsChartBarService.rect(`${row}:${entry.dataKey}`, colors[column], left, width, zero, top, value < 0);
|
|
14
|
+
return PsChartBarService.rect(`${row}:${entry.dataKey}`, colors[column], left, width, zero, top, value < 0, PsChartBarCorner.Value);
|
|
14
15
|
}));
|
|
15
16
|
}
|
|
16
17
|
static stacked(data, series, colors, domain, plot) {
|
|
@@ -19,6 +20,9 @@ export class PsChartBarService {
|
|
|
19
20
|
return data.flatMap((datum, row) => {
|
|
20
21
|
let positive = 0;
|
|
21
22
|
let negative = 0;
|
|
23
|
+
const values = series.map((entry) => PsChartSeriesService.value(datum, entry.dataKey));
|
|
24
|
+
const outerPositive = PsChartBarService.lastIndex(values, 1);
|
|
25
|
+
const outerNegative = PsChartBarService.lastIndex(values, -1);
|
|
22
26
|
return series.map((entry, column) => {
|
|
23
27
|
const value = PsChartSeriesService.value(datum, entry.dataKey);
|
|
24
28
|
const from = value < 0 ? negative : positive;
|
|
@@ -27,7 +31,9 @@ export class PsChartBarService {
|
|
|
27
31
|
negative = to;
|
|
28
32
|
else
|
|
29
33
|
positive = to;
|
|
30
|
-
return PsChartBarService.rect(`${row}:${entry.dataKey}`, colors[column], plot.left + row * slot + offset, width, PsChartScaleService.y(from, domain, plot), PsChartScaleService.y(to, domain, plot), value < 0
|
|
34
|
+
return PsChartBarService.rect(`${row}:${entry.dataKey}`, colors[column], plot.left + row * slot + offset, width, PsChartScaleService.y(from, domain, plot), PsChartScaleService.y(to, domain, plot), value < 0, column === outerPositive || column === outerNegative
|
|
35
|
+
? PsChartBarCorner.Value
|
|
36
|
+
: PsChartBarCorner.None);
|
|
31
37
|
});
|
|
32
38
|
});
|
|
33
39
|
}
|
|
@@ -41,9 +47,18 @@ export class PsChartBarService {
|
|
|
41
47
|
const total = Math.max(slot * (1 - gap), Math.min(PsChartConfig.minBarWidth * size, slot));
|
|
42
48
|
return { offset: (slot - total) / 2, width: total / size };
|
|
43
49
|
}
|
|
44
|
-
static
|
|
50
|
+
static lastIndex(values, sign) {
|
|
51
|
+
return values.reduce((last, value, index) => (Math.sign(value) === sign ? index : last), -1);
|
|
52
|
+
}
|
|
53
|
+
static radius(rect) {
|
|
54
|
+
if (rect.corner === PsChartBarCorner.None)
|
|
55
|
+
return 0;
|
|
56
|
+
return Math.min(PsChartConfig.barRadius, rect.width * PsChartConfig.barRadiusWidthRatio);
|
|
57
|
+
}
|
|
58
|
+
static rect(id, color, x, width, from, to, isNegative, corner) {
|
|
45
59
|
return {
|
|
46
60
|
color,
|
|
61
|
+
corner,
|
|
47
62
|
height: Math.abs(to - from),
|
|
48
63
|
id,
|
|
49
64
|
isNegative,
|
|
@@ -6,5 +6,7 @@ export declare class PsChartDonutService {
|
|
|
6
6
|
static centerFontSize(text: string, size: number): number;
|
|
7
7
|
static radius(size: number): number;
|
|
8
8
|
static circumference(size: number): number;
|
|
9
|
+
static inner(size: number): number;
|
|
9
10
|
static slices(data: PsDonutChartDatum[], colors: string[], size: number): PsDonutSliceData[];
|
|
11
|
+
static angles(shares: number[], minimum: number): number[];
|
|
10
12
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PsChartConfig } from '../constants/ps-chart-config';
|
|
2
|
+
import { PsChartArcService } from './ps-chart-arc-service';
|
|
2
3
|
import { PsChartTextService } from './ps-chart-text-service';
|
|
3
4
|
export class PsChartDonutService {
|
|
4
5
|
static layout(width, height) {
|
|
@@ -17,23 +18,52 @@ export class PsChartDonutService {
|
|
|
17
18
|
static circumference(size) {
|
|
18
19
|
return 2 * Math.PI * PsChartDonutService.radius(size);
|
|
19
20
|
}
|
|
21
|
+
static inner(size) {
|
|
22
|
+
return Math.max(size / 2 - PsChartConfig.donutThickness, 0);
|
|
23
|
+
}
|
|
20
24
|
static slices(data, colors, size) {
|
|
21
|
-
const
|
|
22
|
-
const total =
|
|
25
|
+
const shares = data.map((datum) => Math.max(datum.share, 0));
|
|
26
|
+
const total = shares.reduce((sum, share) => sum + share, 0);
|
|
23
27
|
if (total <= 0)
|
|
24
28
|
return [];
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
const centre = size / 2;
|
|
30
|
+
const outer = centre;
|
|
31
|
+
const inner = PsChartDonutService.inner(size);
|
|
32
|
+
const shown = shares.filter((share) => share > 0).length;
|
|
33
|
+
if (shown === 1)
|
|
34
|
+
return data.map((datum, index) => ({
|
|
29
35
|
color: colors[index % colors.length],
|
|
30
|
-
dashLength: Math.max(length - PsChartConfig.donutSliceGap, 0),
|
|
31
|
-
dashOffset: -start,
|
|
32
36
|
id: datum.id,
|
|
33
|
-
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
path: shares[index] > 0 ? PsChartArcService.ring(centre, inner, outer) : '',
|
|
38
|
+
}));
|
|
39
|
+
const angles = PsChartDonutService.angles(shares, PsChartArcService.minimumAngle(PsChartConfig.donutSliceGap, PsChartConfig.barRadius, inner));
|
|
40
|
+
let start = 0;
|
|
41
|
+
return data.map((datum, index) => {
|
|
42
|
+
const end = start + angles[index];
|
|
43
|
+
const path = angles[index] > 0
|
|
44
|
+
? PsChartArcService.sector({
|
|
45
|
+
centre,
|
|
46
|
+
end,
|
|
47
|
+
gap: PsChartConfig.donutSliceGap,
|
|
48
|
+
inner,
|
|
49
|
+
outer,
|
|
50
|
+
radius: PsChartConfig.barRadius,
|
|
51
|
+
start,
|
|
52
|
+
})
|
|
53
|
+
: '';
|
|
54
|
+
start = end;
|
|
55
|
+
return { color: colors[index % colors.length], id: datum.id, path };
|
|
37
56
|
});
|
|
38
57
|
}
|
|
58
|
+
static angles(shares, minimum) {
|
|
59
|
+
const turn = 2 * Math.PI;
|
|
60
|
+
const shown = shares.filter((share) => share > 0).length;
|
|
61
|
+
const floor = Math.min(minimum, turn / Math.max(shown, 1));
|
|
62
|
+
const total = shares.reduce((sum, share) => sum + share, 0);
|
|
63
|
+
const isSmall = shares.map((share) => share > 0 && (share / total) * turn < floor);
|
|
64
|
+
const smallCount = isSmall.filter(Boolean).length;
|
|
65
|
+
const largeTotal = shares.reduce((sum, share, index) => (isSmall[index] ? sum : sum + share), 0);
|
|
66
|
+
const left = turn - smallCount * floor;
|
|
67
|
+
return shares.map((share, index) => share <= 0 ? 0 : isSmall[index] ? floor : largeTotal > 0 ? (share / largeTotal) * left : 0);
|
|
68
|
+
}
|
|
39
69
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PsChartBarCorner } from '../constants/ps-chart-bar-corner';
|
|
1
2
|
import { PsChartBarService } from './ps-chart-bar-service';
|
|
2
3
|
import { PsChartScaleService } from './ps-chart-scale-service';
|
|
3
4
|
export class PsWaterfallService {
|
|
@@ -25,6 +26,7 @@ export class PsWaterfallService {
|
|
|
25
26
|
const to = PsChartScaleService.y(step.end, domain, plot);
|
|
26
27
|
return {
|
|
27
28
|
color: step.isTotal ? colors[2] : step.value < 0 ? colors[1] : colors[0],
|
|
29
|
+
corner: step.isTotal ? PsChartBarCorner.Value : PsChartBarCorner.Both,
|
|
28
30
|
height: Math.abs(to - from),
|
|
29
31
|
id: `${row}:${step.label}`,
|
|
30
32
|
isNegative: step.end < step.start,
|
package/package.json
CHANGED