Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = deepmerge(base, {
"@typescript-eslint/ban-ts-ignore": "off",
"no-unused-expressions": "off",
"@typescript-eslint/no-unused-expressions": "error",
"no-undef": "off"
"no-undef": "off",
"react/react-in-jsx-scope": "off"
}
});
16 changes: 14 additions & 2 deletions .github/scripts/determine-widget-scope.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,22 @@ all_widgets=$(echo "$widget_dirs" | jq -R -s -c 'split("\n") | map(select(length
all_widgets_and_js=$(echo "$widget_dirs" | jq -R -s -c 'split("\n") | map(select(length > 0)) + ["mobile-resources-native", "nanoflow-actions-native"]')

if [ "$event_name" == "pull_request" ]; then
if git cat-file -e "$before_commit" 2>/dev/null; then
base_sha=""
head_sha=""

# For pull_request events, github.event.before is usually empty.
# Prefer reading the base/head SHAs from the GitHub event payload.
if [ -n "${GITHUB_EVENT_PATH:-}" ] && [ -f "${GITHUB_EVENT_PATH:-}" ]; then
base_sha=$(jq -r '.pull_request.base.sha // empty' "$GITHUB_EVENT_PATH" 2>/dev/null || true)
head_sha=$(jq -r '.pull_request.head.sha // empty' "$GITHUB_EVENT_PATH" 2>/dev/null || true)
fi

if [ -n "$base_sha" ] && [ -n "$head_sha" ] && git cat-file -e "$base_sha" 2>/dev/null && git cat-file -e "$head_sha" 2>/dev/null; then
changed_files=$(git diff --name-only "$base_sha" "$head_sha")
elif [ -n "$before_commit" ] && git cat-file -e "$before_commit" 2>/dev/null; then
changed_files=$(git diff --name-only "$before_commit" "$current_commit")
else
echo "Previous commit not found, using HEAD~1 as fallback"
echo "Base/head SHAs not available locally; falling back to HEAD~1 diff"
changed_files=$(git diff --name-only HEAD~1 "$current_commit")
fi

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/NativePipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 2 # Fetch the latest two commits and its parent commit
fetch-depth: 0 # Required to diff PR base/head SHAs reliably
- name: "Determine scope"
id: scope
run: |
Expand Down
4 changes: 4 additions & 0 deletions packages/pluggableWidgets/bottom-sheet-native/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

- Updated react-native-reanimated to v3.17.5. This addresses compatibility issues with React Native 0.78 and later versions.

### Fixed

- Fixed React Native Reanimated worklet function errors by properly memoizing snap points.

## [5.0.2] - 2025-10-2

- Updated react-native-reanimated to v3.16.7
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ReactElement, ReactNode, useEffect, useRef, useState } from "react";
import { ReactElement, ReactNode, useEffect, useRef, useState, useMemo } from "react";
import { InteractionManager, LayoutChangeEvent, Modal, Pressable, SafeAreaView, StyleSheet, View } from "react-native";
import BottomSheet, { BottomSheetBackdrop, BottomSheetBackdropProps, BottomSheetView } from "@gorhom/bottom-sheet";
import { EditableValue, ValueStatus } from "mendix";
Expand All @@ -17,6 +17,13 @@ export const CustomModalSheet = (props: CustomModalSheetProps): ReactElement =>
const [currentStatus, setCurrentStatus] = useState(false);
const isAvailable = props.triggerAttribute && props.triggerAttribute.status === ValueStatus.Available;

const snapPoints = useMemo(() => {
if (height === 0) {
return ["90%"];
}
return [height - Number(defaultPaddings.paddingBottom)];
}, [height]);

const onLayoutFullscreenHandler = (event: LayoutChangeEvent): void => {
const layoutHeight = event.nativeEvent.layout.height;
if (layoutHeight > 0 && layoutHeight !== height) {
Expand All @@ -34,7 +41,7 @@ export const CustomModalSheet = (props: CustomModalSheetProps): ReactElement =>
bottomSheetRef.current?.close();
setCurrentStatus(false);
}
}, [props.triggerAttribute, currentStatus]);
}, [props.triggerAttribute, currentStatus, isAvailable]);

if (height === 0) {
return (
Expand All @@ -44,14 +51,12 @@ export const CustomModalSheet = (props: CustomModalSheetProps): ReactElement =>
);
}

const snapPoints = [height - Number(defaultPaddings.paddingBottom)];

const isOpen =
props.triggerAttribute &&
props.triggerAttribute.status === ValueStatus.Available &&
props.triggerAttribute.value;

