panelui-native 0.71.0 → 0.73.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/accordion/index.js +10 -4
- package/lib/module/components/accordion/index.js.map +1 -1
- package/lib/module/components/area-chart/index.js +1 -0
- package/lib/module/components/area-chart/index.js.map +1 -1
- package/lib/module/components/breadcrumb/index.js +2 -1
- package/lib/module/components/breadcrumb/index.js.map +1 -1
- package/lib/module/components/button/index.js +7 -5
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/carousel/index.js +1 -1
- package/lib/module/components/carousel/index.js.map +1 -1
- package/lib/module/components/chip/index.js +2 -2
- package/lib/module/components/chip/index.js.map +1 -1
- package/lib/module/components/code-block/index.js.map +1 -1
- package/lib/module/components/collapsible/index.js +5 -1
- package/lib/module/components/collapsible/index.js.map +1 -1
- package/lib/module/components/color-picker/index.js.map +1 -1
- package/lib/module/components/fab/index.js +7 -6
- package/lib/module/components/fab/index.js.map +1 -1
- package/lib/module/components/flow/index.js +1 -1
- package/lib/module/components/flow/index.js.map +1 -1
- package/lib/module/components/frame/index.js +2 -2
- package/lib/module/components/frame/index.js.map +1 -1
- package/lib/module/components/grid-item/index.js +2 -2
- package/lib/module/components/grid-item/index.js.map +1 -1
- package/lib/module/components/heatmap-chart/index.js +1 -0
- package/lib/module/components/heatmap-chart/index.js.map +1 -1
- package/lib/module/components/hex-chart/index.js +1 -0
- package/lib/module/components/hex-chart/index.js.map +1 -1
- package/lib/module/components/input-group/index.js +7 -2
- package/lib/module/components/input-group/index.js.map +1 -1
- package/lib/module/components/item/index.js +2 -2
- package/lib/module/components/item/index.js.map +1 -1
- package/lib/module/components/line-chart/index.js +1 -0
- package/lib/module/components/line-chart/index.js.map +1 -1
- package/lib/module/components/marker/index.js +2 -2
- package/lib/module/components/marker/index.js.map +1 -1
- package/lib/module/components/marquee/index.js +9 -12
- package/lib/module/components/marquee/index.js.map +1 -1
- package/lib/module/components/marquee/marquee-math.js +39 -0
- package/lib/module/components/marquee/marquee-math.js.map +1 -0
- package/lib/module/components/menu/index.js +11 -3
- package/lib/module/components/menu/index.js.map +1 -1
- package/lib/module/components/pagination/index.js +27 -11
- package/lib/module/components/pagination/index.js.map +1 -1
- package/lib/module/components/panelside/index.js +7 -2
- package/lib/module/components/panelside/index.js.map +1 -1
- package/lib/module/components/planner/index.js +26 -5
- package/lib/module/components/planner/index.js.map +1 -1
- package/lib/module/components/plot/index.js +105 -42
- package/lib/module/components/plot/index.js.map +1 -1
- package/lib/module/components/post/index.js +4 -4
- package/lib/module/components/post/index.js.map +1 -1
- package/lib/module/components/search-bar/index.js +331 -0
- package/lib/module/components/search-bar/index.js.map +1 -0
- package/lib/module/components/table/index.js +3 -4
- package/lib/module/components/table/index.js.map +1 -1
- package/lib/module/components/timeline/index.js +288 -39
- package/lib/module/components/timeline/index.js.map +1 -1
- package/lib/module/components/toggle-button/index.js +1 -1
- package/lib/module/components/toggle-button/index.js.map +1 -1
- package/lib/module/components/tree/index.js +13 -8
- package/lib/module/components/tree/index.js.map +1 -1
- package/lib/module/hooks/use-keyboard-avoidance.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/chart.js +50 -7
- package/lib/module/utils/chart.js.map +1 -1
- package/lib/typescript/src/components/accordion/index.d.ts +2 -2
- package/lib/typescript/src/components/accordion/index.d.ts.map +1 -1
- package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -1
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/code-block/index.d.ts +3 -3
- package/lib/typescript/src/components/code-block/index.d.ts.map +1 -1
- package/lib/typescript/src/components/collapsible/index.d.ts +2 -2
- package/lib/typescript/src/components/collapsible/index.d.ts.map +1 -1
- package/lib/typescript/src/components/color-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/fab/index.d.ts +4 -2
- package/lib/typescript/src/components/fab/index.d.ts.map +1 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/hex-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/input-group/index.d.ts.map +1 -1
- package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/marquee/index.d.ts +1 -1
- package/lib/typescript/src/components/marquee/index.d.ts.map +1 -1
- package/lib/typescript/src/components/marquee/marquee-math.d.ts +21 -0
- package/lib/typescript/src/components/marquee/marquee-math.d.ts.map +1 -0
- package/lib/typescript/src/components/menu/index.d.ts +1 -1
- package/lib/typescript/src/components/menu/index.d.ts.map +1 -1
- package/lib/typescript/src/components/pagination/index.d.ts.map +1 -1
- package/lib/typescript/src/components/panelside/index.d.ts +1 -1
- package/lib/typescript/src/components/panelside/index.d.ts.map +1 -1
- package/lib/typescript/src/components/plan/index.d.ts +2 -2
- package/lib/typescript/src/components/planner/index.d.ts.map +1 -1
- package/lib/typescript/src/components/plot/index.d.ts +59 -5
- package/lib/typescript/src/components/plot/index.d.ts.map +1 -1
- package/lib/typescript/src/components/search-bar/index.d.ts +216 -0
- package/lib/typescript/src/components/search-bar/index.d.ts.map +1 -0
- package/lib/typescript/src/components/timeline/index.d.ts +35 -2
- package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tree/index.d.ts +3 -4
- package/lib/typescript/src/components/tree/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts +2 -2
- package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/chart.d.ts +28 -3
- package/lib/typescript/src/utils/chart.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/accordion/index.tsx +17 -7
- package/src/components/area-chart/index.tsx +8 -0
- package/src/components/breadcrumb/index.tsx +2 -1
- package/src/components/button/index.tsx +3 -2
- package/src/components/carousel/index.tsx +1 -1
- package/src/components/chip/index.tsx +2 -2
- package/src/components/code-block/index.tsx +3 -3
- package/src/components/collapsible/index.tsx +13 -4
- package/src/components/color-picker/index.tsx +3 -2
- package/src/components/fab/index.tsx +21 -11
- package/src/components/flow/index.tsx +1 -1
- package/src/components/frame/index.tsx +2 -2
- package/src/components/grid-item/index.tsx +2 -2
- package/src/components/heatmap-chart/index.tsx +7 -1
- package/src/components/hex-chart/index.tsx +7 -1
- package/src/components/input-group/index.tsx +7 -2
- package/src/components/item/index.tsx +2 -2
- package/src/components/line-chart/index.tsx +9 -0
- package/src/components/marker/index.tsx +2 -2
- package/src/components/marquee/index.tsx +14 -12
- package/src/components/marquee/marquee-math.ts +37 -0
- package/src/components/menu/index.tsx +11 -3
- package/src/components/pagination/index.tsx +26 -13
- package/src/components/panelside/index.tsx +7 -2
- package/src/components/plan/index.tsx +2 -2
- package/src/components/planner/index.tsx +20 -5
- package/src/components/plot/index.tsx +183 -39
- package/src/components/post/index.tsx +4 -4
- package/src/components/search-bar/index.tsx +436 -0
- package/src/components/table/index.tsx +2 -2
- package/src/components/timeline/index.tsx +335 -39
- package/src/components/toggle-button/index.tsx +1 -1
- package/src/components/tree/index.tsx +17 -12
- package/src/hooks/use-keyboard-avoidance.ts +7 -2
- package/src/index.ts +2 -0
- package/src/utils/chart.ts +66 -7
|
@@ -1412,6 +1412,7 @@ function PanelsideTrigger({
|
|
|
1412
1412
|
className,
|
|
1413
1413
|
label = 'Open navigation panel',
|
|
1414
1414
|
children,
|
|
1415
|
+
onPress,
|
|
1415
1416
|
...props
|
|
1416
1417
|
}: PanelsideTriggerProps) {
|
|
1417
1418
|
const { toggle, docked } = usePanelsideContext('Panelside.Trigger');
|
|
@@ -1426,6 +1427,7 @@ function PanelsideTrigger({
|
|
|
1426
1427
|
return cloneElement(children, {
|
|
1427
1428
|
onPress: (...args: unknown[]) => {
|
|
1428
1429
|
children.props.onPress?.(...args);
|
|
1430
|
+
onPress?.(...(args as Parameters<NonNullable<PressableProps['onPress']>>));
|
|
1429
1431
|
toggle();
|
|
1430
1432
|
},
|
|
1431
1433
|
});
|
|
@@ -1433,11 +1435,14 @@ function PanelsideTrigger({
|
|
|
1433
1435
|
|
|
1434
1436
|
return (
|
|
1435
1437
|
<AnimatedPressable
|
|
1436
|
-
|
|
1438
|
+
{...props}
|
|
1439
|
+
onPress={(event) => {
|
|
1440
|
+
onPress?.(event);
|
|
1441
|
+
toggle();
|
|
1442
|
+
}}
|
|
1437
1443
|
className={cn('h-10 w-10 items-center justify-center rounded-full', className)}
|
|
1438
1444
|
accessibilityRole="button"
|
|
1439
1445
|
accessibilityLabel={label}
|
|
1440
|
-
{...props}
|
|
1441
1446
|
>
|
|
1442
1447
|
<MenuIcon size={20} color={color} />
|
|
1443
1448
|
</AnimatedPressable>
|
|
@@ -285,7 +285,7 @@ function PlanIcon({ className, children, ...props }: PlanIconProps) {
|
|
|
285
285
|
);
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
export interface PlanTitleProps extends
|
|
288
|
+
export interface PlanTitleProps extends TextProps {
|
|
289
289
|
className?: string;
|
|
290
290
|
children?: ReactNode;
|
|
291
291
|
}
|
|
@@ -306,7 +306,7 @@ function PlanTitle({ className, children, ...props }: PlanTitleProps) {
|
|
|
306
306
|
);
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
export interface PlanDescriptionProps extends
|
|
309
|
+
export interface PlanDescriptionProps extends TextProps {
|
|
310
310
|
className?: string;
|
|
311
311
|
children?: ReactNode;
|
|
312
312
|
}
|
|
@@ -149,7 +149,13 @@ const dayVariants = tv({
|
|
|
149
149
|
* Square-ish because a day here holds a number, a marker and sometimes an
|
|
150
150
|
* icon, and a circle wastes the corners it needs for them.
|
|
151
151
|
*/
|
|
152
|
-
|
|
152
|
+
/*
|
|
153
|
+
* The transparent border is load-bearing: today and the open day both draw
|
|
154
|
+
* one, and a cell that only grows a border when it is picked shifts its
|
|
155
|
+
* contents by a point at the moment you look at it. Every cell reserves the
|
|
156
|
+
* point; the variants below only colour it.
|
|
157
|
+
*/
|
|
158
|
+
cell: 'mx-0.5 h-14 flex-1 rounded-xl border border-transparent px-1.5 pt-1.5',
|
|
153
159
|
number: 'text-xs leading-none',
|
|
154
160
|
marker: 'absolute right-1.5 top-1.5 h-1.5 w-1.5 rounded-full',
|
|
155
161
|
body: 'flex-1 flex-row items-center justify-center gap-0.5 pb-1',
|
|
@@ -161,13 +167,22 @@ const dayVariants = tv({
|
|
|
161
167
|
false: { cell: 'bg-muted/15', number: 'text-muted-foreground/40' },
|
|
162
168
|
},
|
|
163
169
|
/*
|
|
164
|
-
* Today
|
|
165
|
-
*
|
|
170
|
+
* Today rings the tile; the open day fills it. Two rings would be the
|
|
171
|
+
* problem — two channels are not, and the ring is the one that has to
|
|
172
|
+
* survive being read at a glance across forty-two tiles.
|
|
166
173
|
*/
|
|
167
|
-
today: { true: {
|
|
168
|
-
selected: { true: { cell: 'border
|
|
174
|
+
today: { true: { cell: 'border-foreground', number: 'font-semibold' } },
|
|
175
|
+
selected: { true: { cell: 'border-primary/60 bg-primary/10' } },
|
|
169
176
|
disabled: { true: { cell: 'opacity-40' } },
|
|
170
177
|
},
|
|
178
|
+
compoundVariants: [
|
|
179
|
+
/*
|
|
180
|
+
* Today, and open. Declaration order would otherwise hand the border to
|
|
181
|
+
* `selected` and lose the one mark that says which day it actually is —
|
|
182
|
+
* so the ring is restated over the fill.
|
|
183
|
+
*/
|
|
184
|
+
{ today: true, selected: true, class: { cell: 'border-foreground bg-primary/10' } },
|
|
185
|
+
],
|
|
171
186
|
defaultVariants: { inMonth: true },
|
|
172
187
|
});
|
|
173
188
|
|
|
@@ -105,6 +105,7 @@ import {
|
|
|
105
105
|
columnValues,
|
|
106
106
|
compactNumber,
|
|
107
107
|
linePath,
|
|
108
|
+
niceDomain,
|
|
108
109
|
useSeriesColor,
|
|
109
110
|
xOf,
|
|
110
111
|
yOf,
|
|
@@ -281,6 +282,17 @@ export interface PlotProps extends ViewProps {
|
|
|
281
282
|
* arrives.
|
|
282
283
|
*/
|
|
283
284
|
yDomain?: [PlotBound, PlotBound];
|
|
285
|
+
/**
|
|
286
|
+
* Round the derived ends of the y-domain out to whole numbers.
|
|
287
|
+
*
|
|
288
|
+
* Left off, an axis ends a tenth of the span past the largest value, so it
|
|
289
|
+
* gets labelled 34,650 — true, and not a number anybody was looking for. On,
|
|
290
|
+
* the ends move out to a step of 1, 2 or 5 times a power of ten, and the
|
|
291
|
+
* labels become values a reader can measure against.
|
|
292
|
+
*
|
|
293
|
+
* It only ever widens the axis, and a pinned end is left alone.
|
|
294
|
+
*/
|
|
295
|
+
nice?: boolean;
|
|
284
296
|
/**
|
|
285
297
|
* How an index becomes an x. Derived from the marks when left out: a plot
|
|
286
298
|
* with bars in it is banded, and anything else is on points.
|
|
@@ -317,6 +329,7 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
317
329
|
animationDuration = 700,
|
|
318
330
|
domainDuration = 500,
|
|
319
331
|
yDomain,
|
|
332
|
+
nice = false,
|
|
320
333
|
xScale: xScaleProp,
|
|
321
334
|
curve = 'monotone',
|
|
322
335
|
onActiveIndexChange,
|
|
@@ -450,11 +463,20 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
450
463
|
* drifting off the axis is exactly what pinning it was for.
|
|
451
464
|
*/
|
|
452
465
|
const headroom = (max - min) * 0.1;
|
|
466
|
+
/*
|
|
467
|
+
* `nice` replaces the headroom rather than adding to it: rounding out to a
|
|
468
|
+
* whole step is already room, and doing both would leave a tenth of the
|
|
469
|
+
* span of empty axis above a top the reader was told is round.
|
|
470
|
+
*/
|
|
471
|
+
const [niceLow, niceHigh] = nice ? niceDomain(min, max) : [min, max];
|
|
472
|
+
const derivedLow = nice ? niceLow : min - headroom * lowRoom;
|
|
473
|
+
const derivedHigh = nice ? niceHigh : max + headroom * highRoom;
|
|
474
|
+
|
|
453
475
|
return [
|
|
454
|
-
typeof lowPin === 'number' ? lowPin :
|
|
455
|
-
typeof highPin === 'number' ? highPin :
|
|
476
|
+
typeof lowPin === 'number' ? lowPin : derivedLow,
|
|
477
|
+
typeof highPin === 'number' ? highPin : derivedHigh,
|
|
456
478
|
];
|
|
457
|
-
}, [data, yDomain, seriesKeys, hasBars]);
|
|
479
|
+
}, [data, yDomain, seriesKeys, hasBars, nice]);
|
|
458
480
|
|
|
459
481
|
const loading = status === 'loading';
|
|
460
482
|
// Nothing has said what the axis is yet — no mark has registered and no
|
|
@@ -603,11 +625,19 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
603
625
|
* read as the chart arriving, rather than as four separate
|
|
604
626
|
* things animating in at once — which is the thing a composed
|
|
605
627
|
* chart is most at risk of looking like.
|
|
628
|
+
*
|
|
629
|
+
* `width` is set statically as well as animated, and it has
|
|
630
|
+
* to be. Animated props on an element inside `Defs` do not
|
|
631
|
+
* reach the native clip on every platform, and with the width
|
|
632
|
+
* coming only from the animation there is no width at all
|
|
633
|
+
* when they do not — an empty clip, and marks that never
|
|
634
|
+
* appear while the axes draw normally.
|
|
606
635
|
*/}
|
|
607
636
|
<ClipPath id={clipId}>
|
|
608
637
|
<AnimatedRect
|
|
609
638
|
x={plot.left}
|
|
610
639
|
y={0}
|
|
640
|
+
width={plot.width}
|
|
611
641
|
height={size.height}
|
|
612
642
|
animatedProps={clipProps}
|
|
613
643
|
/>
|
|
@@ -759,28 +789,12 @@ function PlotLine({
|
|
|
759
789
|
const max = domainMax.value;
|
|
760
790
|
if (max === min || plot.width <= 0) return { d: '' };
|
|
761
791
|
|
|
762
|
-
if (!banded) return { d: linePath(values, plot, min, max, curve, false) };
|
|
763
|
-
|
|
764
792
|
/*
|
|
765
|
-
*
|
|
766
|
-
*
|
|
767
|
-
*
|
|
768
|
-
* a slice left of the bar it is describing.
|
|
793
|
+
* `banded` moves the points to the middle of their own slice, which is
|
|
794
|
+
* where a line drawn over columns has to sit — spread edge to edge it
|
|
795
|
+
* starts half a slice left of the bar it is describing.
|
|
769
796
|
*/
|
|
770
|
-
|
|
771
|
-
let drawn = 0;
|
|
772
|
-
for (let index = 0; index < values.length; index += 1) {
|
|
773
|
-
const value = values[index];
|
|
774
|
-
if (value === null || value === undefined) {
|
|
775
|
-
drawn = 0;
|
|
776
|
-
continue;
|
|
777
|
-
}
|
|
778
|
-
const x = bandOf(index, values.length, plot);
|
|
779
|
-
const y = yOf(value, plot, min, max);
|
|
780
|
-
d += `${drawn === 0 ? 'M' : 'L'}${x},${y}`;
|
|
781
|
-
drawn += 1;
|
|
782
|
-
}
|
|
783
|
-
return { d };
|
|
797
|
+
return { d: linePath(values, plot, min, max, curve, false, undefined, banded) };
|
|
784
798
|
});
|
|
785
799
|
|
|
786
800
|
if (status === 'loading') return null;
|
|
@@ -820,15 +834,19 @@ function PlotArea({
|
|
|
820
834
|
opacity = 0.18,
|
|
821
835
|
curve = 'monotone',
|
|
822
836
|
}: PlotAreaProps) {
|
|
823
|
-
const { data, plot, domainMin, domainMax, status, clipId } =
|
|
837
|
+
const { data, plot, xScale, domainMin, domainMax, status, clipId } =
|
|
838
|
+
useChart('Plot.Area');
|
|
824
839
|
const fill = useMark(dataKey, color, colorIndex);
|
|
825
840
|
const values = useMemo(() => columnValues(data, dataKey), [data, dataKey]);
|
|
841
|
+
const banded = xScale === 'band';
|
|
826
842
|
|
|
827
843
|
const animatedProps = useAnimatedProps(() => {
|
|
828
844
|
const min = domainMin.value;
|
|
829
845
|
const max = domainMax.value;
|
|
830
846
|
if (max === min || plot.width <= 0) return { d: '' };
|
|
831
|
-
|
|
847
|
+
// Band-aware for the same reason the line is: an area on a plot that also
|
|
848
|
+
// carries columns has to close over the same slices they occupy.
|
|
849
|
+
return { d: areaPath(values, plot, min, max, curve, false, undefined, banded) };
|
|
832
850
|
});
|
|
833
851
|
|
|
834
852
|
if (status === 'loading') return null;
|
|
@@ -848,6 +866,18 @@ export interface PlotBarsProps extends PlotSeriesProps {
|
|
|
848
866
|
/** Rounds the end the bar grows towards, in points. */
|
|
849
867
|
radius?: number;
|
|
850
868
|
opacity?: number;
|
|
869
|
+
/**
|
|
870
|
+
* The value the columns grow from. Zero by default, and zero is nearly always
|
|
871
|
+
* right — a bar is a length, and a length has to start where the quantity
|
|
872
|
+
* does.
|
|
873
|
+
*
|
|
874
|
+
* Set it for the case where the reader is being shown movement rather than
|
|
875
|
+
* size: temperatures against a seasonal average, a score against a pass mark.
|
|
876
|
+
* Columns then run up and down from that line instead of all standing on the
|
|
877
|
+
* floor. It is clamped into the axis, so a baseline the domain does not cover
|
|
878
|
+
* falls back to the nearer edge.
|
|
879
|
+
*/
|
|
880
|
+
baseline?: number;
|
|
851
881
|
}
|
|
852
882
|
|
|
853
883
|
/**
|
|
@@ -866,6 +896,7 @@ function PlotBars({
|
|
|
866
896
|
gap = 0.35,
|
|
867
897
|
radius = 4,
|
|
868
898
|
opacity = 1,
|
|
899
|
+
baseline = 0,
|
|
869
900
|
}: PlotBarsProps) {
|
|
870
901
|
const { data, plot, domainMin, domainMax, status, clipId } = useChart('Plot.Bars');
|
|
871
902
|
const fill = useMark(dataKey, color, colorIndex);
|
|
@@ -881,12 +912,12 @@ function PlotBars({
|
|
|
881
912
|
if (max === min || plot.width <= 0 || total === 0) return { d: '' };
|
|
882
913
|
|
|
883
914
|
/*
|
|
884
|
-
* Bars grow from the baseline
|
|
885
|
-
*
|
|
915
|
+
* Bars grow from the baseline — zero unless the caller moved it — clamped
|
|
916
|
+
* into the domain where the axis does not reach it. A bar drawn from the
|
|
886
917
|
* bottom of a plot whose axis starts at 40 is a length that is not in the
|
|
887
918
|
* data.
|
|
888
919
|
*/
|
|
889
|
-
const base = yOf(Math.min(Math.max(
|
|
920
|
+
const base = yOf(Math.min(Math.max(baseline, min), max), plot, min, max);
|
|
890
921
|
let path = '';
|
|
891
922
|
|
|
892
923
|
for (let index = 0; index < total; index += 1) {
|
|
@@ -1059,11 +1090,36 @@ PlotOverlay.displayName = 'Plot.Overlay';
|
|
|
1059
1090
|
PlotOverlay.layer = 'overlay' as Layer;
|
|
1060
1091
|
|
|
1061
1092
|
export interface PlotRuleProps {
|
|
1062
|
-
/**
|
|
1063
|
-
|
|
1093
|
+
/**
|
|
1094
|
+
* Where to draw it, in the data's own units. Omit it and pass `x` instead for
|
|
1095
|
+
* a rule down the plot rather than across it.
|
|
1096
|
+
*/
|
|
1097
|
+
y?: number;
|
|
1098
|
+
/**
|
|
1099
|
+
* A row to draw a vertical rule at, by index — the release the numbers are
|
|
1100
|
+
* read against, the day a change landed.
|
|
1101
|
+
*
|
|
1102
|
+
* The x axis here carries positions rather than quantities, so this is which
|
|
1103
|
+
* row rather than what value. Exactly one of `y` and `x` is drawn; `y` wins
|
|
1104
|
+
* if both are given.
|
|
1105
|
+
*/
|
|
1106
|
+
x?: number;
|
|
1064
1107
|
/** A name for what the line means. Nothing is drawn without one. */
|
|
1065
1108
|
label?: string;
|
|
1109
|
+
/** Overrides the line *and* its caption, so the two cannot drift apart. */
|
|
1066
1110
|
color?: string;
|
|
1111
|
+
/** Thickness in points. */
|
|
1112
|
+
strokeWidth?: number;
|
|
1113
|
+
/**
|
|
1114
|
+
* Break the line into dashes, for a rule that should read as an annotation
|
|
1115
|
+
* rather than as a series the chart is drawing.
|
|
1116
|
+
*/
|
|
1117
|
+
dashed?: boolean;
|
|
1118
|
+
/** Fades the line and its caption together. */
|
|
1119
|
+
opacity?: number;
|
|
1120
|
+
/** Which end of the rule the caption sits at. */
|
|
1121
|
+
labelPlacement?: 'start' | 'end';
|
|
1122
|
+
labelClassName?: string;
|
|
1067
1123
|
className?: string;
|
|
1068
1124
|
}
|
|
1069
1125
|
|
|
@@ -1071,15 +1127,34 @@ export interface PlotRuleProps {
|
|
|
1071
1127
|
* A reference line across the plot — a target, a limit, an average.
|
|
1072
1128
|
*
|
|
1073
1129
|
* A view rather than an SVG line, so its label is real text and follows the
|
|
1074
|
-
* theme. Its
|
|
1130
|
+
* theme. Its thickness is a border and its position is a transform, so it costs
|
|
1075
1131
|
* no more than the SVG line would while gaining a legible caption.
|
|
1132
|
+
*
|
|
1133
|
+
* It is drawn at full strength in the foreground colour, and it is meant to be.
|
|
1134
|
+
* A reference line is the number the series is being judged against — a target
|
|
1135
|
+
* nobody can read is a target the chart is not actually stating — so what keeps
|
|
1136
|
+
* it from being mistaken for a series is that it is neutral and optionally
|
|
1137
|
+
* dashed, not that it is faint.
|
|
1076
1138
|
*/
|
|
1077
|
-
function PlotRule({
|
|
1078
|
-
|
|
1079
|
-
|
|
1139
|
+
function PlotRule({
|
|
1140
|
+
y,
|
|
1141
|
+
x,
|
|
1142
|
+
label,
|
|
1143
|
+
color,
|
|
1144
|
+
strokeWidth = 1,
|
|
1145
|
+
dashed = false,
|
|
1146
|
+
opacity = 1,
|
|
1147
|
+
labelPlacement = 'end',
|
|
1148
|
+
labelClassName,
|
|
1149
|
+
className,
|
|
1150
|
+
}: PlotRuleProps) {
|
|
1151
|
+
const { plot, domainMin, domainMax, status, data } = useChart('Plot.Rule');
|
|
1152
|
+
const token = useCSSVariable('--color-foreground');
|
|
1080
1153
|
const stroke = color ?? (typeof token === 'string' ? token : '#888888');
|
|
1154
|
+
const vertical = y === undefined && x !== undefined;
|
|
1081
1155
|
|
|
1082
|
-
const
|
|
1156
|
+
const horizontalStyle = useAnimatedStyle(() => {
|
|
1157
|
+
if (vertical || y === undefined) return { opacity: 0 };
|
|
1083
1158
|
const min = domainMin.value;
|
|
1084
1159
|
const max = domainMax.value;
|
|
1085
1160
|
if (max === min) return { opacity: 0 };
|
|
@@ -1087,23 +1162,92 @@ function PlotRule({ y, label, color, className }: PlotRuleProps) {
|
|
|
1087
1162
|
// Off the top or bottom of the plot is out of the chart's range, and a rule
|
|
1088
1163
|
// pinned to the edge would claim a value the axis does not cover.
|
|
1089
1164
|
const inside = at >= plot.top - 0.5 && at <= plot.top + plot.height + 0.5;
|
|
1090
|
-
return { opacity: inside ?
|
|
1165
|
+
return { opacity: inside ? opacity : 0, transform: [{ translateY: at }] };
|
|
1091
1166
|
});
|
|
1092
1167
|
|
|
1093
1168
|
if (status === 'loading') return null;
|
|
1094
1169
|
|
|
1170
|
+
const line = dashed
|
|
1171
|
+
? {
|
|
1172
|
+
borderColor: stroke,
|
|
1173
|
+
borderStyle: 'dashed' as const,
|
|
1174
|
+
borderTopWidth: strokeWidth,
|
|
1175
|
+
}
|
|
1176
|
+
: { backgroundColor: stroke, height: strokeWidth };
|
|
1177
|
+
|
|
1178
|
+
if (vertical) {
|
|
1179
|
+
/*
|
|
1180
|
+
* The x axis carries positions, not quantities, so a vertical rule is
|
|
1181
|
+
* placed from the row's own geometry rather than from the tweening domain —
|
|
1182
|
+
* there is nothing on this axis for it to tween against.
|
|
1183
|
+
*/
|
|
1184
|
+
const total = data.length;
|
|
1185
|
+
const at =
|
|
1186
|
+
total > 0
|
|
1187
|
+
? xOf(Math.max(0, Math.min(total - 1, x!)), total, plot)
|
|
1188
|
+
: plot.left + plot.width / 2;
|
|
1189
|
+
|
|
1190
|
+
return (
|
|
1191
|
+
<View
|
|
1192
|
+
pointerEvents="none"
|
|
1193
|
+
style={{
|
|
1194
|
+
position: 'absolute',
|
|
1195
|
+
left: at,
|
|
1196
|
+
top: plot.top,
|
|
1197
|
+
height: plot.height,
|
|
1198
|
+
opacity,
|
|
1199
|
+
}}
|
|
1200
|
+
className={className}
|
|
1201
|
+
>
|
|
1202
|
+
<View
|
|
1203
|
+
style={
|
|
1204
|
+
dashed
|
|
1205
|
+
? {
|
|
1206
|
+
borderColor: stroke,
|
|
1207
|
+
borderStyle: 'dashed',
|
|
1208
|
+
borderLeftWidth: strokeWidth,
|
|
1209
|
+
flex: 1,
|
|
1210
|
+
}
|
|
1211
|
+
: { backgroundColor: stroke, width: strokeWidth, flex: 1 }
|
|
1212
|
+
}
|
|
1213
|
+
/>
|
|
1214
|
+
{label ? (
|
|
1215
|
+
<Text
|
|
1216
|
+
size="xs"
|
|
1217
|
+
weight="medium"
|
|
1218
|
+
numberOfLines={1}
|
|
1219
|
+
style={color ? { color } : undefined}
|
|
1220
|
+
className={cn('absolute left-1 top-0', labelClassName)}
|
|
1221
|
+
>
|
|
1222
|
+
{label}
|
|
1223
|
+
</Text>
|
|
1224
|
+
) : null}
|
|
1225
|
+
</View>
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1095
1229
|
return (
|
|
1096
1230
|
<Animated.View
|
|
1097
1231
|
pointerEvents="none"
|
|
1098
1232
|
style={[
|
|
1099
1233
|
{ position: 'absolute', left: plot.left, width: plot.width, top: 0 },
|
|
1100
|
-
|
|
1234
|
+
horizontalStyle,
|
|
1101
1235
|
]}
|
|
1102
1236
|
className={className}
|
|
1103
1237
|
>
|
|
1104
|
-
<View style={
|
|
1238
|
+
<View style={line} />
|
|
1105
1239
|
{label ? (
|
|
1106
|
-
<Text
|
|
1240
|
+
<Text
|
|
1241
|
+
size="xs"
|
|
1242
|
+
weight="medium"
|
|
1243
|
+
numberOfLines={1}
|
|
1244
|
+
style={color ? { color } : undefined}
|
|
1245
|
+
className={cn(
|
|
1246
|
+
'pt-0.5',
|
|
1247
|
+
labelPlacement === 'start' ? 'self-start' : 'self-end',
|
|
1248
|
+
labelClassName
|
|
1249
|
+
)}
|
|
1250
|
+
>
|
|
1107
1251
|
{label}
|
|
1108
1252
|
</Text>
|
|
1109
1253
|
) : null}
|
|
@@ -198,7 +198,7 @@ const PostRoot = forwardRef<View, PostProps>(
|
|
|
198
198
|
|
|
199
199
|
if (!onPress) {
|
|
200
200
|
return (
|
|
201
|
-
<View ref={ref} className={root({ className })}
|
|
201
|
+
<View ref={ref} {...props} className={root({ className })}>
|
|
202
202
|
{body}
|
|
203
203
|
</View>
|
|
204
204
|
);
|
|
@@ -210,10 +210,10 @@ const PostRoot = forwardRef<View, PostProps>(
|
|
|
210
210
|
// as one button buries them.
|
|
211
211
|
<AnimatedPressable
|
|
212
212
|
ref={ref}
|
|
213
|
+
{...props}
|
|
213
214
|
accessibilityRole="link"
|
|
214
215
|
onPress={onPress}
|
|
215
216
|
className={root({ className })}
|
|
216
|
-
{...props}
|
|
217
217
|
>
|
|
218
218
|
{body}
|
|
219
219
|
</AnimatedPressable>
|
|
@@ -681,7 +681,7 @@ const PostMedia = forwardRef<View, PostMediaProps>(
|
|
|
681
681
|
|
|
682
682
|
if (!onPress) {
|
|
683
683
|
return (
|
|
684
|
-
<View ref={ref} className={media({ className })} style={{ aspectRatio }}
|
|
684
|
+
<View ref={ref} {...props} className={media({ className })} style={{ aspectRatio }}>
|
|
685
685
|
{picture}
|
|
686
686
|
</View>
|
|
687
687
|
);
|
|
@@ -690,12 +690,12 @@ const PostMedia = forwardRef<View, PostMediaProps>(
|
|
|
690
690
|
return (
|
|
691
691
|
<Pressable
|
|
692
692
|
ref={ref}
|
|
693
|
+
{...props}
|
|
693
694
|
accessibilityRole="imagebutton"
|
|
694
695
|
accessibilityLabel={alt}
|
|
695
696
|
onPress={onPress}
|
|
696
697
|
className={media({ className })}
|
|
697
698
|
style={{ aspectRatio }}
|
|
698
|
-
{...props}
|
|
699
699
|
>
|
|
700
700
|
{picture}
|
|
701
701
|
</Pressable>
|