panelui-native 0.49.0 → 0.53.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 +9 -1
- package/lib/module/components/candlestick-chart/index.js +1161 -0
- package/lib/module/components/candlestick-chart/index.js.map +1 -0
- package/lib/module/components/combobox/index.js +73 -7
- package/lib/module/components/combobox/index.js.map +1 -1
- package/lib/module/components/context-menu/index.js +529 -0
- package/lib/module/components/context-menu/index.js.map +1 -0
- package/lib/module/components/menu/index.js +18 -10
- package/lib/module/components/menu/index.js.map +1 -1
- package/lib/module/components/popover/index.js +71 -7
- package/lib/module/components/popover/index.js.map +1 -1
- package/lib/module/components/sortable/index.js +942 -0
- package/lib/module/components/sortable/index.js.map +1 -0
- package/lib/module/components/swipe/index.js +140 -5
- package/lib/module/components/swipe/index.js.map +1 -1
- package/lib/module/components/tabs/index.js +56 -15
- package/lib/module/components/tabs/index.js.map +1 -1
- package/lib/module/components/time-picker/index.js +295 -31
- package/lib/module/components/time-picker/index.js.map +1 -1
- package/lib/module/icons/index.js +29 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +5 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/haptics.js +19 -0
- package/lib/module/utils/haptics.js.map +1 -1
- package/lib/typescript/src/components/candlestick-chart/index.d.ts +278 -0
- package/lib/typescript/src/components/candlestick-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/combobox/index.d.ts.map +1 -1
- package/lib/typescript/src/components/context-menu/index.d.ts +270 -0
- package/lib/typescript/src/components/context-menu/index.d.ts.map +1 -0
- package/lib/typescript/src/components/menu/index.d.ts +20 -20
- package/lib/typescript/src/components/menu/index.d.ts.map +1 -1
- package/lib/typescript/src/components/popover/index.d.ts +51 -1
- package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
- package/lib/typescript/src/components/sortable/index.d.ts +248 -0
- package/lib/typescript/src/components/sortable/index.d.ts.map +1 -0
- package/lib/typescript/src/components/swipe/index.d.ts +35 -0
- package/lib/typescript/src/components/swipe/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tabs/index.d.ts +34 -2
- package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
- package/lib/typescript/src/components/time-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/icons/index.d.ts +8 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/haptics.d.ts +14 -0
- package/lib/typescript/src/utils/haptics.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/candlestick-chart/index.tsx +1360 -0
- package/src/components/combobox/index.tsx +84 -6
- package/src/components/context-menu/index.tsx +658 -0
- package/src/components/menu/index.tsx +17 -10
- package/src/components/popover/index.tsx +94 -6
- package/src/components/sortable/index.tsx +1266 -0
- package/src/components/swipe/index.tsx +165 -3
- package/src/components/tabs/index.tsx +82 -16
- package/src/components/time-picker/index.tsx +330 -35
- package/src/icons/index.tsx +22 -0
- package/src/index.ts +39 -0
- package/src/utils/haptics.ts +21 -0
|
@@ -27,6 +27,11 @@
|
|
|
27
27
|
*
|
|
28
28
|
* Everything that moves runs on the UI thread. A row being dragged does not
|
|
29
29
|
* re-render — the only React work in a swipe is the callback at the end of it.
|
|
30
|
+
*
|
|
31
|
+
* Rows in a `Swipe.Group` close each other, so only one of them is ever open.
|
|
32
|
+
* That is the behaviour of every list on the phone that has this gesture, and
|
|
33
|
+
* a row cannot arrange it alone: it knows when it opens and has no way to hear
|
|
34
|
+
* that a sibling did.
|
|
30
35
|
*/
|
|
31
36
|
import {
|
|
32
37
|
Children,
|
|
@@ -36,6 +41,7 @@ import {
|
|
|
36
41
|
isValidElement,
|
|
37
42
|
useCallback,
|
|
38
43
|
useContext,
|
|
44
|
+
useEffect,
|
|
39
45
|
useImperativeHandle,
|
|
40
46
|
useMemo,
|
|
41
47
|
useRef,
|
|
@@ -96,6 +102,131 @@ interface SwipeContextValue {
|
|
|
96
102
|
|
|
97
103
|
const SwipeContext = createContext<SwipeContextValue | null>(null);
|
|
98
104
|
|
|
105
|
+
/* -------------------------------------------------------------------------- */
|
|
106
|
+
/* Group */
|
|
107
|
+
/* -------------------------------------------------------------------------- */
|
|
108
|
+
|
|
109
|
+
interface SwipeGroupContextValue {
|
|
110
|
+
/** Take a slot in the group. Returns the function that gives it back. */
|
|
111
|
+
join: (row: SwipeGroupMember) => () => void;
|
|
112
|
+
/** Say that this row has opened, so every other one can put itself away. */
|
|
113
|
+
opened: (row: SwipeGroupMember) => void;
|
|
114
|
+
/** Shut every row in the group. */
|
|
115
|
+
closeAll: () => void;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface SwipeGroupMember {
|
|
119
|
+
close: () => void;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const SwipeGroupContext = createContext<SwipeGroupContextValue | null>(null);
|
|
123
|
+
|
|
124
|
+
export interface SwipeGroupProps extends ViewProps {
|
|
125
|
+
className?: string;
|
|
126
|
+
children?: ReactNode;
|
|
127
|
+
/**
|
|
128
|
+
* Close the other rows when one opens. On by default — that is the whole
|
|
129
|
+
* reason to reach for a group. Turning it off keeps the container and the
|
|
130
|
+
* `useSwipeGroup` handle while letting several rows stand open at once.
|
|
131
|
+
*/
|
|
132
|
+
exclusive?: boolean;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Several rows that agree only one of them is open at a time.
|
|
137
|
+
*
|
|
138
|
+
* Every list of swipeable rows wants this, and every list has to be told: a
|
|
139
|
+
* row knows when it opens but has no way to learn that a sibling did, so
|
|
140
|
+
* without something above them a list ends up with three rows standing open
|
|
141
|
+
* and a screen that reads as broken. The mail, message and reminder lists on
|
|
142
|
+
* a phone all behave this way, and a list that does not is the odd one out.
|
|
143
|
+
*
|
|
144
|
+
* ```tsx
|
|
145
|
+
* <Swipe.Group>
|
|
146
|
+
* {rows.map((row) => (
|
|
147
|
+
* <Swipe key={row.id}>
|
|
148
|
+
* <Swipe.End>
|
|
149
|
+
* <Swipe.Action label="Delete" color="destructive" onPress={…} />
|
|
150
|
+
* </Swipe.End>
|
|
151
|
+
* <Item>…</Item>
|
|
152
|
+
* </Swipe>
|
|
153
|
+
* ))}
|
|
154
|
+
* </Swipe.Group>
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* The rows register themselves rather than being found by inspecting children,
|
|
158
|
+
* so a row nested inside anything at all still belongs — wrapped in an
|
|
159
|
+
* `Item.Group`, produced by a `map`, rendered by a component of your own. The
|
|
160
|
+
* alternative, walking the tree for `Swipe` elements, only ever works for the
|
|
161
|
+
* one arrangement it was written against.
|
|
162
|
+
*
|
|
163
|
+
* Nothing here re-renders. The registry is a ref and closing a sibling writes
|
|
164
|
+
* to that row's shared value, so opening a row costs the springs it starts and
|
|
165
|
+
* no React work at all.
|
|
166
|
+
*/
|
|
167
|
+
const SwipeGroup = forwardRef<View, SwipeGroupProps>(
|
|
168
|
+
({ className, children, exclusive = true, ...props }, ref) => {
|
|
169
|
+
/*
|
|
170
|
+
* A ref rather than state: membership changes as rows mount and unmount,
|
|
171
|
+
* and rendering the whole list again because one row arrived would undo
|
|
172
|
+
* the point of a component that never re-renders while it is dragged.
|
|
173
|
+
*/
|
|
174
|
+
const members = useRef(new Set<SwipeGroupMember>());
|
|
175
|
+
|
|
176
|
+
const context = useMemo<SwipeGroupContextValue>(
|
|
177
|
+
() => ({
|
|
178
|
+
join: (row) => {
|
|
179
|
+
members.current.add(row);
|
|
180
|
+
return () => {
|
|
181
|
+
members.current.delete(row);
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
opened: (row) => {
|
|
185
|
+
if (!exclusive) return;
|
|
186
|
+
for (const other of members.current) {
|
|
187
|
+
if (other !== row) other.close();
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
closeAll: () => {
|
|
191
|
+
for (const row of members.current) row.close();
|
|
192
|
+
},
|
|
193
|
+
}),
|
|
194
|
+
[exclusive]
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<SwipeGroupContext.Provider value={context}>
|
|
199
|
+
<View ref={ref} className={className} {...props}>
|
|
200
|
+
{children}
|
|
201
|
+
</View>
|
|
202
|
+
</SwipeGroupContext.Provider>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
SwipeGroup.displayName = 'Swipe.Group';
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Shut every row in the enclosing `Swipe.Group`.
|
|
210
|
+
*
|
|
211
|
+
* The one thing a group knows that a single row cannot: a list that scrolls,
|
|
212
|
+
* navigates away, or has just deleted the row that was open wants all of them
|
|
213
|
+
* put back, and holding a ref to each row to do it by hand is bookkeeping the
|
|
214
|
+
* group is already doing.
|
|
215
|
+
*
|
|
216
|
+
* ```tsx
|
|
217
|
+
* const { closeAll } = useSwipeGroup();
|
|
218
|
+
* <ScrollView onScrollBeginDrag={closeAll}>…</ScrollView>
|
|
219
|
+
* ```
|
|
220
|
+
*
|
|
221
|
+
* Outside a group it is inert rather than an error, so a row that is sometimes
|
|
222
|
+
* grouped and sometimes not does not need two versions of its parent.
|
|
223
|
+
*/
|
|
224
|
+
export function useSwipeGroup(): { closeAll: () => void } {
|
|
225
|
+
const group = useContext(SwipeGroupContext);
|
|
226
|
+
const closeAll = useCallback(() => group?.closeAll(), [group]);
|
|
227
|
+
return { closeAll };
|
|
228
|
+
}
|
|
229
|
+
|
|
99
230
|
/* -------------------------------------------------------------------------- */
|
|
100
231
|
/* Action */
|
|
101
232
|
/* -------------------------------------------------------------------------- */
|
|
@@ -440,6 +571,35 @@ const SwipeRoot = forwardRef<SwipeHandle, SwipeProps>(
|
|
|
440
571
|
reportOpen(null);
|
|
441
572
|
}, [offset, reportOpen]);
|
|
442
573
|
|
|
574
|
+
/*
|
|
575
|
+
* Membership in a `Swipe.Group`, if there is one above this row.
|
|
576
|
+
*
|
|
577
|
+
* The registered member is a stable object rather than the `close`
|
|
578
|
+
* function itself, because `close` changes identity whenever
|
|
579
|
+
* `onOpenChange` does and the group would then be holding a stale entry
|
|
580
|
+
* alongside a live one. The object never changes; what it closes is read
|
|
581
|
+
* off a ref at the moment it is asked.
|
|
582
|
+
*/
|
|
583
|
+
const group = useContext(SwipeGroupContext);
|
|
584
|
+
const closeRef = useRef(close);
|
|
585
|
+
closeRef.current = close;
|
|
586
|
+
const member = useRef<SwipeGroupMember>({ close: () => closeRef.current() }).current;
|
|
587
|
+
|
|
588
|
+
useEffect(() => group?.join(member), [group, member]);
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Told when this row settles open, so the group can put its siblings away.
|
|
592
|
+
* A row closing says nothing — the others are already closed, and telling
|
|
593
|
+
* them so would be a round of work per settle for no change.
|
|
594
|
+
*/
|
|
595
|
+
const announce = useCallback(
|
|
596
|
+
(side: SwipeOpenSide) => {
|
|
597
|
+
reportOpen(side);
|
|
598
|
+
if (side !== null) group?.opened(member);
|
|
599
|
+
},
|
|
600
|
+
[reportOpen, group, member]
|
|
601
|
+
);
|
|
602
|
+
|
|
443
603
|
useImperativeHandle(
|
|
444
604
|
ref,
|
|
445
605
|
() => ({
|
|
@@ -447,11 +607,11 @@ const SwipeRoot = forwardRef<SwipeHandle, SwipeProps>(
|
|
|
447
607
|
const width = side === 'start' ? startWidth.value : endWidth.value;
|
|
448
608
|
if (width === 0) return;
|
|
449
609
|
offset.value = withSpring(side === 'start' ? width : -width, SPRING);
|
|
450
|
-
|
|
610
|
+
announce(side);
|
|
451
611
|
},
|
|
452
612
|
close,
|
|
453
613
|
}),
|
|
454
|
-
[offset, startWidth, endWidth,
|
|
614
|
+
[offset, startWidth, endWidth, announce, close]
|
|
455
615
|
);
|
|
456
616
|
|
|
457
617
|
/**
|
|
@@ -541,7 +701,7 @@ const SwipeRoot = forwardRef<SwipeHandle, SwipeProps>(
|
|
|
541
701
|
|
|
542
702
|
if (projected > limit * OPEN_RATIO) {
|
|
543
703
|
offset.value = withSpring(toStart ? limit : -limit, SPRING);
|
|
544
|
-
runOnJS(
|
|
704
|
+
runOnJS(announce)(side);
|
|
545
705
|
} else {
|
|
546
706
|
offset.value = withSpring(0, SPRING);
|
|
547
707
|
runOnJS(reportOpen)(null);
|
|
@@ -562,6 +722,7 @@ const SwipeRoot = forwardRef<SwipeHandle, SwipeProps>(
|
|
|
562
722
|
fire,
|
|
563
723
|
tick,
|
|
564
724
|
reportOpen,
|
|
725
|
+
announce,
|
|
565
726
|
]
|
|
566
727
|
);
|
|
567
728
|
|
|
@@ -681,6 +842,7 @@ const SwipeRoot = forwardRef<SwipeHandle, SwipeProps>(
|
|
|
681
842
|
SwipeRoot.displayName = 'Swipe';
|
|
682
843
|
|
|
683
844
|
export const Swipe = Object.assign(SwipeRoot, {
|
|
845
|
+
Group: SwipeGroup,
|
|
684
846
|
Start: SwipeStart,
|
|
685
847
|
End: SwipeEnd,
|
|
686
848
|
Action: SwipeAction,
|
|
@@ -140,6 +140,16 @@ const MEASURE_WIDTH = 400;
|
|
|
140
140
|
|
|
141
141
|
export type TabsVariant = 'segmented' | 'underline' | 'pill' | 'expanding';
|
|
142
142
|
|
|
143
|
+
/**
|
|
144
|
+
* How much of an inactive panel survives a switch away from it.
|
|
145
|
+
*
|
|
146
|
+
* `false` unmounts it. `true` keeps it mounted but takes it out of layout, so
|
|
147
|
+
* it costs nothing to have around. `'measured'` keeps it laid out at full size
|
|
148
|
+
* as well — the expensive option, and the only one a child that sizes itself
|
|
149
|
+
* from its parent can be built inside while it is hidden.
|
|
150
|
+
*/
|
|
151
|
+
export type TabsKeepMounted = boolean | 'measured';
|
|
152
|
+
|
|
143
153
|
const tabsVariants = tv({
|
|
144
154
|
slots: {
|
|
145
155
|
list: 'flex-row',
|
|
@@ -246,7 +256,7 @@ interface TabsContextValue {
|
|
|
246
256
|
variant: TabsVariant;
|
|
247
257
|
scrollable: boolean;
|
|
248
258
|
setScrollable: (scrollable: boolean) => void;
|
|
249
|
-
keepMounted:
|
|
259
|
+
keepMounted: TabsKeepMounted;
|
|
250
260
|
swipeable: boolean;
|
|
251
261
|
/**
|
|
252
262
|
* How far the visible panel is displaced from its resting place, in points.
|
|
@@ -292,8 +302,29 @@ export interface TabsProps extends ViewProps {
|
|
|
292
302
|
* Keep inactive panels mounted and hidden instead of unmounting them, so a
|
|
293
303
|
* scroll position or a half-filled form survives a switch away and back.
|
|
294
304
|
* Costs the render of every panel up front.
|
|
305
|
+
*
|
|
306
|
+
* `true` hides a kept panel with `display: none`, which also takes it out of
|
|
307
|
+
* layout: it is mounted, but it has no size. That is what makes it cheap, and
|
|
308
|
+
* it is enough for a panel whose content sizes itself — a column of views, a
|
|
309
|
+
* form, a `ScrollView` of known children.
|
|
310
|
+
*
|
|
311
|
+
* It is *not* enough for a child that decides what to render by measuring the
|
|
312
|
+
* space it has been given. A virtualised list asks its parent how tall it is
|
|
313
|
+
* and fills that many rows; asked inside a panel of zero height it answers
|
|
314
|
+
* zero rows, and the whole first render still lands on the frame the tab
|
|
315
|
+
* becomes visible — the stall this flag looks like it should have avoided.
|
|
316
|
+
*
|
|
317
|
+
* `'measured'` is for that case. A kept panel stays laid out at the full size
|
|
318
|
+
* of the tab set, and is hidden by not being drawn rather than by being
|
|
319
|
+
* removed from layout: a list inside it measures, renders its rows and
|
|
320
|
+
* settles while it is still hidden, so becoming visible costs nothing.
|
|
321
|
+
*
|
|
322
|
+
* The trade is real and is why it is not the default — every kept panel lays
|
|
323
|
+
* out and draws, up front and on every size change, so a five-tab set builds
|
|
324
|
+
* five panels' worth of rows to show one. Reach for it when a panel is slow
|
|
325
|
+
* to appear and its content is virtualised; leave it at `true` otherwise.
|
|
295
326
|
*/
|
|
296
|
-
keepMounted?:
|
|
327
|
+
keepMounted?: TabsKeepMounted;
|
|
297
328
|
/**
|
|
298
329
|
* Move between tabs by dragging sideways on the panel, as well as by
|
|
299
330
|
* pressing the triggers.
|
|
@@ -310,7 +341,9 @@ export interface TabsProps extends ViewProps {
|
|
|
310
341
|
* something is moving*, so a panel that is slow to build stops being a pause
|
|
311
342
|
* before it appears and starts being a stutter in the movement. If a swipe
|
|
312
343
|
* feels heavier than a press on the same tab set, the panel is expensive to
|
|
313
|
-
* mount
|
|
344
|
+
* mount — and the answer is whichever `keepMounted` actually keeps its
|
|
345
|
+
* content built, which for a virtualised list is `'measured'` rather than
|
|
346
|
+
* `true`. Turning this off hides the cost rather than removing it.
|
|
314
347
|
*/
|
|
315
348
|
swipeable?: boolean;
|
|
316
349
|
children: ReactNode;
|
|
@@ -928,11 +961,41 @@ function TabsContent({ className, value, children, style, ...props }: TabsConten
|
|
|
928
961
|
|
|
929
962
|
if (!active && !context.keepMounted) return null;
|
|
930
963
|
|
|
964
|
+
/*
|
|
965
|
+
* Under `keepMounted='measured'` a hidden panel keeps its size instead of
|
|
966
|
+
* losing it. It is taken out of the flow and stretched over the tab set, so
|
|
967
|
+
* it is laid out at the same size as the visible panel without contributing
|
|
968
|
+
* its height to the parent, and it is hidden by being transparent rather
|
|
969
|
+
* than by `display: none`.
|
|
970
|
+
*
|
|
971
|
+
* That distinction is the whole point: `display: none` lays a panel out at
|
|
972
|
+
* zero size, and a child that sizes itself from its parent — a virtualised
|
|
973
|
+
* list deciding how many rows to render — renders nothing at all inside one.
|
|
974
|
+
* Given a real size while still hidden, it builds its rows now rather than on
|
|
975
|
+
* the frame the tab is switched to.
|
|
976
|
+
*
|
|
977
|
+
* `opacity: 0` is set once and never animated, so it costs a composite and
|
|
978
|
+
* not a per-frame redraw; the negative `zIndex` keeps it behind the visible
|
|
979
|
+
* panel rather than over it, whatever order the panels are written in.
|
|
980
|
+
*/
|
|
981
|
+
const measured = context.keepMounted === 'measured';
|
|
982
|
+
const hiddenStyle = measured
|
|
983
|
+
? ({
|
|
984
|
+
position: 'absolute',
|
|
985
|
+
top: 0,
|
|
986
|
+
left: 0,
|
|
987
|
+
right: 0,
|
|
988
|
+
bottom: 0,
|
|
989
|
+
opacity: 0,
|
|
990
|
+
zIndex: -1,
|
|
991
|
+
} as const)
|
|
992
|
+
: ({ display: 'none' } as const);
|
|
993
|
+
|
|
931
994
|
/*
|
|
932
995
|
* Hidden rather than unmounted under `keepMounted`, and hidden thoroughly:
|
|
933
|
-
*
|
|
934
|
-
*
|
|
935
|
-
*
|
|
996
|
+
* it is not drawn, it takes no touches, and the accessibility props take it
|
|
997
|
+
* out of the reading order too. A screen reader walking through three panels
|
|
998
|
+
* of a tab set it cannot see is worse than no tabs at all.
|
|
936
999
|
*/
|
|
937
1000
|
const panel = (
|
|
938
1001
|
<Animated.View
|
|
@@ -953,19 +1016,21 @@ function TabsContent({ className, value, children, style, ...props }: TabsConten
|
|
|
953
1016
|
// window would be most of the way across it.
|
|
954
1017
|
//
|
|
955
1018
|
// Zero is ignored. Every panel reports into the same value, and a
|
|
956
|
-
// kept
|
|
957
|
-
//
|
|
1019
|
+
// panel kept with `display: none` measures as nothing — letting it say
|
|
1020
|
+
// so would leave the visible panel with no width. A `'measured'` one
|
|
1021
|
+
// is stretched over the tab set and reports the same width as the
|
|
1022
|
+
// visible panel, so it is agreeing rather than overwriting.
|
|
958
1023
|
const measured = event.nativeEvent.layout.width;
|
|
959
1024
|
if (measured > 0) width.value = measured;
|
|
960
1025
|
}}
|
|
961
1026
|
/*
|
|
962
1027
|
* The follow style goes on the panel that is moving, and only that one.
|
|
963
|
-
* Under `keepMounted` every other panel is
|
|
964
|
-
*
|
|
965
|
-
*
|
|
966
|
-
*
|
|
1028
|
+
* Under `keepMounted` every other panel is hidden and cannot be seen to
|
|
1029
|
+
* move — but an animated style still subscribes them all to the offset,
|
|
1030
|
+
* so a five-tab set ran five mappers per frame to reposition four views
|
|
1031
|
+
* nobody was looking at.
|
|
967
1032
|
*/
|
|
968
|
-
style={[!active &&
|
|
1033
|
+
style={[!active && hiddenStyle, swipeable && active && followStyle, style]}
|
|
969
1034
|
pointerEvents={active ? 'auto' : 'none'}
|
|
970
1035
|
accessibilityElementsHidden={!active}
|
|
971
1036
|
importantForAccessibility={active ? 'auto' : 'no-hide-descendants'}
|
|
@@ -976,9 +1041,10 @@ function TabsContent({ className, value, children, style, ...props }: TabsConten
|
|
|
976
1041
|
</Animated.View>
|
|
977
1042
|
);
|
|
978
1043
|
|
|
979
|
-
// Only the visible panel carries the gesture. A kept-mounted panel
|
|
980
|
-
//
|
|
981
|
-
// each of them would put several competing
|
|
1044
|
+
// Only the visible panel carries the gesture. A kept-mounted panel takes no
|
|
1045
|
+
// touches anyway — `pointerEvents` is off on it whichever way it is hidden —
|
|
1046
|
+
// but attaching a detector to each of them would put several competing
|
|
1047
|
+
// recognisers in the same tree.
|
|
982
1048
|
if (!swipeable || !active) return panel;
|
|
983
1049
|
|
|
984
1050
|
return <GestureDetector gesture={pan}>{panel}</GestureDetector>;
|