Skip to content

Commit

Permalink
Added showVertically property
Browse files Browse the repository at this point in the history
  • Loading branch information
mgroeneweg committed Jun 18, 2020
1 parent 4e41ed2 commit 05ff7a6
Show file tree
Hide file tree
Showing 87 changed files with 151 additions and 106 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
## NativeResponsiveListview

## Features
- Renders the items horizontally, without scrollbar
- Renders the items horizontally or vertically, without scrolling
- Items will flow into multiple lines where necessary.
- Widget does not add any padding or margin.
- No events, if you need onClick, just use a container as widget content
- No scroll container, place widget in scroll container where necessary
- Not suitable for really large number of items as all items are rendered directly. No lazy loading!

Originally, this widget was intended to show items horizontally only. The vertical option was added because React Native does not approve of nested scrollable items. A listview is scrollable by design. Nesting listviews or nesting a listview in a scrollable view leads to scrolling issues. These are signalled as warnings when running the Make It Native app in developer mode. Quite often, these nested lists have small number of items, so there is no need to make these nested lists scrollable themselves. This widget can be used in these situations as well because it has no scroll container.
8 changes: 6 additions & 2 deletions src/NativeResponsiveListview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ export interface CustomStyle extends Style {

export class NativeResponsiveListview extends Component<NativeResponsiveListviewProps<CustomStyle>> {
render(): ReactNode {
const { ds } = this.props;
const { ds, content, style } = this.props;
if (!ds || !ds.items) {
return null;
}
let showVertically = false;
if (this.props.showVertically && this.props.showVertically.value) {
showVertically = true;
}
return (
<ResponsiveListview ds={ds} content={this.props.content} style={this.props.style} />
<ResponsiveListview ds={ds} content={content} style={style} showVertically={showVertically} />
);
}
}
7 changes: 6 additions & 1 deletion src/NativeResponsiveListview.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@
<description></description>
</property>

<property key="content" type="widgets" dataSource="ds" required="true">
<property key="content" type="widgets" dataSource="ds">
<caption>Content</caption>
<description></description>
</property>
<property key="showVertically" type="expression" required="false" defaultValue="false">
<caption>Show items vertically</caption>
<description></description>
<returnType type="Boolean" />
</property>
</propertyGroup>
</properties>
</widget>
18 changes: 15 additions & 3 deletions src/components/ResponsiveListview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface ResponsiveListviewProps {
ds: ListValue;
content: (item: ObjectItem) => ReactNode;
style: CustomStyle[];
showVertically: boolean;
}

const defaultStyle: CustomStyle = {
Expand All @@ -18,16 +19,27 @@ const defaultStyle: CustomStyle = {
}
};

const defaultVerticalStyle: CustomStyle = {
container: {
flexDirection: "column"
}
};

export class ResponsiveListview extends Component<ResponsiveListviewProps> {
private readonly styles = flattenStyles(defaultStyle, this.props.style);

render(): ReactNode {
const { ds, content } = this.props;
const { ds, content, showVertically } = this.props;
let styles: CustomStyle;
if (showVertically) {
styles = flattenStyles(defaultVerticalStyle, this.props.style);
} else {
styles = flattenStyles(defaultStyle, this.props.style);
}
if (!ds || !ds.items) {
return null;
}
return (
<View style={this.styles.container}>
<View style={styles.container}>
{ds.items.map((item) => <View key={item.id}>{content(item)}</View>)}
</View>
);
Expand Down
2 changes: 1 addition & 1 deletion src/package.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<package xmlns="http://www.mendix.com/package/1.0/">
<clientModule name="NativeResponsiveListview" version="1.0.0" xmlns="http://www.mendix.com/clientModule/1.0/">
<clientModule name="NativeResponsiveListview" version="1.1.0" xmlns="http://www.mendix.com/clientModule/1.0/">
<widgetFiles>
<widgetFile path="NativeResponsiveListview.xml"/>
</widgetFiles>
Expand Down
Binary file modified test/TestNativeResponsiveListview.mpr
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import { Big } from "big.js";
*/
export async function GetCurrentLocation(timeout, maximumAge, highAccuracy) {
// BEGIN USER CODE
if (navigator && navigator.product === "ReactNative" && !navigator.geolocation) {
navigator.geolocation = require("@react-native-community/geolocation");
}
return new Promise((resolve, reject) => {
const options = getOptions();
navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ export async function GetStorageItemObject(key, entity) {
});
function getItem(key) {
if (navigator && navigator.product === "ReactNative") {
const AsyncStorage = require("@react-native-community/async-storage")
.default;
const AsyncStorage = require("@react-native-community/async-storage").default;
return AsyncStorage.getItem(key);
}
if (window) {
Expand All @@ -48,8 +47,7 @@ export async function GetStorageItemObject(key, entity) {
}
function setItem(key, value) {
if (navigator && navigator.product === "ReactNative") {
const AsyncStorage = require("@react-native-community/async-storage")
.default;
const AsyncStorage = require("@react-native-community/async-storage").default;
return AsyncStorage.setItem(key, value);
}
if (window) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ export async function GetStorageItemObjectList(key, entity) {
});
function getItem(key) {
if (navigator && navigator.product === "ReactNative") {
const AsyncStorage = require("@react-native-community/async-storage")
.default;
const AsyncStorage = require("@react-native-community/async-storage").default;
return AsyncStorage.getItem(key);
}
if (window) {
Expand All @@ -48,8 +47,7 @@ export async function GetStorageItemObjectList(key, entity) {
}
function setItem(key, value) {
if (navigator && navigator.product === "ReactNative") {
const AsyncStorage = require("@react-native-community/async-storage")
.default;
const AsyncStorage = require("@react-native-community/async-storage").default;
return AsyncStorage.setItem(key, value);
}
if (window) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ export async function GetStorageItemString(key) {
});
async function getItem(key) {
if (navigator && navigator.product === "ReactNative") {
const AsyncStorage = require("@react-native-community/async-storage")
.default;
const AsyncStorage = require("@react-native-community/async-storage").default;
return AsyncStorage.getItem(key);
}
if (window) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export async function NavigateTo(location) {
const Platform = require("react-native").Platform;
const url = Platform.select({
ios: iosUrl,
android: androidUrl
default: androidUrl
});
return Linking.canOpenURL(url).then(supported => {
if (!supported) {
Expand Down
2 changes: 1 addition & 1 deletion test/javascriptsource/nanoflowcommons/actions/OpenMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export async function OpenMap(location) {
const Platform = require("react-native").Platform;
const url = Platform.select({
ios: iosUrl,
android: androidUrl
default: androidUrl
});
return Linking.canOpenURL(url).then(supported => {
if (!supported) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ export async function RemoveStorageItem(key) {
return removeItem(key).then(() => true);
function removeItem(key) {
if (navigator && navigator.product === "ReactNative") {
const AsyncStorage = require("@react-native-community/async-storage")
.default;
const AsyncStorage = require("@react-native-community/async-storage").default;
return AsyncStorage.removeItem(key);
}
if (window) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export async function RequestLocationPermission() {
if (navigator && navigator.product === "ReactNative") {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const RN = require("react-native");
if (!navigator.geolocation) {
navigator.geolocation = require("@react-native-community/geolocation");
}
if (RN.Platform.OS === "android") {
const locationPermission = RN.PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION;
return RN.PermissionsAndroid.check(locationPermission).then(hasPermission => hasPermission
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ export async function SetStorageItemObject(key, value) {
return setItem(key, JSON.stringify(serializedObject));
function setItem(key, value) {
if (navigator && navigator.product === "ReactNative") {
const AsyncStorage = require("@react-native-community/async-storage")
.default;
const AsyncStorage = require("@react-native-community/async-storage").default;
return AsyncStorage.setItem(key, value);
}
if (window) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ export async function SetStorageItemObjectList(key, value) {
return setItem(key, JSON.stringify(serializedObjects));
function setItem(key, value) {
if (navigator && navigator.product === "ReactNative") {
const AsyncStorage = require("@react-native-community/async-storage")
.default;
const AsyncStorage = require("@react-native-community/async-storage").default;
return AsyncStorage.setItem(key, value);
}
if (window) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ export async function SetStorageItemString(key, value) {
return setItem(key, value);
function setItem(key, value) {
if (navigator && navigator.product === "ReactNative") {
const AsyncStorage = require("@react-native-community/async-storage")
.default;
const AsyncStorage = require("@react-native-community/async-storage").default;
return AsyncStorage.setItem(key, value);
}
if (window) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,25 @@ import { Big } from "big.js";

/**
* Shows a confirmation dialog during the execution of a nanoflow, to make perform actions based on the user input.
* @param {string} titleCaption - Set to empty to use default text 'Confirmation'. (Only for native)
* @param {string} question - This field is required.
* @param {string} cancelButtonCaption - Set to empty to use default text 'Cancel'.
* @param {string} proceedButtonCaption - Set to empty to use default text 'OK'.
* @returns {Promise.<boolean>}
*/
export async function ShowConfirmation(question, cancelButtonCaption, proceedButtonCaption) {
export async function ShowConfirmation(titleCaption, question, cancelButtonCaption, proceedButtonCaption) {
// BEGIN USER CODE
if (!question) {
return Promise.reject(new Error("Input parameter 'Question' is required"));
}
const cancel = cancelButtonCaption || "Cancel";
const proceed = proceedButtonCaption || "OK";
const title = titleCaption || "Confirmation";
// Native platform
if (navigator && navigator.product === "ReactNative") {
const Alert = require("react-native").Alert;
return new Promise(resolve => {
Alert.alert("Confirmation", question, [
Alert.alert(title, question, [
{ text: cancel, onPress: () => resolve(false), style: "cancel" },
{ text: proceed, onPress: () => resolve(true) }
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ export async function StorageItemExists(key) {
return getItem(key).then(result => result !== null);
function getItem(key) {
if (navigator && navigator.product === "ReactNative") {
const AsyncStorage = require("@react-native-community/async-storage")
.default;
const AsyncStorage = require("@react-native-community/async-storage").default;
return AsyncStorage.getItem(key);
}
if (window) {
Expand Down
21 changes: 21 additions & 0 deletions test/javascriptsource/nanoflowcommons/actions/ToggleSidebar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
import { Big } from "big.js";

// BEGIN EXTRA CODE
// END EXTRA CODE

/**
* @returns {Promise.<void>}
*/
export async function ToggleSidebar() {
// BEGIN USER CODE
mx.ui.toggleSidebar();
return Promise.resolve();
// END USER CODE
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import { Platform, StatusBar } from "react-native";
// END EXTRA CODE

/**
* @param {"NativeMobileActions.StatusBarStyle.DefaultStyle"|"NativeMobileActions.StatusBarStyle.LightContentStyle"|"NativeMobileActions.StatusBarStyle.DarkContentStyle"} style - If empty, the current style is not changed.
* @param {"NativeMobileResources.StatusBarStyle.DefaultStyle"|"NativeMobileResources.StatusBarStyle.LightContentStyle"|"NativeMobileResources.StatusBarStyle.DarkContentStyle"} style - If empty, the current style is not changed.
* @param {boolean} hidden
* @param {boolean} animateChanges
* @param {string} backgroundColor - If empty, the current background color is not changed.
* @param {boolean} translucent
* @param {boolean} networkActivityIndicatorVisible
* @param {"NativeMobileActions.StatusBarHideShowAnimation.none"|"NativeMobileActions.StatusBarHideShowAnimation.fade"|"NativeMobileActions.StatusBarHideShowAnimation.slide"} animateHideShow - If empty, the default value 'none' is used.
* @param {"NativeMobileResources.StatusBarHideShowAnimation.none"|"NativeMobileResources.StatusBarHideShowAnimation.fade"|"NativeMobileResources.StatusBarHideShowAnimation.slide"} animateHideShow - If empty, the default value 'none' is used.
* @returns {Promise.<void>}
*/
export async function ChangeStatusBar(style, hidden, animateChanges, backgroundColor, translucent, networkActivityIndicatorVisible, animateHideShow) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import InAppBrowser from "react-native-inappbrowser-reborn";
/**
* @param {string} url - This field is required.
* @param {string} toolbarColor - An optional custom background color for the browser toolbar. For example: 'red' or '#6200EE'.
* @param {"NativeMobileActions.InAppBrowserDismissButtonStyle.done"|"NativeMobileActions.InAppBrowserDismissButtonStyle.close"|"NativeMobileActions.InAppBrowserDismissButtonStyle.cancel"} iosDismissButtonStyle - iOS only setting. The text that should be used for the button that closes the in app browser. Set to empty to use default value 'close'.
* @param {"NativeMobileResources.InAppBrowserDismissButtonStyle.done"|"NativeMobileResources.InAppBrowserDismissButtonStyle.close"|"NativeMobileResources.InAppBrowserDismissButtonStyle.cancel"} iosDismissButtonStyle - iOS only setting. The text that should be used for the button that closes the in app browser. Set to empty to use default value 'close'.
* @param {boolean} androidShowTitle - Android only setting. Set to true to show the title of the web page in the toolbar.
* @returns {Promise.<void>}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
import { Big } from "big.js";
import { CameraRoll } from "react-native";

// BEGIN EXTRA CODE
// END EXTRA CODE
Expand All @@ -18,6 +17,8 @@ import { CameraRoll } from "react-native";
export async function SaveToPictureLibrary(picture) {
// BEGIN USER CODE
// Documentation https://facebook.github.io/react-native/docs/cameraroll#savetocameraroll
var _a;
const CameraRoll = (_a = require("@react-native-community/cameraroll")) !== null && _a !== void 0 ? _a : require("react-native").CameraRoll;
if (!picture) {
return Promise.reject(new Error("Input parameter 'Picture' is required"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import ImagePicker from "react-native-image-picker";

/**
* @param {MxObject} picture - This field is required.
* @param {"NativeMobileActions.PictureSource.camera"|"NativeMobileActions.PictureSource.imageLibrary"|"NativeMobileActions.PictureSource.either"} pictureSource - Select a picture from the library or the camera. The default is to let the user decide.
* @param {"NativeMobileActions.PictureQuality.original"|"NativeMobileActions.PictureQuality.low"|"NativeMobileActions.PictureQuality.medium"|"NativeMobileActions.PictureQuality.high"|"NativeMobileActions.PictureQuality.custom"} pictureQuality - Set to empty to use default value 'medium'.
* @param {"NativeMobileResources.PictureSource.camera"|"NativeMobileResources.PictureSource.imageLibrary"|"NativeMobileResources.PictureSource.either"} pictureSource - Select a picture from the library or the camera. The default is to let the user decide.
* @param {"NativeMobileResources.PictureQuality.original"|"NativeMobileResources.PictureQuality.low"|"NativeMobileResources.PictureQuality.medium"|"NativeMobileResources.PictureQuality.high"|"NativeMobileResources.PictureQuality.custom"} pictureQuality - Set to empty to use default value 'medium'.
* @param {Big} maximumWidth - The picture will be scaled to this maximum pixel width, while maintaing the aspect ratio.
* @param {Big} maximumHeight - The picture will be scaled to this maximum pixel height, while maintaing the aspect ratio.
* @returns {Promise.<boolean>}
Expand Down
Loading

0 comments on commit 05ff7a6

Please sign in to comment.