panelui-native 0.79.1 → 0.81.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/lib/module/components/bottom-sheet/index.js +9 -2
- package/lib/module/components/bottom-sheet/index.js.map +1 -1
- package/lib/module/components/panelside/index.js +527 -19
- package/lib/module/components/panelside/index.js.map +1 -1
- package/lib/module/components/planner/index.js +446 -32
- package/lib/module/components/planner/index.js.map +1 -1
- package/lib/module/components/planner/planner-entries.js +36 -0
- package/lib/module/components/planner/planner-entries.js.map +1 -1
- package/lib/module/components/planner/planner-grid-navigation.js +34 -2
- package/lib/module/components/planner/planner-grid-navigation.js.map +1 -1
- package/lib/module/components/planner/planner-weeks.js +95 -0
- package/lib/module/components/planner/planner-weeks.js.map +1 -0
- package/lib/module/components/qr-code/index.js +84 -15
- package/lib/module/components/qr-code/index.js.map +1 -1
- package/lib/module/components/qr-code/qr-shapes.js +202 -0
- package/lib/module/components/qr-code/qr-shapes.js.map +1 -0
- package/lib/module/components/timeline/index.js +145 -18
- package/lib/module/components/timeline/index.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/date.js +10 -3
- package/lib/module/utils/date.js.map +1 -1
- package/lib/typescript/src/components/bottom-sheet/index.d.ts +16 -0
- package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/src/components/panelside/index.d.ts +242 -5
- package/lib/typescript/src/components/panelside/index.d.ts.map +1 -1
- package/lib/typescript/src/components/planner/index.d.ts +65 -4
- package/lib/typescript/src/components/planner/index.d.ts.map +1 -1
- package/lib/typescript/src/components/planner/planner-entries.d.ts +18 -0
- package/lib/typescript/src/components/planner/planner-entries.d.ts.map +1 -1
- package/lib/typescript/src/components/planner/planner-grid-navigation.d.ts +7 -1
- package/lib/typescript/src/components/planner/planner-grid-navigation.d.ts.map +1 -1
- package/lib/typescript/src/components/planner/planner-weeks.d.ts +50 -0
- package/lib/typescript/src/components/planner/planner-weeks.d.ts.map +1 -0
- package/lib/typescript/src/components/qr-code/index.d.ts +42 -3
- package/lib/typescript/src/components/qr-code/index.d.ts.map +1 -1
- package/lib/typescript/src/components/qr-code/qr-shapes.d.ts +64 -0
- package/lib/typescript/src/components/qr-code/qr-shapes.d.ts.map +1 -0
- package/lib/typescript/src/components/timeline/index.d.ts +33 -0
- package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/date.d.ts +1 -1
- package/lib/typescript/src/utils/date.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/bottom-sheet/index.tsx +9 -2
- package/src/components/panelside/index.tsx +750 -28
- package/src/components/planner/index.tsx +516 -38
- package/src/components/planner/planner-entries.ts +33 -0
- package/src/components/planner/planner-grid-navigation.ts +37 -2
- package/src/components/planner/planner-weeks.ts +90 -0
- package/src/components/qr-code/index.tsx +124 -18
- package/src/components/qr-code/qr-shapes.ts +234 -0
- package/src/components/timeline/index.tsx +165 -14
- package/src/index.ts +5 -0
- package/src/utils/date.ts +13 -3
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
createContext,
|
|
50
50
|
forwardRef,
|
|
51
51
|
isValidElement,
|
|
52
|
+
useCallback,
|
|
52
53
|
useContext,
|
|
53
54
|
useMemo,
|
|
54
55
|
type ReactElement,
|
|
@@ -94,10 +95,27 @@ const AnimatedText = Animated.createAnimatedComponent(Text);
|
|
|
94
95
|
function useColumnDetent(
|
|
95
96
|
scrollX: SharedValue<number>,
|
|
96
97
|
offsets: number[],
|
|
97
|
-
|
|
98
|
+
haptics: boolean,
|
|
99
|
+
onColumnChange?: (index: number) => void
|
|
98
100
|
) {
|
|
99
101
|
const nearest = useSharedValue(0);
|
|
100
102
|
|
|
103
|
+
/*
|
|
104
|
+
* A stable function for `runOnJS`. The prop itself is a fresh closure on
|
|
105
|
+
* every render, and handing that to the UI thread re-serialises it on every
|
|
106
|
+
* frame the reaction is rebuilt.
|
|
107
|
+
*/
|
|
108
|
+
const report = useCallback(
|
|
109
|
+
(index: number) => {
|
|
110
|
+
onColumnChange?.(index);
|
|
111
|
+
},
|
|
112
|
+
[onColumnChange]
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Nothing to compute unless somebody is listening. The reaction runs on
|
|
116
|
+
// every scroll frame, so a timeline that wants neither pays nothing.
|
|
117
|
+
const enabled = haptics || onColumnChange !== undefined;
|
|
118
|
+
|
|
101
119
|
useAnimatedReaction(
|
|
102
120
|
() => {
|
|
103
121
|
if (!enabled || offsets.length === 0) return 0;
|
|
@@ -115,7 +133,8 @@ function useColumnDetent(
|
|
|
115
133
|
(index, previous) => {
|
|
116
134
|
if (!enabled || previous === null || index === previous) return;
|
|
117
135
|
nearest.value = index;
|
|
118
|
-
runOnJS(selectionTick)();
|
|
136
|
+
if (haptics) runOnJS(selectionTick)();
|
|
137
|
+
if (onColumnChange !== undefined) runOnJS(report)(index);
|
|
119
138
|
}
|
|
120
139
|
);
|
|
121
140
|
}
|
|
@@ -184,10 +203,18 @@ const TimelineColumnOffsetContext = createContext<number | null>(null);
|
|
|
184
203
|
*
|
|
185
204
|
* The rail has to land on exactly the same line in every column, and the
|
|
186
205
|
* simplest way to guarantee that is to make the band above it a fixed height
|
|
187
|
-
* rather than to position the rail against measured content.
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
206
|
+
* rather than to position the rail against measured content.
|
|
207
|
+
*
|
|
208
|
+
* So the *column* reserves it, as padding, and the rail is drawn once across
|
|
209
|
+
* the whole track at that offset. Each column's tick is the first thing after
|
|
210
|
+
* the padding and is pulled up by half its own height to straddle the line.
|
|
211
|
+
*
|
|
212
|
+
* `Timeline.Aside` fills that band with a negative margin equal to its own
|
|
213
|
+
* height: it draws inside the reserved strip and takes no flow height of its
|
|
214
|
+
* own. That is what makes it optional. Reserved by the aside instead, a column
|
|
215
|
+
* without one put its tick at the top of the column and 64 points above the
|
|
216
|
+
* rail — which is not a mistake anyone makes twice, but is also not a mistake
|
|
217
|
+
* the component should let anyone make once.
|
|
191
218
|
*/
|
|
192
219
|
const HORIZONTAL_RAIL_TOP = 64;
|
|
193
220
|
const HORIZONTAL_TICK_HEIGHT = 10;
|
|
@@ -239,6 +266,25 @@ const timelineVariants = tv({
|
|
|
239
266
|
stats: 'mt-2 flex-row gap-6 rounded-xl border border-border px-3.5 py-2.5',
|
|
240
267
|
statLabel: 'text-xs text-muted-foreground',
|
|
241
268
|
statValue: 'text-sm font-medium text-foreground',
|
|
269
|
+
/*
|
|
270
|
+
* The masthead over a horizontal rail: what the run of columns is, said
|
|
271
|
+
* once, above them.
|
|
272
|
+
*
|
|
273
|
+
* `gap-0` between the two lines and tight leading on both, because they
|
|
274
|
+
* are one thing rather than two — a label and the name it introduces. Set
|
|
275
|
+
* a step apart with a normal paragraph gap they read as a small heading
|
|
276
|
+
* followed by a bigger unrelated one, which is what the pair looked like
|
|
277
|
+
* before.
|
|
278
|
+
*
|
|
279
|
+
* The label is a size below the title and muted rather than the same size
|
|
280
|
+
* in a lighter weight: at the same size the eye reads the grey one first
|
|
281
|
+
* because it is on the left of the taller one, and the name — the part
|
|
282
|
+
* anybody is actually looking for — arrives second.
|
|
283
|
+
*/
|
|
284
|
+
masthead: 'gap-2',
|
|
285
|
+
mastheadMedia: 'flex-row items-center',
|
|
286
|
+
mastheadLabel: 'text-lg font-medium leading-tight text-muted-foreground',
|
|
287
|
+
mastheadTitle: 'text-2xl font-semibold leading-tight text-foreground',
|
|
242
288
|
},
|
|
243
289
|
variants: {
|
|
244
290
|
variant: {
|
|
@@ -280,10 +326,25 @@ const timelineVariants = tv({
|
|
|
280
326
|
vertical: {},
|
|
281
327
|
horizontal: {
|
|
282
328
|
item: 'w-auto shrink-0 flex-col gap-0 pr-6',
|
|
283
|
-
aside: 'w-auto items-start justify-end gap-
|
|
329
|
+
aside: 'w-auto items-start justify-end gap-0 pb-2 pt-0',
|
|
330
|
+
// Reserved by the column, not by whatever it happens to contain.
|
|
284
331
|
rail: 'w-full items-start',
|
|
285
332
|
body: 'w-auto flex-none pb-0 pt-5',
|
|
286
333
|
title: 'text-sm',
|
|
334
|
+
/*
|
|
335
|
+
* A column's date is its name, not a caption on it. At `text-xs` next
|
|
336
|
+
* to a `text-xs` meta line the two were a two-line grey block and the
|
|
337
|
+
* year — the only thing distinguishing one column from the next — had
|
|
338
|
+
* to be looked for. The meta line stays small above it and the pair
|
|
339
|
+
* closes up, so they read as one label with an eyebrow rather than as
|
|
340
|
+
* two.
|
|
341
|
+
*
|
|
342
|
+
* Colour is left alone: `useColumnInk` animates it per column so the
|
|
343
|
+
* one you have landed on is the one in full ink, and a colour in the
|
|
344
|
+
* class would be overridden by that anyway.
|
|
345
|
+
*/
|
|
346
|
+
date: 'text-sm font-semibold leading-tight',
|
|
347
|
+
meta: 'text-[11px] font-medium leading-tight',
|
|
287
348
|
},
|
|
288
349
|
},
|
|
289
350
|
},
|
|
@@ -416,6 +477,24 @@ export interface TimelineProps extends ViewProps {
|
|
|
416
477
|
* whether this one is worth feeling is the caller's call.
|
|
417
478
|
*/
|
|
418
479
|
haptics?: boolean;
|
|
480
|
+
/**
|
|
481
|
+
* Horizontal only: which column is at the reading edge, reported as it
|
|
482
|
+
* changes.
|
|
483
|
+
*
|
|
484
|
+
* For anything outside the rail that belongs to the column being read — a
|
|
485
|
+
* masthead naming it, a caption, a picture. Without it that block can only
|
|
486
|
+
* show the same thing for the whole run, which makes a swipe through ten
|
|
487
|
+
* columns a swipe under one unchanging heading.
|
|
488
|
+
*
|
|
489
|
+
* The index is the column's position among the rendered items, not its
|
|
490
|
+
* `step`: `step` is the progress value and may be sparse or repeated, so it
|
|
491
|
+
* cannot address a column.
|
|
492
|
+
*
|
|
493
|
+
* It fires on the crossing, not per frame — the reading edge passing from
|
|
494
|
+
* one column to the next — so it is a state update per column rather than
|
|
495
|
+
* per scroll event.
|
|
496
|
+
*/
|
|
497
|
+
onColumnChange?: (index: number) => void;
|
|
419
498
|
children?: ReactNode;
|
|
420
499
|
}
|
|
421
500
|
|
|
@@ -428,6 +507,7 @@ const TimelineRoot = forwardRef<View, TimelineProps>(
|
|
|
428
507
|
orientation = 'vertical',
|
|
429
508
|
snap = true,
|
|
430
509
|
haptics = false,
|
|
510
|
+
onColumnChange,
|
|
431
511
|
children,
|
|
432
512
|
...props
|
|
433
513
|
},
|
|
@@ -484,7 +564,7 @@ const TimelineRoot = forwardRef<View, TimelineProps>(
|
|
|
484
564
|
scrollX.value = event.contentOffset.x;
|
|
485
565
|
});
|
|
486
566
|
|
|
487
|
-
useColumnDetent(scrollX, offsets, horizontal && snap && haptics);
|
|
567
|
+
useColumnDetent(scrollX, offsets, horizontal && snap && haptics, horizontal ? onColumnChange : undefined);
|
|
488
568
|
|
|
489
569
|
if (!horizontal) {
|
|
490
570
|
return (
|
|
@@ -777,7 +857,13 @@ const TimelineItem = forwardRef<View, TimelineItemProps>(
|
|
|
777
857
|
ref={ref}
|
|
778
858
|
className={item({ className })}
|
|
779
859
|
{...props}
|
|
780
|
-
style={[
|
|
860
|
+
style={[
|
|
861
|
+
style,
|
|
862
|
+
// The band above the rail, reserved whether or not this column
|
|
863
|
+
// writes a `Timeline.Aside` into it.
|
|
864
|
+
{ width: columnWidth, paddingTop: HORIZONTAL_RAIL_TOP },
|
|
865
|
+
columnStyle,
|
|
866
|
+
]}
|
|
781
867
|
>
|
|
782
868
|
{virtualized && !last ? (
|
|
783
869
|
<View
|
|
@@ -817,14 +903,15 @@ const TimelineAside = forwardRef<View, ViewProps & { className?: string }>(
|
|
|
817
903
|
ref={ref}
|
|
818
904
|
className={aside({ className })}
|
|
819
905
|
/*
|
|
820
|
-
* Horizontal: this
|
|
821
|
-
*
|
|
822
|
-
*
|
|
823
|
-
*
|
|
906
|
+
* Horizontal: this draws *into* the band the column has already
|
|
907
|
+
* reserved, rather than being the thing that reserves it — hence the
|
|
908
|
+
* negative margin cancelling its own height. A column with a longer
|
|
909
|
+
* label therefore cannot push its own tick below everybody else's, and
|
|
910
|
+
* a column with no aside at all still has its tick on the rail.
|
|
824
911
|
*/
|
|
825
912
|
style={
|
|
826
913
|
orientation === 'horizontal'
|
|
827
|
-
? [{ height: HORIZONTAL_RAIL_TOP }, style]
|
|
914
|
+
? [{ height: HORIZONTAL_RAIL_TOP, marginTop: -HORIZONTAL_RAIL_TOP }, style]
|
|
828
915
|
: style
|
|
829
916
|
}
|
|
830
917
|
{...props}
|
|
@@ -1104,10 +1191,74 @@ const TimelineStat = forwardRef<View, TimelineStatProps>(
|
|
|
1104
1191
|
);
|
|
1105
1192
|
TimelineStat.displayName = 'Timeline.Stat';
|
|
1106
1193
|
|
|
1194
|
+
export interface TimelineMastheadProps extends ViewProps {
|
|
1195
|
+
className?: string;
|
|
1196
|
+
/**
|
|
1197
|
+
* What sits above the two lines — a logo pair, an avatar stack, a single
|
|
1198
|
+
* mark. Anything; the slot only lays it out in a row.
|
|
1199
|
+
*/
|
|
1200
|
+
media?: ReactNode;
|
|
1201
|
+
/** The small line: what kind of thing the run below is. */
|
|
1202
|
+
label?: string;
|
|
1203
|
+
/** The name of it, in the size the eye lands on first. */
|
|
1204
|
+
title?: string;
|
|
1205
|
+
/** Anything else, below the title. */
|
|
1206
|
+
children?: ReactNode;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* The block above a horizontal rail, saying what the run of columns is.
|
|
1211
|
+
*
|
|
1212
|
+
* Media, then a small label, then the name — the order a reader takes them in
|
|
1213
|
+
* anyway, and the reason it is a part rather than two `Text`s at every call
|
|
1214
|
+
* site: the pair has to be typeset as one unit, and a masthead written by hand
|
|
1215
|
+
* is a heading above another heading.
|
|
1216
|
+
*
|
|
1217
|
+
* ```tsx
|
|
1218
|
+
* <Timeline.Masthead
|
|
1219
|
+
* media={
|
|
1220
|
+
* <Avatar.Group size="sm">
|
|
1221
|
+
* <Avatar fallback="A"><AppleIcon size={18} /></Avatar>
|
|
1222
|
+
* </Avatar.Group>
|
|
1223
|
+
* }
|
|
1224
|
+
* label="Built for"
|
|
1225
|
+
* title="iOS and Android"
|
|
1226
|
+
* />
|
|
1227
|
+
*
|
|
1228
|
+
* <Timeline orientation="horizontal">…</Timeline>
|
|
1229
|
+
* ```
|
|
1230
|
+
*
|
|
1231
|
+
* It sits outside the `Timeline`, above it, because it belongs to the whole
|
|
1232
|
+
* run rather than to any column in it — and a column is the only thing a
|
|
1233
|
+
* horizontal `Timeline` lays out.
|
|
1234
|
+
*/
|
|
1235
|
+
const TimelineMasthead = forwardRef<View, TimelineMastheadProps>(
|
|
1236
|
+
({ className, media, label, title, children, ...props }, ref) => {
|
|
1237
|
+
const { masthead, mastheadMedia, mastheadLabel, mastheadTitle } = timelineVariants({
|
|
1238
|
+
orientation: 'horizontal',
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
return (
|
|
1242
|
+
<View ref={ref} className={masthead({ className })} {...props}>
|
|
1243
|
+
{media ? <View className={mastheadMedia()}>{media}</View> : null}
|
|
1244
|
+
{label !== undefined || title !== undefined ? (
|
|
1245
|
+
<View>
|
|
1246
|
+
{label !== undefined ? <Text className={mastheadLabel()}>{label}</Text> : null}
|
|
1247
|
+
{title !== undefined ? <Text className={mastheadTitle()}>{title}</Text> : null}
|
|
1248
|
+
</View>
|
|
1249
|
+
) : null}
|
|
1250
|
+
{children}
|
|
1251
|
+
</View>
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
);
|
|
1255
|
+
TimelineMasthead.displayName = 'Timeline.Masthead';
|
|
1256
|
+
|
|
1107
1257
|
export const Timeline = Object.assign(TimelineRoot, {
|
|
1108
1258
|
List: TimelineList,
|
|
1109
1259
|
Item: TimelineItem,
|
|
1110
1260
|
Aside: TimelineAside,
|
|
1261
|
+
Masthead: TimelineMasthead,
|
|
1111
1262
|
Indicator: TimelineIndicator,
|
|
1112
1263
|
Content: TimelineContent,
|
|
1113
1264
|
Header: TimelineHeader,
|
package/src/index.ts
CHANGED
|
@@ -833,6 +833,8 @@ export {
|
|
|
833
833
|
type PlannerNavProps,
|
|
834
834
|
type PlannerActionProps,
|
|
835
835
|
type PlannerGridProps,
|
|
836
|
+
type PlannerScrollerProps,
|
|
837
|
+
type PlannerVariant,
|
|
836
838
|
type PlannerDayProps,
|
|
837
839
|
type PlannerDayState,
|
|
838
840
|
type PlannerDayRenderer,
|
|
@@ -884,6 +886,9 @@ export {
|
|
|
884
886
|
type QRCodeContentProps,
|
|
885
887
|
type QRCodeSize,
|
|
886
888
|
type QRCodePresentation,
|
|
889
|
+
type QRCodeModuleShape,
|
|
890
|
+
type QRCodeEyeFrameShape,
|
|
891
|
+
type QRCodeEyeBallShape,
|
|
887
892
|
type ErrorCorrectionLevel,
|
|
888
893
|
} from './components/qr-code';
|
|
889
894
|
export {
|
package/src/utils/date.ts
CHANGED
|
@@ -535,13 +535,23 @@ export function monthNames(locale: DateLocale, style: 'long' | 'short' = 'long')
|
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
/** Column headings, rotated so the first one is `weekStartsOn`. */
|
|
538
|
-
export function weekdayNames(
|
|
538
|
+
export function weekdayNames(
|
|
539
|
+
locale: DateLocale,
|
|
540
|
+
weekStartsOn: number,
|
|
541
|
+
/*
|
|
542
|
+
* `narrow` is a single letter in most locales, and several of them repeat —
|
|
543
|
+
* English has T twice and S twice. That is only safe where the column it
|
|
544
|
+
* heads is already unambiguous by position, which is why it is a caller's
|
|
545
|
+
* choice rather than the default.
|
|
546
|
+
*/
|
|
547
|
+
width: 'short' | 'narrow' = 'short'
|
|
548
|
+
): string[] {
|
|
539
549
|
const first = normalizeWeekStart(weekStartsOn);
|
|
540
550
|
// 2021-08-01 was a Sunday, so adding the index lands on each weekday in turn.
|
|
541
551
|
return Array.from({ length: 7 }, (_unused, day) => {
|
|
542
552
|
const index = (day + first) % 7;
|
|
543
|
-
const formatted = formatGregory(new Date(2021, 7, 1 + index), locale, { weekday:
|
|
544
|
-
return formatted ?? WEEKDAYS_EN[index]
|
|
553
|
+
const formatted = formatGregory(new Date(2021, 7, 1 + index), locale, { weekday: width });
|
|
554
|
+
return formatted ?? (width === 'narrow' ? WEEKDAYS_EN[index]![0]! : WEEKDAYS_EN[index]!);
|
|
545
555
|
});
|
|
546
556
|
}
|
|
547
557
|
|