panelui-native 0.44.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/lib/module/components/combobox/index.js +7 -1
- package/lib/module/components/combobox/index.js.map +1 -1
- package/lib/module/components/date-time-picker/index.js +254 -0
- package/lib/module/components/date-time-picker/index.js.map +1 -0
- package/lib/module/components/grid-item/index.js +486 -0
- package/lib/module/components/grid-item/index.js.map +1 -0
- package/lib/module/components/{kpi-chart → kpi}/index.js +62 -62
- package/lib/module/components/kpi/index.js.map +1 -0
- package/lib/module/components/line-chart/index.js +92 -10
- package/lib/module/components/line-chart/index.js.map +1 -1
- package/lib/module/components/pie-chart/index.js +625 -0
- package/lib/module/components/pie-chart/index.js.map +1 -0
- package/lib/module/components/scatter-chart/index.js +1173 -0
- package/lib/module/components/scatter-chart/index.js.map +1 -0
- package/lib/module/components/tabs/index.js +275 -33
- package/lib/module/components/tabs/index.js.map +1 -1
- package/lib/module/index.js +5 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/chart.js +81 -0
- package/lib/module/utils/chart.js.map +1 -1
- package/lib/typescript/src/components/combobox/index.d.ts.map +1 -1
- package/lib/typescript/src/components/date-time-picker/index.d.ts +121 -0
- package/lib/typescript/src/components/date-time-picker/index.d.ts.map +1 -0
- package/lib/typescript/src/components/grid-item/index.d.ts +292 -0
- package/lib/typescript/src/components/grid-item/index.d.ts.map +1 -0
- package/lib/typescript/src/components/{kpi-chart → kpi}/index.d.ts +65 -65
- package/lib/typescript/src/components/kpi/index.d.ts.map +1 -0
- package/lib/typescript/src/components/line-chart/index.d.ts +25 -1
- package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/pie-chart/index.d.ts +245 -0
- package/lib/typescript/src/components/pie-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/scatter-chart/index.d.ts +309 -0
- package/lib/typescript/src/components/scatter-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/tabs/index.d.ts +20 -2
- package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +6 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/chart.d.ts +34 -0
- package/lib/typescript/src/utils/chart.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/combobox/index.tsx +7 -1
- package/src/components/date-time-picker/index.tsx +375 -0
- package/src/components/grid-item/index.tsx +515 -0
- package/src/components/{kpi-chart → kpi}/index.tsx +85 -85
- package/src/components/line-chart/index.tsx +98 -8
- package/src/components/pie-chart/index.tsx +863 -0
- package/src/components/scatter-chart/index.tsx +1401 -0
- package/src/components/tabs/index.tsx +305 -35
- package/src/index.ts +65 -17
- package/src/utils/chart.ts +110 -0
- package/lib/module/components/kpi-chart/index.js.map +0 -1
- package/lib/typescript/src/components/kpi-chart/index.d.ts.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Kpi — one number, and what it is doing.
|
|
3
3
|
*
|
|
4
4
|
* A metric card is not a chart with a caption. The number is the message and
|
|
5
5
|
* the chart is the footnote, so the parts here are sized and ordered around
|
|
@@ -8,16 +8,16 @@
|
|
|
8
8
|
* because nobody is reading values off it.
|
|
9
9
|
*
|
|
10
10
|
* ```tsx
|
|
11
|
-
* <
|
|
12
|
-
* <
|
|
13
|
-
* <
|
|
14
|
-
* </
|
|
15
|
-
* <
|
|
16
|
-
* <
|
|
17
|
-
* <
|
|
18
|
-
* </
|
|
19
|
-
* <
|
|
20
|
-
* </
|
|
11
|
+
* <Kpi>
|
|
12
|
+
* <Kpi.Header>
|
|
13
|
+
* <Kpi.Title>Active users</Kpi.Title>
|
|
14
|
+
* </Kpi.Header>
|
|
15
|
+
* <Kpi.Content>
|
|
16
|
+
* <Kpi.Value>12,480</Kpi.Value>
|
|
17
|
+
* <Kpi.Trend value={12.4} />
|
|
18
|
+
* </Kpi.Content>
|
|
19
|
+
* <Kpi.Chart data={week} dataKey="v" />
|
|
20
|
+
* </Kpi>
|
|
21
21
|
* ```
|
|
22
22
|
*
|
|
23
23
|
* The trend is given a number rather than a written string, so the component
|
|
@@ -131,15 +131,15 @@ export type KpiTone = NonNullable<KpiVariantProps['tone']>;
|
|
|
131
131
|
/** Which direction of movement is the good news for this metric. */
|
|
132
132
|
export type KpiGoodDirection = 'up' | 'down' | 'none';
|
|
133
133
|
|
|
134
|
-
interface
|
|
134
|
+
interface KpiContextValue {
|
|
135
135
|
colorIndex: SeriesColorIndex;
|
|
136
136
|
goodDirection: KpiGoodDirection;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
const
|
|
139
|
+
const KpiContext = createContext<KpiContextValue | null>(null);
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
|
-
* Whether the part rendering is inside `
|
|
142
|
+
* Whether the part rendering is inside `Kpi.Header`.
|
|
143
143
|
*
|
|
144
144
|
* `Title` is the only part that cares, and it cares a great deal. In a header
|
|
145
145
|
* it is one of several things on a row and has to take the space between the
|
|
@@ -152,7 +152,7 @@ const KpiChartContext = createContext<KpiChartContextValue | null>(null);
|
|
|
152
152
|
const KpiHeaderContext = createContext(false);
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
|
-
* Whether the part rendering is inside `
|
|
155
|
+
* Whether the part rendering is inside `Kpi.Stat`.
|
|
156
156
|
*
|
|
157
157
|
* `Trend` is the one that cares. `self-center` on a flex child means "centre
|
|
158
158
|
* on the cross axis", and the cross axis is not the same axis in the two
|
|
@@ -163,9 +163,9 @@ const KpiHeaderContext = createContext(false);
|
|
|
163
163
|
*/
|
|
164
164
|
const KpiStatContext = createContext(false);
|
|
165
165
|
|
|
166
|
-
function
|
|
167
|
-
const context = useContext(
|
|
168
|
-
if (!context) throw new Error(`${part} must be used inside <
|
|
166
|
+
function useKpi(part: string): KpiContextValue {
|
|
167
|
+
const context = useContext(KpiContext);
|
|
168
|
+
if (!context) throw new Error(`${part} must be used inside <Kpi>.`);
|
|
169
169
|
return context;
|
|
170
170
|
}
|
|
171
171
|
|
|
@@ -173,7 +173,7 @@ function useKpiChart(part: string): KpiChartContextValue {
|
|
|
173
173
|
* Root.
|
|
174
174
|
* ------------------------------------------------------------------ */
|
|
175
175
|
|
|
176
|
-
export interface
|
|
176
|
+
export interface KpiProps extends Omit<ViewProps, 'children'> {
|
|
177
177
|
className?: string;
|
|
178
178
|
/**
|
|
179
179
|
* Which `--color-chart-*` token the sparkline and the icon take. Set on the
|
|
@@ -192,7 +192,7 @@ export interface KpiChartProps extends Omit<ViewProps, 'children'> {
|
|
|
192
192
|
children: ReactNode;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
const
|
|
195
|
+
const KpiRoot = forwardRef<View, KpiProps>(function KpiRoot(
|
|
196
196
|
{ className, colorIndex = 1, goodDirection = 'up', surface = true, children, ...props },
|
|
197
197
|
ref
|
|
198
198
|
) {
|
|
@@ -206,7 +206,7 @@ const KpiChartRoot = forwardRef<View, KpiChartProps>(function KpiChartRoot(
|
|
|
206
206
|
);
|
|
207
207
|
|
|
208
208
|
return (
|
|
209
|
-
<
|
|
209
|
+
<KpiContext.Provider value={context}>
|
|
210
210
|
{surface ? (
|
|
211
211
|
<Surface ref={ref} variant="secondary" padding="lg" className="w-full">
|
|
212
212
|
{body}
|
|
@@ -214,7 +214,7 @@ const KpiChartRoot = forwardRef<View, KpiChartProps>(function KpiChartRoot(
|
|
|
214
214
|
) : (
|
|
215
215
|
body
|
|
216
216
|
)}
|
|
217
|
-
</
|
|
217
|
+
</KpiContext.Provider>
|
|
218
218
|
);
|
|
219
219
|
});
|
|
220
220
|
|
|
@@ -222,13 +222,13 @@ const KpiChartRoot = forwardRef<View, KpiChartProps>(function KpiChartRoot(
|
|
|
222
222
|
* Header, and the things that live in it.
|
|
223
223
|
* ------------------------------------------------------------------ */
|
|
224
224
|
|
|
225
|
-
export interface
|
|
225
|
+
export interface KpiHeaderProps extends ViewProps {
|
|
226
226
|
className?: string;
|
|
227
227
|
children: ReactNode;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
/** The top row: an icon, the metric's name, and anything acting on it. */
|
|
231
|
-
function
|
|
231
|
+
function KpiHeader({ className, children, ...props }: KpiHeaderProps) {
|
|
232
232
|
const { header } = kpiVariants();
|
|
233
233
|
return (
|
|
234
234
|
<KpiHeaderContext.Provider value={true}>
|
|
@@ -239,7 +239,7 @@ function KpiChartHeader({ className, children, ...props }: KpiChartHeaderProps)
|
|
|
239
239
|
);
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
export interface
|
|
242
|
+
export interface KpiIconProps extends ViewProps {
|
|
243
243
|
className?: string;
|
|
244
244
|
/** Overrides the tint the card's `colorIndex` would give it. */
|
|
245
245
|
tone?: KpiTone;
|
|
@@ -253,7 +253,7 @@ export interface KpiChartIconProps extends ViewProps {
|
|
|
253
253
|
* from whatever set the app already uses — and an icon from outside this
|
|
254
254
|
* library will not read an ambient colour, so pass it one.
|
|
255
255
|
*/
|
|
256
|
-
function
|
|
256
|
+
function KpiIcon({ className, tone = 'neutral', children, ...props }: KpiIconProps) {
|
|
257
257
|
const { icon } = kpiVariants({ tone });
|
|
258
258
|
return (
|
|
259
259
|
<View className={icon({ className })} {...props}>
|
|
@@ -262,13 +262,13 @@ function KpiChartIcon({ className, tone = 'neutral', children, ...props }: KpiCh
|
|
|
262
262
|
);
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
export interface
|
|
265
|
+
export interface KpiTitleProps {
|
|
266
266
|
className?: string;
|
|
267
267
|
children: ReactNode;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
/** The metric's name. Quiet on purpose — the value is the thing being read. */
|
|
271
|
-
function
|
|
271
|
+
function KpiTitle({ className, children }: KpiTitleProps) {
|
|
272
272
|
const { title } = kpiVariants();
|
|
273
273
|
// Grows across a header row; never down a column — see `KpiHeaderContext`.
|
|
274
274
|
const inHeader = useContext(KpiHeaderContext);
|
|
@@ -279,7 +279,7 @@ function KpiChartTitle({ className, children }: KpiChartTitleProps) {
|
|
|
279
279
|
);
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
export interface
|
|
282
|
+
export interface KpiStatProps extends ViewProps {
|
|
283
283
|
className?: string;
|
|
284
284
|
children: ReactNode;
|
|
285
285
|
}
|
|
@@ -293,7 +293,7 @@ export interface KpiChartStatProps extends ViewProps {
|
|
|
293
293
|
* between facts. It also takes the width in an `inline` row, leaving the chart
|
|
294
294
|
* its column on the end.
|
|
295
295
|
*/
|
|
296
|
-
function
|
|
296
|
+
function KpiStat({ className, children, ...props }: KpiStatProps) {
|
|
297
297
|
const { stat } = kpiVariants();
|
|
298
298
|
return (
|
|
299
299
|
<KpiStatContext.Provider value={true}>
|
|
@@ -304,13 +304,13 @@ function KpiChartStat({ className, children, ...props }: KpiChartStatProps) {
|
|
|
304
304
|
);
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
export interface
|
|
307
|
+
export interface KpiActionsProps extends ViewProps {
|
|
308
308
|
className?: string;
|
|
309
309
|
children: ReactNode;
|
|
310
310
|
}
|
|
311
311
|
|
|
312
312
|
/** The trailing end of the header — a menu trigger, a filter, a link. */
|
|
313
|
-
function
|
|
313
|
+
function KpiActions({ className, children, ...props }: KpiActionsProps) {
|
|
314
314
|
return (
|
|
315
315
|
<View className={cn('flex-row items-center gap-1', className)} {...props}>
|
|
316
316
|
{children}
|
|
@@ -322,7 +322,7 @@ function KpiChartActions({ className, children, ...props }: KpiChartActionsProps
|
|
|
322
322
|
* The number, and what it is doing.
|
|
323
323
|
* ------------------------------------------------------------------ */
|
|
324
324
|
|
|
325
|
-
export interface
|
|
325
|
+
export interface KpiContentProps extends ViewProps {
|
|
326
326
|
className?: string;
|
|
327
327
|
/** `inline` puts the chart beside the value instead of under everything. */
|
|
328
328
|
layout?: NonNullable<KpiVariantProps['layout']>;
|
|
@@ -330,12 +330,12 @@ export interface KpiChartContentProps extends ViewProps {
|
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
/** The row the value and the trend share. */
|
|
333
|
-
function
|
|
333
|
+
function KpiContent({
|
|
334
334
|
className,
|
|
335
335
|
layout = 'below',
|
|
336
336
|
children,
|
|
337
337
|
...props
|
|
338
|
-
}:
|
|
338
|
+
}: KpiContentProps) {
|
|
339
339
|
const { content } = kpiVariants({ layout });
|
|
340
340
|
return (
|
|
341
341
|
<View className={content({ className })} {...props}>
|
|
@@ -344,7 +344,7 @@ function KpiChartContent({
|
|
|
344
344
|
);
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
export interface
|
|
347
|
+
export interface KpiValueProps {
|
|
348
348
|
className?: string;
|
|
349
349
|
children: ReactNode;
|
|
350
350
|
}
|
|
@@ -356,7 +356,7 @@ export interface KpiChartValueProps {
|
|
|
356
356
|
* and units are locale decisions, and a component that guessed them would be
|
|
357
357
|
* wrong in a way that is hard to notice and impossible to override.
|
|
358
358
|
*/
|
|
359
|
-
function
|
|
359
|
+
function KpiValue({ className, children }: KpiValueProps) {
|
|
360
360
|
const { value } = kpiVariants();
|
|
361
361
|
return (
|
|
362
362
|
<Text className={value({ className })} numberOfLines={1}>
|
|
@@ -365,7 +365,7 @@ function KpiChartValue({ className, children }: KpiChartValueProps) {
|
|
|
365
365
|
);
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
export interface
|
|
368
|
+
export interface KpiTrendProps extends Omit<ViewProps, 'children'> {
|
|
369
369
|
className?: string;
|
|
370
370
|
/**
|
|
371
371
|
* How much it moved, as a percentage. The sign carries the direction, so
|
|
@@ -403,7 +403,7 @@ export interface KpiChartTrendProps extends Omit<ViewProps, 'children'> {
|
|
|
403
403
|
* label has to remember to change the colour when the metric changes, and
|
|
404
404
|
* nobody does.
|
|
405
405
|
*/
|
|
406
|
-
function
|
|
406
|
+
function KpiTrend({
|
|
407
407
|
className,
|
|
408
408
|
value,
|
|
409
409
|
format,
|
|
@@ -413,8 +413,8 @@ function KpiChartTrend({
|
|
|
413
413
|
threshold = 0,
|
|
414
414
|
children,
|
|
415
415
|
...props
|
|
416
|
-
}:
|
|
417
|
-
const context =
|
|
416
|
+
}: KpiTrendProps) {
|
|
417
|
+
const context = useKpi('Kpi.Trend');
|
|
418
418
|
const polarity = goodDirection ?? context.goodDirection;
|
|
419
419
|
|
|
420
420
|
const flat = Math.abs(value) <= threshold;
|
|
@@ -478,7 +478,7 @@ function KpiChartTrend({
|
|
|
478
478
|
* The chart, and the bar.
|
|
479
479
|
* ------------------------------------------------------------------ */
|
|
480
480
|
|
|
481
|
-
export interface
|
|
481
|
+
export interface KpiSparklineProps {
|
|
482
482
|
className?: string;
|
|
483
483
|
/** The rows. One point each, in order. */
|
|
484
484
|
data: LineChartDatum[];
|
|
@@ -510,7 +510,7 @@ export interface KpiChartSparklineProps {
|
|
|
510
510
|
* every point of padding is a point the shape is not using. Nobody reads a
|
|
511
511
|
* value off one of these; they read whether it is going up.
|
|
512
512
|
*/
|
|
513
|
-
function
|
|
513
|
+
function KpiSparkline({
|
|
514
514
|
className,
|
|
515
515
|
data,
|
|
516
516
|
dataKey,
|
|
@@ -519,8 +519,8 @@ function KpiChartSparkline({
|
|
|
519
519
|
height,
|
|
520
520
|
inline = false,
|
|
521
521
|
strokeWidth = 2,
|
|
522
|
-
}:
|
|
523
|
-
const context =
|
|
522
|
+
}: KpiSparklineProps) {
|
|
523
|
+
const context = useKpi('Kpi.Chart');
|
|
524
524
|
const index = colorIndex ?? context.colorIndex;
|
|
525
525
|
const fill = filled ?? !inline;
|
|
526
526
|
|
|
@@ -541,7 +541,7 @@ function KpiChartSparkline({
|
|
|
541
541
|
);
|
|
542
542
|
}
|
|
543
543
|
|
|
544
|
-
export interface
|
|
544
|
+
export interface KpiProgressProps {
|
|
545
545
|
className?: string;
|
|
546
546
|
/** Where it has got to. */
|
|
547
547
|
value: number;
|
|
@@ -554,13 +554,13 @@ export interface KpiChartProgressProps {
|
|
|
554
554
|
}
|
|
555
555
|
|
|
556
556
|
/** Progress towards a target, for a metric that has one. */
|
|
557
|
-
function
|
|
557
|
+
function KpiProgressBar({
|
|
558
558
|
className,
|
|
559
559
|
value,
|
|
560
560
|
maxValue = 100,
|
|
561
561
|
label,
|
|
562
562
|
showValueLabel,
|
|
563
|
-
}:
|
|
563
|
+
}: KpiProgressProps) {
|
|
564
564
|
return (
|
|
565
565
|
<Progress
|
|
566
566
|
className={className}
|
|
@@ -577,13 +577,13 @@ function KpiChartProgressBar({
|
|
|
577
577
|
* Footer and separator.
|
|
578
578
|
* ------------------------------------------------------------------ */
|
|
579
579
|
|
|
580
|
-
export interface
|
|
580
|
+
export interface KpiFooterProps extends ViewProps {
|
|
581
581
|
className?: string;
|
|
582
582
|
children: ReactNode;
|
|
583
583
|
}
|
|
584
584
|
|
|
585
585
|
/** The bottom strip — a comparison period, a caveat, a link. */
|
|
586
|
-
function
|
|
586
|
+
function KpiFooter({ className, children, ...props }: KpiFooterProps) {
|
|
587
587
|
const { footer } = kpiVariants();
|
|
588
588
|
return (
|
|
589
589
|
<View className={footer({ className })} {...props}>
|
|
@@ -592,12 +592,12 @@ function KpiChartFooter({ className, children, ...props }: KpiChartFooterProps)
|
|
|
592
592
|
);
|
|
593
593
|
}
|
|
594
594
|
|
|
595
|
-
export interface
|
|
595
|
+
export interface KpiSeparatorProps extends ViewProps {
|
|
596
596
|
className?: string;
|
|
597
597
|
}
|
|
598
598
|
|
|
599
599
|
/** A hairline across the card. */
|
|
600
|
-
function
|
|
600
|
+
function KpiSeparator({ className, ...props }: KpiSeparatorProps) {
|
|
601
601
|
const { separator } = kpiVariants();
|
|
602
602
|
return <View className={separator({ className })} {...props} />;
|
|
603
603
|
}
|
|
@@ -606,12 +606,12 @@ function KpiChartSeparator({ className, ...props }: KpiChartSeparatorProps) {
|
|
|
606
606
|
* Group.
|
|
607
607
|
* ------------------------------------------------------------------ */
|
|
608
608
|
|
|
609
|
-
export type
|
|
609
|
+
export type KpiGroupOrientation = 'horizontal' | 'vertical';
|
|
610
610
|
|
|
611
|
-
export interface
|
|
611
|
+
export interface KpiGroupProps extends ViewProps {
|
|
612
612
|
className?: string;
|
|
613
613
|
/** `horizontal` splits the row between the cards; `vertical` stacks them. */
|
|
614
|
-
orientation?:
|
|
614
|
+
orientation?: KpiGroupOrientation;
|
|
615
615
|
/**
|
|
616
616
|
* Draw a hairline between the cards rather than spacing them apart. Several
|
|
617
617
|
* metrics separated by a rule read as one panel; several spaced apart read
|
|
@@ -628,7 +628,7 @@ export interface KpiChartGroupProps extends ViewProps {
|
|
|
628
628
|
* caller, because "between" is the one thing a list of siblings cannot express
|
|
629
629
|
* — a trailing rule after the last card is the mistake this exists to prevent.
|
|
630
630
|
*/
|
|
631
|
-
const
|
|
631
|
+
const KpiGroup = forwardRef<View, KpiGroupProps>(function KpiGroup(
|
|
632
632
|
{ className, orientation = 'horizontal', separated = true, children, ...props },
|
|
633
633
|
ref
|
|
634
634
|
) {
|
|
@@ -687,33 +687,33 @@ const KpiChartGroup = forwardRef<View, KpiChartGroupProps>(function KpiChartGrou
|
|
|
687
687
|
);
|
|
688
688
|
});
|
|
689
689
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
export const
|
|
706
|
-
Header:
|
|
707
|
-
Icon:
|
|
708
|
-
Title:
|
|
709
|
-
Stat:
|
|
710
|
-
Actions:
|
|
711
|
-
Content:
|
|
712
|
-
Value:
|
|
713
|
-
Trend:
|
|
714
|
-
Chart:
|
|
715
|
-
Progress:
|
|
716
|
-
Footer:
|
|
717
|
-
Separator:
|
|
718
|
-
Group:
|
|
690
|
+
KpiRoot.displayName = 'Kpi';
|
|
691
|
+
KpiHeader.displayName = 'Kpi.Header';
|
|
692
|
+
KpiIcon.displayName = 'Kpi.Icon';
|
|
693
|
+
KpiTitle.displayName = 'Kpi.Title';
|
|
694
|
+
KpiStat.displayName = 'Kpi.Stat';
|
|
695
|
+
KpiActions.displayName = 'Kpi.Actions';
|
|
696
|
+
KpiContent.displayName = 'Kpi.Content';
|
|
697
|
+
KpiValue.displayName = 'Kpi.Value';
|
|
698
|
+
KpiTrend.displayName = 'Kpi.Trend';
|
|
699
|
+
KpiSparkline.displayName = 'Kpi.Chart';
|
|
700
|
+
KpiProgressBar.displayName = 'Kpi.Progress';
|
|
701
|
+
KpiFooter.displayName = 'Kpi.Footer';
|
|
702
|
+
KpiSeparator.displayName = 'Kpi.Separator';
|
|
703
|
+
KpiGroup.displayName = 'Kpi.Group';
|
|
704
|
+
|
|
705
|
+
export const Kpi = Object.assign(KpiRoot, {
|
|
706
|
+
Header: KpiHeader,
|
|
707
|
+
Icon: KpiIcon,
|
|
708
|
+
Title: KpiTitle,
|
|
709
|
+
Stat: KpiStat,
|
|
710
|
+
Actions: KpiActions,
|
|
711
|
+
Content: KpiContent,
|
|
712
|
+
Value: KpiValue,
|
|
713
|
+
Trend: KpiTrend,
|
|
714
|
+
Chart: KpiSparkline,
|
|
715
|
+
Progress: KpiProgressBar,
|
|
716
|
+
Footer: KpiFooter,
|
|
717
|
+
Separator: KpiSeparator,
|
|
718
|
+
Group: KpiGroup,
|
|
719
719
|
});
|
|
@@ -101,6 +101,12 @@ const AnimatedLinearGradient = Animated.createAnimatedComponent(LinearGradient);
|
|
|
101
101
|
/** Room left around the plot for the axis labels and the marker rings. */
|
|
102
102
|
const PADDING = { top: 12, right: 10, bottom: 22, left: 10 };
|
|
103
103
|
|
|
104
|
+
/** Left gutter reserved when a `YAxis` is present, for its labels to sit in. */
|
|
105
|
+
const Y_AXIS_WIDTH = 44;
|
|
106
|
+
|
|
107
|
+
/** Gap between the value labels and the plot they sit beside. */
|
|
108
|
+
const Y_AXIS_GUTTER = 6;
|
|
109
|
+
|
|
104
110
|
/**
|
|
105
111
|
* Which layer a part belongs to. Read off the component itself, so composition
|
|
106
112
|
* stays a flat list of children instead of two nested slots the caller has to
|
|
@@ -125,6 +131,12 @@ interface LineChartContextValue {
|
|
|
125
131
|
/** Tweened y-domain. Read inside worklets to build the paths. */
|
|
126
132
|
domainMin: SharedValue<number>;
|
|
127
133
|
domainMax: SharedValue<number>;
|
|
134
|
+
/**
|
|
135
|
+
* The domain the tween is heading for. The axis labels read this rather than
|
|
136
|
+
* the shared values: a number re-rendered on every frame of a 500ms tween is
|
|
137
|
+
* 30 renders of a label that lands on the same string it started on.
|
|
138
|
+
*/
|
|
139
|
+
extent: [number, number];
|
|
128
140
|
/** Index under the finger, or -1 when nothing is being touched. */
|
|
129
141
|
activeIndex: SharedValue<number>;
|
|
130
142
|
activeIndexJS: number;
|
|
@@ -246,10 +258,28 @@ const LineChartRoot = forwardRef<LineChartHandle, LineChartProps>(function LineC
|
|
|
246
258
|
[]
|
|
247
259
|
);
|
|
248
260
|
|
|
261
|
+
/*
|
|
262
|
+
* Whether a `YAxis` was declared, read off the children before anything is
|
|
263
|
+
* laid out. The gutter its labels sit in has to come off the plot's width
|
|
264
|
+
* *before* the plot exists — an axis given no room is drawn over the series,
|
|
265
|
+
* which loses both the numbers and the shape they were meant to explain.
|
|
266
|
+
*/
|
|
267
|
+
const hasYAxis = useMemo(() => {
|
|
268
|
+
let found = false;
|
|
269
|
+
Children.forEach(children, (child) => {
|
|
270
|
+
if (isValidElement(child) && (child.type as { axis?: string }).axis === 'y') {
|
|
271
|
+
found = true;
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
return found;
|
|
275
|
+
}, [children]);
|
|
276
|
+
|
|
249
277
|
// A sparkline has no axis or grid to leave room for, so the line reaches the
|
|
250
278
|
// edges; a stroke of a couple of pixels still needs a hair of inset not to be
|
|
251
279
|
// clipped at the very top and bottom.
|
|
252
|
-
const pad = compact
|
|
280
|
+
const pad = compact
|
|
281
|
+
? { top: 2, right: 1, bottom: 2, left: 1 }
|
|
282
|
+
: { ...PADDING, left: hasYAxis ? Y_AXIS_WIDTH : PADDING.left };
|
|
253
283
|
const plot: Plot = {
|
|
254
284
|
left: pad.left,
|
|
255
285
|
top: pad.top,
|
|
@@ -360,6 +390,7 @@ const LineChartRoot = forwardRef<LineChartHandle, LineChartProps>(function LineC
|
|
|
360
390
|
unregisterSeries,
|
|
361
391
|
domainMin,
|
|
362
392
|
domainMax,
|
|
393
|
+
extent,
|
|
363
394
|
activeIndex,
|
|
364
395
|
activeIndexJS,
|
|
365
396
|
setActiveIndexJS: handleActiveIndex,
|
|
@@ -381,6 +412,7 @@ const LineChartRoot = forwardRef<LineChartHandle, LineChartProps>(function LineC
|
|
|
381
412
|
unregisterSeries,
|
|
382
413
|
domainMin,
|
|
383
414
|
domainMax,
|
|
415
|
+
extent,
|
|
384
416
|
activeIndex,
|
|
385
417
|
activeIndexJS,
|
|
386
418
|
handleActiveIndex,
|
|
@@ -789,6 +821,69 @@ function LineChartXAxis({ ticks = 4, format, className }: LineChartXAxisProps) {
|
|
|
789
821
|
LineChartXAxis.displayName = 'LineChart.XAxis';
|
|
790
822
|
LineChartXAxis.layer = 'overlay' as Layer;
|
|
791
823
|
|
|
824
|
+
export interface LineChartYAxisProps {
|
|
825
|
+
/** How many intervals to divide the axis into. Yields `ticks + 1` labels. */
|
|
826
|
+
ticks?: number;
|
|
827
|
+
/** Turn a value into its label. Defaults to a compact number. */
|
|
828
|
+
format?: (value: number) => string;
|
|
829
|
+
className?: string;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Value labels down the side, one per grid line.
|
|
834
|
+
*
|
|
835
|
+
* Give it the same `ticks` as the grid, or the numbers name lines that are not
|
|
836
|
+
* there. Four is the default on both for that reason.
|
|
837
|
+
*
|
|
838
|
+
* The labels are the domain the data settles at, not the tweening one — a
|
|
839
|
+
* number that counts up through every intermediate value while the axis
|
|
840
|
+
* animates is noise, and the axis is the part of the chart that is supposed to
|
|
841
|
+
* hold still enough to read.
|
|
842
|
+
*/
|
|
843
|
+
function LineChartYAxis({ ticks = 4, format, className }: LineChartYAxisProps) {
|
|
844
|
+
const { plot, extent } = useChart('LineChart.YAxis');
|
|
845
|
+
|
|
846
|
+
const labels = useMemo(() => {
|
|
847
|
+
const [min, max] = extent;
|
|
848
|
+
if (min === 0 && max === 0) return [];
|
|
849
|
+
return Array.from({ length: ticks + 1 }, (_unused, index) => {
|
|
850
|
+
const value = max - ((max - min) * index) / ticks;
|
|
851
|
+
return { key: index, text: format ? format(value) : compactNumber(value) };
|
|
852
|
+
});
|
|
853
|
+
}, [extent, ticks, format]);
|
|
854
|
+
|
|
855
|
+
return (
|
|
856
|
+
<View
|
|
857
|
+
pointerEvents="none"
|
|
858
|
+
style={{
|
|
859
|
+
position: 'absolute',
|
|
860
|
+
left: 0,
|
|
861
|
+
// Centred on the grid line each label names: the strip is lifted half
|
|
862
|
+
// a label and grown by a whole one, so `justify-between` lands the
|
|
863
|
+
// text's middle on the line rather than its top edge on the first line
|
|
864
|
+
// and its bottom edge on the last.
|
|
865
|
+
top: plot.top - AXIS_LABEL_HEIGHT / 2,
|
|
866
|
+
height: plot.height + AXIS_LABEL_HEIGHT,
|
|
867
|
+
// The gutter the root reserved, less a little breathing room, so the
|
|
868
|
+
// numbers sit clear of the plot instead of against it.
|
|
869
|
+
width: Math.max(plot.left - Y_AXIS_GUTTER, 0),
|
|
870
|
+
}}
|
|
871
|
+
className={cn('items-end justify-between', className)}
|
|
872
|
+
>
|
|
873
|
+
{labels.map((label) => (
|
|
874
|
+
<Text key={label.key} size="xs" muted numberOfLines={1}>
|
|
875
|
+
{label.text}
|
|
876
|
+
</Text>
|
|
877
|
+
))}
|
|
878
|
+
</View>
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
LineChartYAxis.displayName = 'LineChart.YAxis';
|
|
882
|
+
LineChartYAxis.layer = 'overlay' as Layer;
|
|
883
|
+
// Read by the root, which has to leave room for the labels before it lays the
|
|
884
|
+
// plot out.
|
|
885
|
+
LineChartYAxis.axis = 'y' as const;
|
|
886
|
+
|
|
792
887
|
export interface LineChartTooltipProps {
|
|
793
888
|
color?: string;
|
|
794
889
|
/**
|
|
@@ -994,7 +1089,7 @@ const POINT_LABEL_WIDTH = 56;
|
|
|
994
1089
|
/** Line height of an `xs` label, for centring one on the grid line it names. */
|
|
995
1090
|
const AXIS_LABEL_HEIGHT = 16;
|
|
996
1091
|
|
|
997
|
-
/**
|
|
1092
|
+
/** Diameter of the dot that rides a series under the crosshair. */
|
|
998
1093
|
const DOT = 9;
|
|
999
1094
|
|
|
1000
1095
|
/** The dot riding one series under the crosshair. */
|
|
@@ -1177,12 +1272,6 @@ function LineChartHeader({
|
|
|
1177
1272
|
LineChartHeader.displayName = 'LineChart.Header';
|
|
1178
1273
|
LineChartHeader.layer = 'header' as Layer;
|
|
1179
1274
|
|
|
1180
|
-
/* -------------------------------------------------------------------------- */
|
|
1181
|
-
/* Scales and paths */
|
|
1182
|
-
/* -------------------------------------------------------------------------- */
|
|
1183
|
-
|
|
1184
|
-
/** Only reached if the theme CSS was never imported. */
|
|
1185
|
-
|
|
1186
1275
|
export const LineChart = Object.assign(LineChartRoot, {
|
|
1187
1276
|
Header: LineChartHeader,
|
|
1188
1277
|
Grid: LineChartGrid,
|
|
@@ -1190,6 +1279,7 @@ export const LineChart = Object.assign(LineChartRoot, {
|
|
|
1190
1279
|
Line: LineChartLine,
|
|
1191
1280
|
Skeleton: LineChartSkeleton,
|
|
1192
1281
|
XAxis: LineChartXAxis,
|
|
1282
|
+
YAxis: LineChartYAxis,
|
|
1193
1283
|
Tooltip: LineChartTooltip,
|
|
1194
1284
|
Legend: LineChartLegend,
|
|
1195
1285
|
});
|