Skip to content

Commit f9d83e3

Browse files
authored
fix: Multiple minor improvements (#439)
## Description Before release improvements
1 parent cee5ea0 commit f9d83e3

File tree

24 files changed

+177
-155
lines changed

24 files changed

+177
-155
lines changed

example/app/src/examples/SortableGrid/tests/InputFocusExample.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ export default function InputFocusExample() {
3939

4040
return (
4141
<KeyboardAvoidingView
42-
behavior={IS_IOS ? 'height' : undefined}
43-
keyboardVerticalOffset={100}
42+
behavior={IS_IOS ? 'height' : 'padding'}
43+
keyboardVerticalOffset={IS_IOS ? 100 : 80}
4444
style={styles.flex}>
4545
<ScrollScreen
4646
contentContainerStyle={styles.container}

packages/react-native-sortables/src/components/SortableGrid.tsx

Lines changed: 30 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useMemo } from 'react';
2+
import type { DimensionValue } from 'react-native';
23
import { StyleSheet } from 'react-native';
3-
import type { AnimatedStyle, SharedValue } from 'react-native-reanimated';
4+
import type { SharedValue } from 'react-native-reanimated';
45
import { runOnUI, useAnimatedStyle } from 'react-native-reanimated';
56

67
import { DEFAULT_SORTABLE_GRID_PROPS, IS_WEB } from '../constants';
@@ -11,7 +12,6 @@ import {
1112
GridLayoutProvider,
1213
OrderUpdaterComponent,
1314
SharedProvider,
14-
useGridLayoutContext,
1515
useMeasurementsContext,
1616
useStrategyKey
1717
} from '../providers';
@@ -175,67 +175,47 @@ function SortableGridInner<I>({
175175
rowHeight,
176176
...containerProps
177177
}: SortableGridInnerProps<I>) {
178-
const { mainGroupSize } = useGridLayoutContext();
179178
const { handleContainerMeasurement } = useMeasurementsContext();
180179

181-
const animatedInnerStyle = useAnimatedStyle(() => ({
182-
flexDirection: isVertical ? 'row' : 'column',
183-
height: isVertical
184-
? undefined
185-
: groups * (rowHeight + rowGap.value) - rowGap.value,
186-
...(IS_WEB || mainGroupSize.value
180+
const animatedInnerStyle = useAnimatedStyle(() =>
181+
isVertical
187182
? {
188-
columnGap: columnGap.value,
189-
marginHorizontal: 0,
190-
marginVertical: 0,
183+
columnGap: IS_WEB ? columnGap.value : 0,
184+
flexDirection: 'row',
185+
marginHorizontal: IS_WEB ? 0 : -columnGap.value / 2,
191186
rowGap: rowGap.value
192187
}
193188
: {
194-
marginHorizontal: -columnGap.value / 2,
195-
marginVertical: -rowGap.value / 2
196-
})
197-
}));
198-
199-
let animatedItemStyle: AnimatedStyle;
200-
if (IS_WEB) {
201-
// eslint-disable-next-line react-hooks/rules-of-hooks
202-
animatedItemStyle = useAnimatedStyle(() => ({
203-
[isVertical ? 'width' : 'height']:
204-
`calc((100% - ${columnGap.value * (groups - 1)}px) / ${groups})`
205-
}));
206-
} else {
207-
// eslint-disable-next-line react-hooks/rules-of-hooks
208-
animatedItemStyle = useAnimatedStyle(() => {
209-
if (!mainGroupSize.value) {
210-
return {
211-
flexBasis: `${100 / groups}%`,
212-
paddingHorizontal: columnGap.value / 2,
213-
paddingVertical: rowGap.value / 2
214-
};
215-
}
189+
columnGap: columnGap.value,
190+
flexDirection: 'column',
191+
height: groups * (rowHeight + rowGap.value) - rowGap.value,
192+
rowGap: rowGap.value
193+
}
194+
);
216195

217-
return {
218-
flexBasis: 'auto',
219-
[isVertical ? 'width' : 'height']: mainGroupSize.value,
220-
paddingHorizontal: 0,
221-
paddingVertical: 0
222-
};
223-
});
224-
}
196+
const animatedItemStyle = useAnimatedStyle(() =>
197+
isVertical
198+
? IS_WEB
199+
? {
200+
width:
201+
`calc((100% - ${columnGap.value * (groups - 1)}px) / ${groups})` as DimensionValue
202+
}
203+
: {
204+
flexBasis: `${100 / groups}%`,
205+
paddingHorizontal: columnGap.value / 2
206+
}
207+
: { height: rowHeight }
208+
);
225209

226210
return (
227211
<SortableContainer
228212
{...containerProps}
229213
style={[styles.gridContainer, animatedInnerStyle]}
230214
onLayout={runOnUI((width, height) => {
231-
if (IS_WEB || mainGroupSize.value) {
232-
handleContainerMeasurement(width, height);
233-
} else {
234-
handleContainerMeasurement(
235-
width - columnGap.value,
236-
height - rowGap.value
237-
);
238-
}
215+
handleContainerMeasurement(
216+
width - (isVertical && !IS_WEB ? columnGap.value : 0),
217+
height
218+
);
239219
})}>
240220
{zipArrays(data, itemKeys).map(([item, key], index) => (
241221
<SortableGridItem

packages/react-native-sortables/src/components/shared/DraggableView/DraggableView.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,9 @@ export type DraggableViewProps = PropsWithChildren<{
3939

4040
function DraggableView({
4141
children,
42-
entering: entering_,
43-
exiting,
4442
itemKey: key,
45-
style
43+
style,
44+
...layoutAnimations
4645
}: DraggableViewProps) {
4746
const portalContext = usePortalContext();
4847
const commonValuesContext = useCommonValuesContext();
@@ -51,10 +50,7 @@ function DraggableView({
5150
const { handleDragEnd } = useDragContext();
5251
const { activeItemKey, containerId, customHandle } = commonValuesContext;
5352

54-
const [{ entering, isTeleported }, setState] = useState<{
55-
entering?: LayoutAnimation;
56-
isTeleported: boolean;
57-
}>({ entering: entering_, isTeleported: false });
53+
const [isTeleported, setIsTeleported] = useState(false);
5854
const activationAnimationProgress = useMutableValue(0);
5955
const isActive = useDerivedValue(() => activeItemKey.value === key);
6056
const itemStyles = useItemStyles(key, isActive, activationAnimationProgress);
@@ -71,14 +67,14 @@ function DraggableView({
7167

7268
useEffect(() => {
7369
if (!portalContext) {
74-
setState(prev => (prev.isTeleported ? { isTeleported: false } : prev));
70+
setIsTeleported(false);
7571
return;
7672
}
7773

7874
const teleportedItemId = `${containerId}-${key}`;
7975
const unsubscribe = portalContext?.subscribe?.(
8076
teleportedItemId,
81-
teleported => setState({ isTeleported: teleported })
77+
setIsTeleported
8278
);
8379

8480
return () => {
@@ -116,10 +112,9 @@ function DraggableView({
116112
const renderItemCell = (hidden = false) => {
117113
const innerComponent = (
118114
<ItemCell
115+
{...layoutAnimations}
119116
{...sharedCellProps}
120117
cellStyle={[style, itemStyles]}
121-
entering={entering}
122-
exiting={exiting}
123118
hidden={hidden}>
124119
<LayoutAnimationConfig skipEntering={false} skipExiting={false}>
125120
{children}

packages/react-native-sortables/src/components/shared/DraggableView/ItemCell.tsx

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@ import {
66
type ViewStyle
77
} from 'react-native';
88
import type { SharedValue } from 'react-native-reanimated';
9-
import Animated from 'react-native-reanimated';
9+
import Animated, {
10+
useAnimatedStyle,
11+
useDerivedValue
12+
} from 'react-native-reanimated';
1013

14+
import { HIDDEN_X_OFFSET } from '../../../constants';
1115
import type {
1216
AnimatedStyleProp,
1317
LayoutAnimation
1418
} from '../../../integrations/reanimated';
15-
import { useItemDecorationStyle } from '../../../providers';
19+
import { useCommonValuesContext, useItemDecoration } from '../../../providers';
20+
import type { Dimensions } from '../../../types';
21+
import { resolveDimension } from '../../../utils';
1622
import AnimatedOnLayoutView from '../AnimatedOnLayoutView';
1723

1824
export type ItemCellProps = PropsWithChildren<{
@@ -37,18 +43,40 @@ export default function ItemCell({
3743
itemKey,
3844
onLayout
3945
}: ItemCellProps) {
40-
const decorationStyle = useItemDecorationStyle(
46+
const { controlledItemDimensions, itemHeights, itemWidths } =
47+
useCommonValuesContext();
48+
49+
const decoration = useItemDecoration(
4150
itemKey,
4251
isActive,
4352
activationAnimationProgress
4453
);
4554

55+
const controlledDimensions = useDerivedValue(() => {
56+
const result: Partial<Dimensions> = {};
57+
if (controlledItemDimensions.width) {
58+
result.width = resolveDimension(itemWidths.value, itemKey) ?? undefined;
59+
}
60+
if (controlledItemDimensions.height) {
61+
result.height = resolveDimension(itemHeights.value, itemKey) ?? undefined;
62+
}
63+
return result;
64+
});
65+
66+
const decorationStyle = useAnimatedStyle(() => decoration.value);
67+
const dimensionsStyle = useAnimatedStyle(() => controlledDimensions.value);
68+
4669
return (
4770
<Animated.View style={cellStyle}>
4871
<AnimatedOnLayoutView
4972
entering={entering}
5073
exiting={exiting}
51-
style={[styles.decoration, decorationStyle, hidden && styles.hidden]}
74+
style={[
75+
styles.decoration,
76+
decorationStyle,
77+
dimensionsStyle,
78+
hidden && styles.hidden
79+
]}
5280
onLayout={hidden ? undefined : onLayout}>
5381
{children}
5482
</AnimatedOnLayoutView>
@@ -75,6 +103,11 @@ const styles = StyleSheet.create({
75103
}
76104
}),
77105
hidden: {
78-
display: 'none'
106+
// We change the x position to hide items when teleported (we can't use
107+
// non-layout props like opacity as they are sometimes not updated via
108+
// Reanimated on the Old Architecture; we also can't use any props that
109+
// affect item dimensions, etc., so the safest way is to put the item
110+
// far away from the viewport to hide it)
111+
left: HIDDEN_X_OFFSET
79112
}
80113
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
export const EMPTY_OBJECT = {};
22
export const EMPTY_ARRAY = [];
3+
4+
export const HIDDEN_X_OFFSET = 9999;

packages/react-native-sortables/src/providers/layout/flex/updates/insert/index.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useAnimatedReaction, useDerivedValue } from 'react-native-reanimated';
33

44
import { useMutableValue } from '../../../../../integrations/reanimated';
55
import type {
6+
ControlledSizes,
67
Coordinate,
78
Dimension,
89
FlexLayout,
@@ -53,7 +54,7 @@ const useInsertStrategy: SortStrategyFactory = () => {
5354
let mainDimension: Dimension;
5455
let crossDimension: Dimension;
5556
let mainGap: SharedValue<number>;
56-
let mainItemSizes: SharedValue<number | Record<string, number>>;
57+
let mainItemSizes: SharedValue<ControlledSizes>;
5758

5859
if (isRow) {
5960
mainCoordinate = 'x';
@@ -306,7 +307,7 @@ const useInsertStrategy: SortStrategyFactory = () => {
306307
mainItemSizes.value,
307308
currentItemKey
308309
);
309-
if (!currentItemPosition || itemMainSize === undefined) return;
310+
if (!currentItemPosition || itemMainSize === null) return;
310311

311312
swapItemAfterOffset =
312313
currentItemPosition[mainCoordinate] + (isReverse ? 0 : itemMainSize);
@@ -322,7 +323,7 @@ const useInsertStrategy: SortStrategyFactory = () => {
322323
mainItemSizes.value,
323324
nextItemKey
324325
);
325-
if (!nextItemPosition || nextItemMainSize === undefined) break;
326+
if (!nextItemPosition || nextItemMainSize === null) break;
326327

327328
const currentItemMainAxisPosition = currentItemPosition[mainCoordinate];
328329
const nextItemMainAxisPosition = nextItemPosition[mainCoordinate];
@@ -375,7 +376,7 @@ const useInsertStrategy: SortStrategyFactory = () => {
375376
mainItemSizes.value,
376377
currentItemKey
377378
);
378-
if (!currentItemPosition || currentItemMainSize === undefined) return;
379+
if (!currentItemPosition || currentItemMainSize === null) return;
379380

380381
swapItemBeforeOffset =
381382
currentItemPosition[mainCoordinate] +
@@ -392,7 +393,7 @@ const useInsertStrategy: SortStrategyFactory = () => {
392393
mainItemSizes.value,
393394
prevItemKey
394395
);
395-
if (!prevItemPosition || prevItemMainSize === undefined) return;
396+
if (!prevItemPosition || prevItemMainSize === null) return;
396397

397398
const currentItemMainAxisPosition = currentItemPosition[mainCoordinate];
398399
const prevItemMainAxisPosition = prevItemPosition[mainCoordinate];

packages/react-native-sortables/src/providers/layout/flex/updates/insert/utils.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ControlledSizes } from '../../../../../types';
12
import { reorderInsert, resolveDimension } from '../../../../../utils';
23

34
export type ItemGroupSwapProps = {
@@ -7,7 +8,7 @@ export type ItemGroupSwapProps = {
78
groupSizeLimit: number;
89
indexToKey: Array<string>;
910
keyToIndex: Record<string, number>;
10-
mainItemSizes: number | Record<string, number>;
11+
mainItemSizes: ControlledSizes;
1112
itemGroups: Array<Array<string>>;
1213
mainGap: number;
1314
fixedKeys: Record<string, boolean> | undefined;
@@ -34,7 +35,7 @@ const getGroupItemIndex = (
3435

3536
export const getTotalGroupSize = (
3637
group: Array<string>,
37-
mainItemSizes: number | Record<string, number>,
38+
mainItemSizes: ControlledSizes,
3839
gap: number
3940
) => {
4041
'worklet';

packages/react-native-sortables/src/providers/layout/flex/utils/debug.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import type { DebugRectUpdater } from '../../../../debug';
2-
import type { FlexDirection, FlexLayout, Vector } from '../../../../types';
2+
import type {
3+
ControlledSizes,
4+
FlexDirection,
5+
FlexLayout,
6+
Vector
7+
} from '../../../../types';
38
import { resolveDimension } from '../../../../utils';
49

510
const DEBUG_COLORS = {
@@ -12,8 +17,8 @@ export const updateLayoutDebugRects = (
1217
layout: FlexLayout,
1318
debugCrossAxisGapRects: Array<DebugRectUpdater>,
1419
debugMainAxisGapRects: Array<DebugRectUpdater>,
15-
itemWidths: number | Record<string, number>,
16-
itemHeights: number | Record<string, number>
20+
itemWidths: ControlledSizes,
21+
itemHeights: ControlledSizes
1722
) => {
1823
'worklet';
1924
const isRow = flexDirection.startsWith('row');

0 commit comments

Comments
 (0)