const renderBackdrop = (backdropProps: BottomSheetBackdropProps) => (
const renderBackdrop = (backdropProps: BottomSheetBackdropProps): ReactElement => (
<Pressable style={{ flex: 1 }} onPress={close}>
<BottomSheetBackdrop
{...backdropProps}
Expand All @@ -63,7 +68,7 @@ export const CustomModalSheet = (props: CustomModalSheetProps): ReactElement =>
</Pressable>
);

const handleSheetChanges = (index: number) => {
const handleSheetChanges = (index: number): void => {
if (!isAvailable) {
return;
}
Expand All @@ -79,7 +84,7 @@ export const CustomModalSheet = (props: CustomModalSheetProps): ReactElement =>
}
};

const close = () => {
const close = (): void => {
bottomSheetRef.current?.close();
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ReactNode, ReactElement, useCallback, useState, useRef, Children } from "react";
import { ReactNode, ReactElement, useCallback, useState, useRef, Children, useMemo } from "react";
import { Dimensions, LayoutChangeEvent, SafeAreaView, StyleSheet, View } from "react-native";
import BottomSheet, { BottomSheetView } from "@gorhom/bottom-sheet";
import { BottomSheetStyle } from "../ui/Styles";
Expand Down Expand Up @@ -26,23 +26,29 @@ export const ExpandingDrawer = (props: ExpandingDrawerProps): ReactElement => {
const isSmallContentValid = Children.count(props.smallContent) > 0;
const isLargeContentValid = Children.count(props.largeContent) > 0;

const onLayoutHandlerHeader = (event: LayoutChangeEvent): void => {
const height = event.nativeEvent.layout.height;
if (height > 0 && height <= maxHeight) {
setHeightHeader(height);
}
};
const onLayoutHandlerHeader = useCallback(
(event: LayoutChangeEvent): void => {
const height = event.nativeEvent.layout.height;
if (height > 0 && height <= maxHeight) {
setHeightHeader(height);
}
},
[maxHeight]
);

const onLayoutHandlerContent = (event: LayoutChangeEvent): void => {
const height = event.nativeEvent.layout.height;
if (height > 0) {
if (height <= maxHeight) {
setHeightContent(height);
} else if (!props.fullscreenContent) {
setHeightContent(maxHeight);
const onLayoutHandlerContent = useCallback(
(event: LayoutChangeEvent): void => {
const height = event.nativeEvent.layout.height;
if (height > 0) {
if (height <= maxHeight) {
setHeightContent(height);
} else if (!props.fullscreenContent) {
setHeightContent(maxHeight);
}
}
}
};
},
[maxHeight, props.fullscreenContent]
);

const onLayoutFullscreenHandler = (event: LayoutChangeEvent): void => {
const height = event.nativeEvent.layout.height;
Expand All @@ -54,6 +60,21 @@ export const ExpandingDrawer = (props: ExpandingDrawerProps): ReactElement => {
const containerStyle =
props.fullscreenContent && isOpen ? props.styles.containerWhenExpandedFullscreen : props.styles.container;

const snapPoints = useMemo(() => {
if (props.fullscreenContent && heightContent) {
return [fullscreenHeight, heightContent, heightHeader];
}
if (props.fullscreenContent) {
return [fullscreenHeight, heightHeader];
}
if (isLargeContentValid) {
return [heightContent, heightHeader];
}
return [heightHeader];
}, [props.fullscreenContent, heightContent, fullscreenHeight, heightHeader, isLargeContentValid]);

const offsetSnapPoints = useMemo(() => snapPoints.map(p => p + OFFSET_BOTTOM_SHEET), [snapPoints]);

const renderContent = useCallback((): ReactNode => {
const content = (
<View onLayout={onLayoutHandlerContent} pointerEvents="box-none">
Expand All @@ -77,7 +98,15 @@ export const ExpandingDrawer = (props: ExpandingDrawerProps): ReactElement => {
);
}
return content;
}, [props.smallContent, props.largeContent, props.fullscreenContent, isOpen, fullscreenHeight]);
}, [
props.smallContent,
props.largeContent,
props.fullscreenContent,
fullscreenHeight,
isSmallContentValid,
onLayoutHandlerContent,
onLayoutHandlerHeader
]);

if (props.fullscreenContent && fullscreenHeight === 0) {
return (
Expand All @@ -91,18 +120,9 @@ export const ExpandingDrawer = (props: ExpandingDrawerProps): ReactElement => {
return <View style={{ position: "absolute", bottom: -maxHeight }}>{renderContent()}</View>;
}

const snapPoints =
props.fullscreenContent && heightContent
? [fullscreenHeight, heightContent, heightHeader]
: props.fullscreenContent
? [fullscreenHeight, heightHeader]
: isLargeContentValid
? [heightContent, heightHeader]
: [heightHeader];

const collapsedIndex = 0;

const onChange = (index: number) => {
const onChange = (index: number): void => {
const hasOpened = lastIndexRef === -1 && index === 0;
const hasClosed = index === -1;

Expand All @@ -123,7 +143,7 @@ export const ExpandingDrawer = (props: ExpandingDrawerProps): ReactElement => {
<BottomSheet
ref={bottomSheetRef}
index={collapsedIndex}
snapPoints={snapPoints.map(p => p + OFFSET_BOTTOM_SHEET)}
snapPoints={offsetSnapPoints}
onClose={() => setIsOpen(false)}
enablePanDownToClose={false}
onChange={onChange}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ReactElement, useCallback, useEffect, useRef } from "react";
import { ReactElement, useCallback, useEffect, useMemo, useRef } from "react";
import {
ActionSheetIOS,
Appearance,
Dimensions,
Modal,
Platform,
Pressable,
Expand Down Expand Up @@ -29,6 +30,18 @@ let lastIndexRef = -1;
export const NativeBottomSheet = (props: NativeBottomSheetProps): ReactElement => {
const bottomSheetRef = useRef<BottomSheet>(null);

const snapPoints = useMemo(() => {
// @gorhom/bottom-sheet relies on Reanimated worklets; passing a stable snapPoints array
// avoids UI-thread "non-worklet function" crashes caused by unstable/undefined inputs.
const itemCount = props.itemsBasic.length;
const itemHeight = 44;
const verticalPadding = 24;
const maxHeight = Dimensions.get("window").height * 0.9;
const estimatedHeight = itemCount * itemHeight + verticalPadding;

return [Math.min(maxHeight, estimatedHeight)];
}, [props.itemsBasic.length]);

const isAvailable = props.triggerAttribute && props.triggerAttribute.status === ValueStatus.Available;
const isOpen =
props.triggerAttribute &&
Expand Down Expand Up @@ -139,12 +152,12 @@ export const NativeBottomSheet = (props: NativeBottomSheetProps): ReactElement =
);
};

const getContainerStyle = () => {
const containerStyle = useMemo(() => {
if (props.useNative) {
return [nativeAndroidStyles.sheetContainer];
}
return [styles.sheetContainer, props.styles.container];
};
}, [props.useNative, props.styles.container]);

const handleSheetChanges = (index: number) => {
if (!isAvailable) {
Expand Down Expand Up @@ -175,11 +188,12 @@ export const NativeBottomSheet = (props: NativeBottomSheetProps): ReactElement =
<BottomSheet
ref={bottomSheetRef}
index={isOpen ? 0 : -1} // Start closed.
snapPoints={snapPoints}
enablePanDownToClose
animateOnMount
onClose={() => handleSheetChanges(-1)}
onChange={handleSheetChanges}
style={getContainerStyle()}
style={containerStyle}
backdropComponent={renderBackdrop}
backgroundStyle={props.styles.container}
handleComponent={null}
Expand Down
44 changes: 43 additions & 1 deletion patches/@mendix+pluggable-widgets-tools+10.21.1.patch
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,48 @@ index 682cf9fc4259a1f431e7cc3b17a319fdb072929e..5be03c770f8247637223f78738c3cdec
/^react-native-svg($|\/)/,
/^react-native-vector-icons($|\/)/,
/^@?react-navigation($|\/)/,
@@ -179,6 +179,18 @@ export default async args => {
return result;

function getCommonPlugins(config) {
+ const hasReanimated = (() => {
+ try {
+ const packageJson = require(join(sourcePath, "package.json"));
+ return !!(
+ (packageJson.dependencies && packageJson.dependencies["react-native-reanimated"]) ||
+ (packageJson.peerDependencies && packageJson.peerDependencies["react-native-reanimated"])
+ );
+ } catch {
+ return false;
+ }
+ })();
+
return [
nodeResolve({ preferBuiltins: false, mainFields: ["module", "browser", "main"] }),
isTypescript
@@ -232,7 +244,21 @@ export default async args => {
})
: null,
image(),
- production ? terser({ mangle: false }) : null,
+ production ? terser(hasReanimated
+ ? {
+ mangle: false,
+ compress: {
+ defaults: false,
+ inline: false,
+ reduce_funcs: false,
+ side_effects: false,
+ },
+ keep_fnames: true,
+ keep_classnames: true,
+ module: false,
+ format: { comments: false }
+ }
+ : { mangle: false }) : null,
// We need to create .mpk and copy results to test project after bundling is finished.
// In case of a regular build is it is on `writeBundle` of the last config we define
// (since rollup processes configs sequentially). But in watch mode rollup re-bundles only
diff --git a/configs/tsconfig.base.json b/configs/tsconfig.base.json
index 5cee5d4d880e152576fc7cad69dd8c22dfbedee8..62627fd52bf0d5847d9ebec37fadac3142ed71a7 100644
--- a/configs/tsconfig.base.json
Expand Down Expand Up @@ -176,4 +218,4 @@ index eed8109dada3788bb1195573b9713eb1f00dd8f9..4b422fac0e21d2b1f44a763d0b21b028
module.exports = require("babel-jest").createTransformer({
- presets: ["module:metro-react-native-babel-preset"]
+ presets: ["module:@react-native/babel-preset"]
});
});
Loading
Loading