panelui-native 0.98.0 → 0.100.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.
Files changed (41) hide show
  1. package/lib/module/components/animated-badge/index.js +24 -2
  2. package/lib/module/components/animated-badge/index.js.map +1 -1
  3. package/lib/module/components/bump-chart/index.js +1236 -0
  4. package/lib/module/components/bump-chart/index.js.map +1 -0
  5. package/lib/module/components/image-viewer/image-viewer-geometry.js +203 -0
  6. package/lib/module/components/image-viewer/image-viewer-geometry.js.map +1 -0
  7. package/lib/module/components/image-viewer/index.js +1100 -0
  8. package/lib/module/components/image-viewer/index.js.map +1 -0
  9. package/lib/module/components/mirror-area-chart/index.js +1113 -0
  10. package/lib/module/components/mirror-area-chart/index.js.map +1 -0
  11. package/lib/module/index.js +3 -0
  12. package/lib/module/index.js.map +1 -1
  13. package/lib/module/primitives/scrim.js +65 -1
  14. package/lib/module/primitives/scrim.js.map +1 -1
  15. package/lib/module/utils/chart.js +70 -0
  16. package/lib/module/utils/chart.js.map +1 -1
  17. package/lib/typescript/src/components/animated-badge/index.d.ts +5 -0
  18. package/lib/typescript/src/components/animated-badge/index.d.ts.map +1 -1
  19. package/lib/typescript/src/components/bump-chart/index.d.ts +267 -0
  20. package/lib/typescript/src/components/bump-chart/index.d.ts.map +1 -0
  21. package/lib/typescript/src/components/image-viewer/image-viewer-geometry.d.ts +83 -0
  22. package/lib/typescript/src/components/image-viewer/image-viewer-geometry.d.ts.map +1 -0
  23. package/lib/typescript/src/components/image-viewer/index.d.ts +148 -0
  24. package/lib/typescript/src/components/image-viewer/index.d.ts.map +1 -0
  25. package/lib/typescript/src/components/mirror-area-chart/index.d.ts +263 -0
  26. package/lib/typescript/src/components/mirror-area-chart/index.d.ts.map +1 -0
  27. package/lib/typescript/src/index.d.ts +3 -0
  28. package/lib/typescript/src/index.d.ts.map +1 -1
  29. package/lib/typescript/src/primitives/scrim.d.ts +11 -1
  30. package/lib/typescript/src/primitives/scrim.d.ts.map +1 -1
  31. package/lib/typescript/src/utils/chart.d.ts +23 -0
  32. package/lib/typescript/src/utils/chart.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/components/animated-badge/index.tsx +35 -2
  35. package/src/components/bump-chart/index.tsx +1455 -0
  36. package/src/components/image-viewer/image-viewer-geometry.ts +205 -0
  37. package/src/components/image-viewer/index.tsx +1329 -0
  38. package/src/components/mirror-area-chart/index.tsx +1333 -0
  39. package/src/index.ts +42 -0
  40. package/src/primitives/scrim.tsx +96 -1
  41. package/src/utils/chart.ts +70 -0
@@ -0,0 +1,267 @@
1
+ /**
2
+ * BumpChart — how a set of things ranked against each other over time.
3
+ *
4
+ * ```tsx
5
+ * <BumpChart data={standings} xDataKey="week" defaultHighlight="harbour">
6
+ * <BumpChart.Grid />
7
+ * <BumpChart.Line dataKey="harbour" label="Harbour" />
8
+ * <BumpChart.Line dataKey="northside" label="Northside" />
9
+ * <BumpChart.YAxis />
10
+ * <BumpChart.XAxis />
11
+ * <BumpChart.Labels />
12
+ * <BumpChart.Tooltip />
13
+ * </BumpChart>
14
+ * ```
15
+ *
16
+ * ## Positions, not values
17
+ *
18
+ * The y-axis is the rank, so the rows are evenly spaced whatever the gap
19
+ * between the underlying scores. That is the point of the chart: who passed
20
+ * whom, and when. A line chart of the same scores answers how far apart they
21
+ * were, and loses the order wherever two lines run close together.
22
+ *
23
+ * Pass the ranks directly, or pass the scores with `values="score"` and each
24
+ * column is ranked for you, highest first. `bumpRanks` is exported so a header
25
+ * or a table beside the chart can read the same order the chart drew.
26
+ *
27
+ * ## One line at a time
28
+ *
29
+ * Past three or four series, every line in its own colour is a tangle. The
30
+ * chart reads best with one line picked out: `highlight` draws that one in its
31
+ * colour and on top, and the rest in a muted grey. Tapping a name in
32
+ * `BumpChart.Labels` picks that line, and tapping it again clears it.
33
+ */
34
+ import { type ReactNode } from 'react';
35
+ import { type ViewProps } from 'react-native';
36
+ import { type ChartAccessibilityProps } from '../../primitives/chart-accessibility.js';
37
+ import { type SeriesColorIndex } from '../../utils/chart.js';
38
+ type Layer = 'svg' | 'series' | 'overlay' | 'header';
39
+ export type BumpChartStatus = 'loading' | 'ready';
40
+ export type BumpChartDatum = Record<string, string | number | null | undefined>;
41
+ /** Ranks per series key, one entry per row. `null` where a series has none. */
42
+ export type BumpRanks = Record<string, (number | null)[]>;
43
+ /**
44
+ * Each series' rank in every row.
45
+ *
46
+ * With `values: 'rank'` the rows already hold ranks and they are only
47
+ * validated. With `values: 'score'` each row is ranked highest first; a tie
48
+ * keeps the order the keys are passed in, so two equal scores never draw on
49
+ * top of each other.
50
+ */
51
+ export declare function bumpRanks(data: BumpChartDatum[], keys: string[], options?: {
52
+ values?: 'rank' | 'score';
53
+ }): BumpRanks;
54
+ /** The row under the scrub, and which line is picked out. */
55
+ export declare function useBumpChart(): {
56
+ activeIndex: number;
57
+ activePoint: BumpChartDatum | null;
58
+ xDataKey: string;
59
+ ranks: BumpRanks;
60
+ highlight: string | null;
61
+ setHighlight: (key: string | null) => void;
62
+ };
63
+ export interface BumpChartProps extends ViewProps, ChartAccessibilityProps<BumpChartDatum> {
64
+ className?: string;
65
+ /** The rows. Each one is a column: a week, a round, a release. */
66
+ data: BumpChartDatum[];
67
+ /** Key holding the column's label. */
68
+ xDataKey?: string;
69
+ /**
70
+ * What the series columns hold. `rank` (the default) takes them as places,
71
+ * `1` at the top. `score` ranks every row for you, highest first.
72
+ */
73
+ values?: 'rank' | 'score';
74
+ /**
75
+ * The series drawn in its colour and on top, with the rest muted. `null`
76
+ * picks none, and every line keeps its own colour. Controlled — pair it with
77
+ * `onHighlightChange`.
78
+ */
79
+ highlight?: string | null;
80
+ /** The series picked out on first render, when `highlight` is not passed. */
81
+ defaultHighlight?: string | null;
82
+ /** Called when a name in `BumpChart.Labels` is tapped. */
83
+ onHighlightChange?: (key: string | null) => void;
84
+ /**
85
+ * `loading` hides the lines and shows `BumpChart.Skeleton` if there is one.
86
+ * The lines are revealed when it turns `ready`.
87
+ */
88
+ status?: BumpChartStatus;
89
+ /** Width ÷ height. */
90
+ aspectRatio?: number;
91
+ /** Milliseconds for the reveal on mount. */
92
+ animationDuration?: number;
93
+ /** Milliseconds for the lines to move to new places when the data changes. */
94
+ morphDuration?: number;
95
+ /**
96
+ * The column under the scrub as it moves, and `-1`/`null` when it lifts.
97
+ * Fires when the index changes, not per frame.
98
+ */
99
+ onActiveIndexChange?: (index: number, datum: BumpChartDatum | null) => void;
100
+ /** Drop the axis padding, for a chart with no axes. */
101
+ compact?: boolean;
102
+ children?: ReactNode;
103
+ }
104
+ /** Imperative handle: re-run the reveal on demand, for a "replay" control. */
105
+ export interface BumpChartHandle {
106
+ replay: () => void;
107
+ }
108
+ export interface BumpChartGridProps {
109
+ /** A guide down every column. */
110
+ vertical?: boolean;
111
+ /** A guide across every rank. */
112
+ horizontal?: boolean;
113
+ color?: string;
114
+ dashArray?: string;
115
+ opacity?: number;
116
+ }
117
+ /** Guides down each column, so a dot can be read against the label below it. */
118
+ declare function BumpChartGrid({ vertical, horizontal, color, dashArray, opacity, }: BumpChartGridProps): import("react").JSX.Element;
119
+ declare namespace BumpChartGrid {
120
+ var displayName: string;
121
+ var layer: Layer;
122
+ }
123
+ export interface BumpChartLineProps {
124
+ /** Column in the data holding this series' rank, or its score with `values="score"`. */
125
+ dataKey: string;
126
+ /** The name shown by `Labels`, the tooltip and the legend. Defaults to `dataKey`. */
127
+ label?: string;
128
+ /** Explicit colour. Defaults to the `--color-chart-*` token for `colorIndex`. */
129
+ color?: string;
130
+ /** Which of the five chart tokens to take. */
131
+ colorIndex?: SeriesColorIndex;
132
+ /** Thickness of the line. The picked-out line is drawn one point thicker. */
133
+ strokeWidth?: number;
134
+ /** Draw a dot at every column. */
135
+ showDots?: boolean;
136
+ }
137
+ /**
138
+ * One series. It draws nothing itself: the lines are drawn together, so the
139
+ * one picked out can be drawn last and sit on top of every crossing.
140
+ */
141
+ declare function BumpChartLine({ dataKey, label, color, colorIndex, strokeWidth, showDots, }: BumpChartLineProps): null;
142
+ declare namespace BumpChartLine {
143
+ var displayName: string;
144
+ var layer: Layer;
145
+ var line: true;
146
+ }
147
+ export interface BumpChartSkeletonProps {
148
+ /** Milliseconds for one pass of the sweep. */
149
+ duration?: number;
150
+ color?: string;
151
+ /** How many rank rows to stand in for. */
152
+ rows?: number;
153
+ }
154
+ /**
155
+ * The loading state: a thin bar on every rank row with a highlight travelling
156
+ * across them. Flat, so it says where the lines will be and nothing about
157
+ * where they go.
158
+ */
159
+ declare function BumpChartSkeleton({ duration, color, rows: rowsProp }: BumpChartSkeletonProps): import("react").JSX.Element | null;
160
+ declare namespace BumpChartSkeleton {
161
+ var displayName: string;
162
+ var layer: Layer;
163
+ }
164
+ export interface BumpChartXAxisProps {
165
+ /** How many columns to label, spread across the run. */
166
+ ticks?: number;
167
+ format?: (datum: BumpChartDatum, index: number) => string;
168
+ className?: string;
169
+ }
170
+ /** The column labels. Real text, so they follow the theme's font and text scaling. */
171
+ declare function BumpChartXAxis({ ticks, format, className }: BumpChartXAxisProps): import("react").JSX.Element;
172
+ declare namespace BumpChartXAxis {
173
+ var displayName: string;
174
+ var layer: Layer;
175
+ }
176
+ export interface BumpChartYAxisProps {
177
+ /** Format a rank. Defaults to `#1`, `#2`… */
178
+ format?: (rank: number) => string;
179
+ className?: string;
180
+ }
181
+ /** The ranks down the side, one on every row. */
182
+ declare function BumpChartYAxis({ format, className }: BumpChartYAxisProps): import("react").JSX.Element;
183
+ declare namespace BumpChartYAxis {
184
+ var displayName: string;
185
+ var layer: Layer;
186
+ var axis: "y";
187
+ }
188
+ export interface BumpChartLabelsProps {
189
+ /** Room kept to the right of the plot for the names. Longer names are cut short. */
190
+ width?: number;
191
+ /** Let a tap on a name pick that line out, and a second tap clear it. */
192
+ pressable?: boolean;
193
+ className?: string;
194
+ }
195
+ /**
196
+ * Each series' name beside its last point, so the lines can be told apart
197
+ * without a legend to look across to. The names move with their lines when
198
+ * the data changes.
199
+ */
200
+ declare function BumpChartLabels({ pressable, className }: BumpChartLabelsProps): import("react").JSX.Element | null;
201
+ declare namespace BumpChartLabels {
202
+ var displayName: string;
203
+ var layer: Layer;
204
+ var labels: true;
205
+ }
206
+ export interface BumpChartTooltipProps {
207
+ color?: string;
208
+ /** Format a rank in the readout. Defaults to `#1`, `#2`… */
209
+ formatRank?: (rank: number) => string;
210
+ /** Format the readout's heading from the row. Defaults to the value at xDataKey. */
211
+ formatX?: (datum: BumpChartDatum) => string;
212
+ className?: string;
213
+ }
214
+ /**
215
+ * A scrub across the columns, and a readout listing every series in the order
216
+ * it stood at the column under the finger.
217
+ */
218
+ declare function BumpChartTooltip({ color, formatRank, formatX, className }: BumpChartTooltipProps): import("react").JSX.Element | null;
219
+ declare namespace BumpChartTooltip {
220
+ var displayName: string;
221
+ var layer: Layer;
222
+ }
223
+ export interface BumpChartLegendProps extends ViewProps {
224
+ className?: string;
225
+ }
226
+ /** A swatch and a name per series, in declaration order. */
227
+ declare function BumpChartLegend({ className, ...props }: BumpChartLegendProps): import("react").JSX.Element | null;
228
+ declare namespace BumpChartLegend {
229
+ var displayName: string;
230
+ var layer: Layer;
231
+ }
232
+ export interface BumpChartHeaderProps extends ViewProps {
233
+ className?: string;
234
+ /** Small line above the value — what the chart is of. */
235
+ title?: string;
236
+ /** The readout. The largest thing on the card, and the first thing read. */
237
+ value?: string;
238
+ /** One muted line under the value — a period, a comparison, a total. */
239
+ caption?: string;
240
+ /** Draw a swatch and a name per series along the trailing edge. */
241
+ legend?: boolean;
242
+ /** Trailing slot — a control, a badge, a range picker. Wins over `legend`. */
243
+ children?: ReactNode;
244
+ }
245
+ /**
246
+ * The strip above the plot: what the chart is of and what it currently reads.
247
+ * The value is not derived here — pass the formatted string, from
248
+ * `onActiveIndexChange` or `onHighlightChange` if it follows the chart.
249
+ */
250
+ declare function BumpChartHeader({ className, title, value, caption, legend, children, ...props }: BumpChartHeaderProps): import("react").JSX.Element;
251
+ declare namespace BumpChartHeader {
252
+ var displayName: string;
253
+ var layer: Layer;
254
+ }
255
+ export declare const BumpChart: import("react").ForwardRefExoticComponent<BumpChartProps & import("react").RefAttributes<BumpChartHandle>> & {
256
+ Header: typeof BumpChartHeader;
257
+ Grid: typeof BumpChartGrid;
258
+ Line: typeof BumpChartLine;
259
+ Skeleton: typeof BumpChartSkeleton;
260
+ XAxis: typeof BumpChartXAxis;
261
+ YAxis: typeof BumpChartYAxis;
262
+ Labels: typeof BumpChartLabels;
263
+ Tooltip: typeof BumpChartTooltip;
264
+ Legend: typeof BumpChartLegend;
265
+ };
266
+ export {};
267
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/bump-chart/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The arithmetic behind the image viewer, kept apart from the component.
3
+ *
4
+ * Every function here runs on the UI thread inside a gesture or an animated
5
+ * style, so each is a worklet. None of them touches React Native, which is what
6
+ * lets the contract tests run them in Node.
7
+ */
8
+ export interface Size {
9
+ width: number;
10
+ height: number;
11
+ }
12
+ export interface Rect extends Size {
13
+ x: number;
14
+ y: number;
15
+ }
16
+ /** How much a rubber-banded overshoot gives. Lower is stiffer. */
17
+ export declare const RUBBER_BAND = 0.55;
18
+ export declare function clamp(value: number, min: number, max: number): number;
19
+ export declare function lerp(from: number, to: number, t: number): number;
20
+ export declare function lerpRect(from: Rect, to: Rect, t: number): Rect;
21
+ /**
22
+ * The largest rect with the image's proportions that fits inside the box,
23
+ * centred in it. An image with no known size fills the box.
24
+ */
25
+ export declare function fitContain(image: Size, box: Size): Rect;
26
+ /** `fitContain` inside a rect that does not start at the origin. */
27
+ export declare function fitWithin(image: Size, box: Rect): Rect;
28
+ /**
29
+ * The smallest size with the image's proportions that covers the frame.
30
+ *
31
+ * A thumbnail crops its picture to its box; the viewer shows the whole of it.
32
+ * Drawing the image at this size inside a frame that grows from one to the
33
+ * other is what makes the crop open out instead of the picture jumping.
34
+ */
35
+ export declare function coverSize(image: Size, frame: Size): Size;
36
+ /**
37
+ * How far past its limit a value is allowed to travel, given how far it was
38
+ * pushed. The resistance grows with the push and never quite reaches `dimension`.
39
+ */
40
+ export declare function rubberBand(overshoot: number, dimension: number, coefficient?: number): number;
41
+ /** Clamps to `[min, max]`, letting the value stretch past either end with resistance. */
42
+ export declare function rubberBandClamp(value: number, min: number, max: number, dimension: number, coefficient?: number): number;
43
+ /**
44
+ * How far a zoomed image may be moved along one axis before its edge comes away
45
+ * from the screen's. Zero while the scaled image is smaller than the screen,
46
+ * which keeps it centred on that axis.
47
+ */
48
+ export declare function panBound(fitted: number, scale: number, viewport: number): number;
49
+ /**
50
+ * The translation that keeps a point under the fingers while the scale changes.
51
+ *
52
+ * `offset` is where that point sat relative to the image's centre when the
53
+ * pinch began, at `fromScale`; `focal` is the fingers' position now, relative
54
+ * to the screen's centre. The result is measured from the screen's centre too.
55
+ */
56
+ export declare function focalTranslation(focal: number, offset: number, fromScale: number, toScale: number): number;
57
+ /**
58
+ * The page a released pager settles on. Where it would have coasted to decides
59
+ * it rather than where it was let go, so a short flick still turns the page —
60
+ * but never by more than one, which is what a flick means.
61
+ */
62
+ export declare function snapPage(offset: number, velocity: number, current: number, count: number, pageWidth: number): number;
63
+ /** How far a drag has to travel, counting where it was flung, to close the viewer. */
64
+ export declare const DISMISS_DISTANCE = 110;
65
+ /**
66
+ * Whether a released drag closes the viewer.
67
+ *
68
+ * Measured on where the image was heading rather than where it was dropped, so
69
+ * a quick flick closes it from a short distance and a slow drag that is let go
70
+ * of carefully does not. A throw back towards the centre cancels.
71
+ */
72
+ export declare function shouldDismiss(translation: number, velocity: number): boolean;
73
+ /**
74
+ * How much of the backdrop is left while the image is dragged away from the
75
+ * centre. It is mostly gone by the time the drag would close the viewer, so the
76
+ * page coming back into focus is the warning that letting go will close it.
77
+ */
78
+ export declare function dragFade(distance: number, viewport: number): number;
79
+ /** How small the image gets while it is dragged. Never below 0.8 of its size. */
80
+ export declare function dragScale(distance: number, viewport: number): number;
81
+ /** Whether a point lands on a rect that has been scaled about its centre and moved. */
82
+ export declare function hitsRect(px: number, py: number, rect: Rect, scale: number, tx: number, ty: number): boolean;
83
+ //# sourceMappingURL=image-viewer-geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-viewer-geometry.d.ts","sourceRoot":"","sources":["../../../../../src/components/image-viewer/image-viewer-geometry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,IAAK,SAAQ,IAAI;IAChC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,kEAAkE;AAClE,eAAO,MAAM,WAAW,OAAO,CAAC;AAEhC,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAQ9D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI,CASvD;AAED,oEAAoE;AACpE,wBAAgB,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI,CAItD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,IAAI,CAOxD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAK7F;AAED,yFAAyF;AACzF,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAOR;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGhF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,MAAM,CAIR;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,CAMR;AAED,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK5E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAInE;AAED,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIpE;AAED,uFAAuF;AACvF,wBAAgB,QAAQ,CACtB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,OAAO,CAQT"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * ImageViewer — a picture that opens out of the page to fill the screen.
3
+ *
4
+ * Pressing a trigger lifts its image from where it sits, grows it to the whole
5
+ * picture in the middle of the screen, and blurs the page behind it. From there
6
+ * it can be pinched and double-tapped to zoom, swiped sideways to the next
7
+ * image under the same root, and dragged away to put it back.
8
+ *
9
+ * ```tsx
10
+ * <ImageViewer>
11
+ * {photos.map((photo) => (
12
+ * <ImageViewer.Trigger
13
+ * key={photo.id}
14
+ * source={photo.source}
15
+ * alt={photo.alt}
16
+ * radius={12}
17
+ * className="h-40 flex-1 rounded-xl"
18
+ * />
19
+ * ))}
20
+ * </ImageViewer>
21
+ * ```
22
+ *
23
+ * ## The image travels; nothing appears
24
+ *
25
+ * The viewer never fades a second copy of the picture in over the first. The
26
+ * thumbnail is hidden and the same image is drawn in a frame that starts at the
27
+ * thumbnail's exact rectangle and corner radius and ends at the fitted one. The
28
+ * picture inside that frame is always drawn at the size that *covers* it, so the
29
+ * crop a thumbnail shows opens out into the whole image as the frame grows — and
30
+ * closes back into the crop on the way home. That continuity is the point: the
31
+ * picture on screen is the one that was pressed, not a picture of it.
32
+ *
33
+ * The frame animates its bounds rather than a transform, because a transform
34
+ * cannot change a crop. It is one view with one child, so the layout pass it
35
+ * costs per frame is small.
36
+ *
37
+ * ## Every trigger under one root is a page
38
+ *
39
+ * Triggers register themselves with the root as they mount, in render order or
40
+ * by an explicit `index`. Opening one opens the gallery at its page, and the
41
+ * thumbnail hidden underneath follows the page — close on the third image and it
42
+ * flies back into the third thumbnail.
43
+ *
44
+ * ## One gesture surface, decided at the start
45
+ *
46
+ * Pinching, panning a zoomed image, turning the page and dragging to dismiss all
47
+ * begin as a one- or two-finger drag, and which one it is gets decided the
48
+ * moment it starts: two fingers zoom, one finger on a zoomed image pans it, one
49
+ * finger moving sideways across a gallery turns the page, and anything else
50
+ * drags the image away. Deciding once keeps a drag from changing its mind
51
+ * halfway, which is what makes a viewer feel loose.
52
+ */
53
+ import { type ReactNode } from 'react';
54
+ import { type ImageSourcePropType, type PressableProps } from 'react-native';
55
+ export interface ImageViewerProps {
56
+ children?: ReactNode;
57
+ /** Controlled open state. */
58
+ open?: boolean;
59
+ /** Initial open state when uncontrolled. */
60
+ defaultOpen?: boolean;
61
+ onOpenChange?: (open: boolean) => void;
62
+ /** Controlled page — which trigger's image is showing, in page order. */
63
+ index?: number;
64
+ /** Initial page when uncontrolled. */
65
+ defaultIndex?: number;
66
+ onIndexChange?: (index: number) => void;
67
+ /**
68
+ * Blur the page behind the picture. Needs the optional `expo-blur`, and dims
69
+ * instead without it. Reduce Transparency draws an opaque backdrop.
70
+ */
71
+ blur?: boolean;
72
+ /** How far a pinch can zoom in, as a multiple of the fitted size. */
73
+ maxScale?: number;
74
+ /** Where a double tap zooms to, as a multiple of the fitted size. */
75
+ doubleTapScale?: number;
76
+ /** Tick as a drag to dismiss begins. Needs the optional `expo-haptics`. */
77
+ haptics?: boolean;
78
+ /** Draw the close button. Tapping outside the picture and dragging it away still close it. */
79
+ showClose?: boolean;
80
+ /** Read out for the close button. */
81
+ closeLabel?: string;
82
+ /**
83
+ * Space kept between the open picture and the edges of the screen, in points.
84
+ * Measured from the safe area at the top and bottom, and the larger of the
85
+ * two is used for both, so the picture stays centred. `0` fills the screen.
86
+ */
87
+ inset?: number;
88
+ /**
89
+ * Corner radius of the open picture, in points. The corners stay this size on
90
+ * screen while the picture is zoomed or dragged. `0` squares them.
91
+ */
92
+ cornerRadius?: number;
93
+ }
94
+ declare function ImageViewerRoot({ children, open, defaultOpen, onOpenChange, index, defaultIndex, onIndexChange, blur, maxScale, doubleTapScale, haptics, showClose, closeLabel, inset, cornerRadius, }: ImageViewerProps): import("react").JSX.Element;
95
+ export interface ImageViewerTriggerProps extends Omit<PressableProps, 'children' | 'style' | 'disabled'> {
96
+ /**
97
+ * Classes on the pressable. Without `children` the image fills it, so give it
98
+ * a size — `h-48 w-full`, `aspect-square flex-1`.
99
+ */
100
+ className?: string;
101
+ /** The picture. Shown in the page and, until `fullSource` loads, in the viewer. */
102
+ source: ImageSourcePropType;
103
+ /**
104
+ * A larger copy to show once the viewer is open. It loads when the viewer
105
+ * opens and replaces `source` when it arrives, so a feed can carry thumbnails.
106
+ */
107
+ fullSource?: ImageSourcePropType;
108
+ /** Described for a screen reader, on the trigger and in the viewer. */
109
+ alt?: string;
110
+ /** Shown under the picture while it is open. A string is set as text. */
111
+ caption?: ReactNode;
112
+ /** Page order within the root. Render order when left out. */
113
+ index?: number;
114
+ /**
115
+ * The corner radius the picture has in the page, in points, so the flight
116
+ * starts from the same shape. Match it to the `rounded-*` class you gave the
117
+ * trigger — a class cannot be read back.
118
+ */
119
+ radius?: number;
120
+ /**
121
+ * The image's own width and height, when known. Saves looking it up, and is
122
+ * the only way the viewer knows the proportions before a remote image loads.
123
+ */
124
+ width?: number;
125
+ height?: number;
126
+ /** Nothing opens, and the press is not reported. */
127
+ disabled?: boolean;
128
+ /**
129
+ * Draw the picture yourself — a `Post.Media`, a card. It should show `source`
130
+ * cropped to fill, the way the viewer's own flight starts. Left out, the
131
+ * trigger draws `source` to fill its box.
132
+ */
133
+ children?: ReactNode;
134
+ }
135
+ /**
136
+ * The thing pressed to open the viewer.
137
+ *
138
+ * It registers its picture with the root, so the gallery is simply every
139
+ * trigger under that root. While its picture is out on the screen it is drawn
140
+ * transparent — not removed, so the page does not reflow under the blur and the
141
+ * flight home has somewhere to land.
142
+ */
143
+ declare function ImageViewerTrigger({ className, source, fullSource, alt, caption, index, radius, width, height, disabled, children, onPress, onLayout, ...props }: ImageViewerTriggerProps): import("react").JSX.Element;
144
+ export declare const ImageViewer: typeof ImageViewerRoot & {
145
+ Trigger: typeof ImageViewerTrigger;
146
+ };
147
+ export {};
148
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/image-viewer/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,OAAO,EAWL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAOL,KAAK,mBAAmB,EAExB,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAyKtB,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8FAA8F;IAC9F,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,iBAAS,eAAe,CAAC,EACvB,QAAQ,EACR,IAAI,EACJ,WAAmB,EACnB,YAAY,EACZ,KAAK,EACL,YAAgB,EAChB,aAAa,EACb,IAAW,EACX,QAAY,EACZ,cAAoB,EACpB,OAAc,EACd,SAAgB,EAChB,UAAoB,EACpB,KAAqB,EACrB,YAAoC,GACrC,EAAE,gBAAgB,+BAuElB;AAMD,MAAM,WAAW,uBACf,SAAQ,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,CAAC;IAC/D;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,MAAM,EAAE,mBAAmB,CAAC;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAmBD;;;;;;;GAOG;AACH,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,MAAM,EACN,UAAU,EACV,GAAG,EACH,OAAO,EACP,KAAK,EACL,MAAU,EACV,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,uBAAuB,+BAyGzB;AA0vBD,eAAO,MAAM,WAAW;;CAEtB,CAAC"}