fix layout of carpet roll calculator. todo: reinstate unit buttons for plywood calculator.
This commit is contained in:
@ -36,7 +36,6 @@ const BIG_FONT_SIZE = 30;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
component: {
|
||||
paddingVertical: 100,
|
||||
alignItems: "center",
|
||||
},
|
||||
dimensions: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { View, Text, StyleSheet } from "react-native";
|
||||
import { View, Text, StyleSheet, StatusBar, Dimensions, useWindowDimensions } from "react-native";
|
||||
import { Product } from "@/lib/product";
|
||||
import { selectProducts } from "@/features/product/productSlice";
|
||||
import { area_t, diameterToLength, length_t } from "@/lib/dimensions";
|
||||
@ -9,10 +9,14 @@ import convert, { Length } from "convert";
|
||||
import ProductList from "@/components/ProductList";
|
||||
import { HelpfulMeasurementUnitInput } from "./HelpfulMeasurementInput";
|
||||
import { ScrollView } from "react-native-gesture-handler";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
const DEFAULT_DIAMETER_UNIT: Length = "in";
|
||||
const DEFAULT_LENGTH_UNIT: Length = "ft";
|
||||
|
||||
const screenDimensions = Dimensions.get('screen');
|
||||
const windowDimensions = Dimensions.get('window');
|
||||
|
||||
export const CarpetRollCalculator = () => {
|
||||
const products = useAppSelector(selectProducts);
|
||||
|
||||
@ -61,85 +65,93 @@ export const CarpetRollCalculator = () => {
|
||||
}, [outerDiameter, innerDiameter, width, numRings, selectedProduct, units]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View>
|
||||
{selectedProduct ? (
|
||||
<AreaRugTag dimensions={rugDimensions} product={selectedProduct} />
|
||||
) : (
|
||||
<Text style={styles.placeholder}>Please Select a Product</Text>
|
||||
)}
|
||||
</View>
|
||||
<View style={{ flex: 1, }}>
|
||||
<ScrollView>
|
||||
<View style={styles.inputFields}>
|
||||
<View style={styles.inputFieldWrapper}>
|
||||
<HelpfulMeasurementUnitInput
|
||||
label="Length"
|
||||
svgUri="/assets/images/icons/carpet-roll-length-raw.svg"
|
||||
onUnitSet={setUnits}
|
||||
onValueSet={setWidth}
|
||||
defaultValue={width}
|
||||
defaultUnit={units}
|
||||
unitChoices={["ft", "in"]}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.inputFieldWrapper}>
|
||||
<HelpfulMeasurementUnitInput
|
||||
label="Inner diameter"
|
||||
svgUri="/assets/images/icons/carpet-roll-length-inner-diameter-raw.svg"
|
||||
onUnitSet={(u) => setInnerDiameter({ ...innerDiameter, u })}
|
||||
defaultValue={innerDiameter.l}
|
||||
defaultUnit={innerDiameter.u}
|
||||
unitChoices={["ft", "in"]}
|
||||
onValueSet={(l) => setInnerDiameter({ ...innerDiameter, l })}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.inputFieldWrapper}>
|
||||
<HelpfulMeasurementUnitInput
|
||||
label="Outer diameter"
|
||||
svgUri="/assets/images/icons/carpet-roll-length-outer-diameter-raw.svg"
|
||||
onUnitSet={(u) => setOuterDiameter({ ...outerDiameter, u })}
|
||||
defaultValue={innerDiameter.l}
|
||||
defaultUnit={innerDiameter.u}
|
||||
unitChoices={["ft", "in"]}
|
||||
onValueSet={(l) => setOuterDiameter({ ...outerDiameter, l })}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.inputFieldWrapper}>
|
||||
<HelpfulMeasurementUnitInput
|
||||
label="Number of rings"
|
||||
svgUri="/assets/images/icons/carpet-roll-length-number-of-rings-raw.svg"
|
||||
defaultValue={0}
|
||||
onValueSet={setNumRings}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.container}>
|
||||
<ProductList
|
||||
onProductSelected={setSelectedProduct}
|
||||
productType="area_rug"
|
||||
<SafeAreaView style={styles.container}>
|
||||
<View style={styles.placeholder}>
|
||||
{selectedProduct ? (
|
||||
<AreaRugTag dimensions={rugDimensions} product={selectedProduct} />
|
||||
) : (
|
||||
<Text style={styles.placeholderText}>Please Select a Product</Text>
|
||||
)}
|
||||
</View>
|
||||
<ScrollView style={styles.scrollView}>
|
||||
<View>
|
||||
<View style={styles.inputFieldWrapper}>
|
||||
<HelpfulMeasurementUnitInput
|
||||
label="Length"
|
||||
svgUri="/assets/images/icons/carpet-roll-length-raw.svg"
|
||||
onUnitSet={setUnits}
|
||||
onValueSet={setWidth}
|
||||
defaultValue={width}
|
||||
defaultUnit={units}
|
||||
unitChoices={["ft", "in"]}
|
||||
/>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.inputFieldWrapper}>
|
||||
<HelpfulMeasurementUnitInput
|
||||
label="Inner diameter"
|
||||
svgUri="/assets/images/icons/carpet-roll-length-inner-diameter-raw.svg"
|
||||
onUnitSet={(u) => setInnerDiameter({ ...innerDiameter, u })}
|
||||
defaultValue={innerDiameter.l}
|
||||
defaultUnit={innerDiameter.u}
|
||||
unitChoices={["ft", "in"]}
|
||||
onValueSet={(l) => setInnerDiameter({ ...innerDiameter, l })}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.inputFieldWrapper}>
|
||||
<HelpfulMeasurementUnitInput
|
||||
label="Outer diameter"
|
||||
svgUri="/assets/images/icons/carpet-roll-length-outer-diameter-raw.svg"
|
||||
onUnitSet={(u) => setOuterDiameter({ ...outerDiameter, u })}
|
||||
defaultValue={innerDiameter.l}
|
||||
defaultUnit={innerDiameter.u}
|
||||
unitChoices={["ft", "in"]}
|
||||
onValueSet={(l) => setOuterDiameter({ ...outerDiameter, l })}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.inputFieldWrapper}>
|
||||
<HelpfulMeasurementUnitInput
|
||||
label="Number of rings"
|
||||
svgUri="/assets/images/icons/carpet-roll-length-number-of-rings-raw.svg"
|
||||
defaultValue={0}
|
||||
onValueSet={setNumRings}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View>
|
||||
<ProductList
|
||||
onProductSelected={setSelectedProduct}
|
||||
productType="area_rug"
|
||||
/>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexGrow: 1,
|
||||
flex: 1,
|
||||
paddingTop: StatusBar.currentHeight,
|
||||
},
|
||||
scrollView: {
|
||||
marginHorizontal: 20,
|
||||
height: windowDimensions.height - 50,
|
||||
},
|
||||
placeholder: {
|
||||
alignContent: "center",
|
||||
alignSelf: "center",
|
||||
paddingTop: 50,
|
||||
paddingBottom: 50,
|
||||
height: 300,
|
||||
paddingTop: 40,
|
||||
paddingBottom: 40,
|
||||
fontSize: 30,
|
||||
position: "static",
|
||||
},
|
||||
placeholderText: {
|
||||
fontSize: 30,
|
||||
paddingVertical: 70,
|
||||
},
|
||||
inputFieldWrapper: {
|
||||
padding: 10,
|
||||
// padding: 10,
|
||||
},
|
||||
inputFields: {},
|
||||
label: {
|
||||
|
@ -5,13 +5,12 @@ import { View, Text, StyleSheet } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import PriceDisplay from "./Price";
|
||||
import { AreaInput } from "./AreaInput";
|
||||
import { MeasurementInput } from "./MeasurementInput";
|
||||
import ProductList from "./ProductList";
|
||||
import UnitChooser from "./UnitChooser";
|
||||
import convert, { Length } from "convert";
|
||||
import PercentDamage from "./PercentDamange";
|
||||
import MeasurementUnitInput from "./MeasurementUnitInput";
|
||||
|
||||
|
||||
export default function ProductCalculatorSelector() {
|
||||
const [activeProduct, setActiveProduct] = useState(null as Product | null);
|
||||
const [price, setPrice] = useState(0);
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { ScrollView, StyleSheet } from "react-native";
|
||||
import { Dimensions, ScrollView, StyleSheet } from "react-native";
|
||||
import { ProductTile } from "./ProductTile";
|
||||
import { Product, product_type_t } from "@/lib/product";
|
||||
import { useState } from "react";
|
||||
import { selectProducts } from "@/features/product/productSlice";
|
||||
import { useAppSelector } from "@/app/store";
|
||||
|
||||
const windowDimensions = Dimensions.get('window');
|
||||
|
||||
export type ProductSelectionProps = {
|
||||
onProductSelected?: (product: Product) => any;
|
||||
productType?: product_type_t;
|
||||
@ -28,7 +30,7 @@ export default function ProductList({
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollView scrollToOverflowEnabled={true} aria-label="product list">
|
||||
<ScrollView style={styles.productSelectorFlatList} contentContainerStyle={styles.content} aria-label="product list">
|
||||
{products.map((product) => {
|
||||
return (
|
||||
<ProductTile
|
||||
@ -47,5 +49,12 @@ const styles = StyleSheet.create({
|
||||
productSelectorFlatList: {
|
||||
padding: 10,
|
||||
margin: 10,
|
||||
height: windowDimensions.height - 200,
|
||||
width: windowDimensions.width,
|
||||
},
|
||||
content: {
|
||||
alignItems: "flex-start",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
}
|
||||
});
|
||||
|
@ -1,58 +1,84 @@
|
||||
import { Product, priceDisplay, pricePerUnitDisplay } from "@/lib/product"
|
||||
import { ImageBackground, StyleProp, StyleSheet, Text, TouchableHighlight, View, ViewStyle } from "react-native";
|
||||
import { Product, priceDisplay, pricePerUnitDisplay } from "@/lib/product";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import {
|
||||
ImageBackground,
|
||||
Pressable,
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from "react-native";
|
||||
import { AnimatedStyle } from "react-native-reanimated";
|
||||
|
||||
export type OnProductSelectedFunc = (product : Product) => any;
|
||||
export type OnProductSelectedFunc = (product: Product) => any;
|
||||
|
||||
type MyStyle = StyleProp<AnimatedStyle<StyleProp<ViewStyle>>>;
|
||||
|
||||
type StyleSpec = {
|
||||
highlight?: MyStyle,
|
||||
text?: MyStyle,
|
||||
image?: MyStyle,
|
||||
}
|
||||
|
||||
highlight?: MyStyle;
|
||||
text?: MyStyle;
|
||||
image?: MyStyle;
|
||||
};
|
||||
|
||||
export type ProductTileProps = {
|
||||
product: (Product),
|
||||
onProductSelected?: OnProductSelectedFunc,
|
||||
isActive: boolean,
|
||||
}
|
||||
product: Product;
|
||||
onProductSelected?: OnProductSelectedFunc;
|
||||
isActive: boolean;
|
||||
};
|
||||
|
||||
const FALLBACK_IMAGE = "";
|
||||
|
||||
export function ProductTile ({product, onProductSelected, isActive} : ProductTileProps) {
|
||||
const k = isActive ? "active" : "default";
|
||||
export function ProductTile({
|
||||
product,
|
||||
onProductSelected,
|
||||
isActive,
|
||||
}: ProductTileProps) {
|
||||
const k = isActive ? "active" : "default";
|
||||
|
||||
const priceDisplay = pricePerUnitDisplay(product);
|
||||
return (
|
||||
const BLUE_HILIGHT = "#caceff";
|
||||
const BLUE = "#8b9cff";
|
||||
const GRAY_HILIGHT = "#ffffff";
|
||||
const GRAY = "#b1b1b1";
|
||||
|
||||
<TouchableHighlight
|
||||
aria-label={`product ${product.id}`}
|
||||
style={styles[k].highlight}
|
||||
onPress={() => onProductSelected && onProductSelected(product)}>
|
||||
<Text style={styles[k].text}>{product.attributes?.name || `Product ${product.id}`} ({priceDisplay})</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
const activeColors = [BLUE_HILIGHT, BLUE, BLUE, BLUE];
|
||||
const inactiveColors = [GRAY_HILIGHT, GRAY, GRAY, GRAY];
|
||||
|
||||
const priceDisplay = pricePerUnitDisplay(product);
|
||||
return (
|
||||
<LinearGradient
|
||||
colors={isActive ? activeColors : inactiveColors}
|
||||
style={styles.gradientButton}
|
||||
>
|
||||
<Pressable
|
||||
style={styles.button}
|
||||
aria-label={`product ${product.id}`}
|
||||
onPress={() => onProductSelected && onProductSelected(product)}
|
||||
>
|
||||
<Text style={styles.text}>
|
||||
{product.attributes?.name || `Product ${product.id}`} ({priceDisplay})
|
||||
</Text>
|
||||
</Pressable>
|
||||
</LinearGradient>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = {
|
||||
active: StyleSheet.create({
|
||||
highlight: {
|
||||
padding: 10,
|
||||
margin: 2,
|
||||
color: "lightblue",
|
||||
},
|
||||
text: {
|
||||
}
|
||||
}),
|
||||
default: StyleSheet.create({
|
||||
highlight: {
|
||||
padding: 10,
|
||||
margin: 2,
|
||||
backgroundColor: "lightgrey",
|
||||
},
|
||||
text: {
|
||||
}
|
||||
}),
|
||||
}
|
||||
const styles = StyleSheet.create({
|
||||
gradientButton: {
|
||||
borderRadius: 10,
|
||||
borderWidth: 1,
|
||||
borderColor: "gray",
|
||||
borderStyle: "solid",
|
||||
margin: 1,
|
||||
width: 300,
|
||||
marginVertical: 10,
|
||||
marginHorizontal: 10,
|
||||
},
|
||||
button: {
|
||||
},
|
||||
text: {
|
||||
paddingVertical: 30,
|
||||
paddingHorizontal: 40,
|
||||
}
|
||||
});
|
||||
|
@ -38,8 +38,13 @@ export default function UnitChooser({
|
||||
onUnitSet && onUnitSet(choice);
|
||||
}
|
||||
|
||||
const activeColors = ['#a7caff', '#5588ff', '#5588ff', '#5588ff'];
|
||||
const inactiveColors = ['#d0d0d0', '#828282', '#828282', '#828282'];
|
||||
const BLUE_HILIGHT = "#caceff";
|
||||
const BLUE = "#8b9cff";
|
||||
const GRAY_HILIGHT = "#ffffff";
|
||||
const GRAY = "#b1b1b1";
|
||||
|
||||
const activeColors = [BLUE_HILIGHT, BLUE, BLUE, BLUE];
|
||||
const inactiveColors = [GRAY_HILIGHT, GRAY, GRAY, GRAY];
|
||||
|
||||
return (
|
||||
<View style={styles.unitChooser}>
|
||||
@ -49,7 +54,7 @@ export default function UnitChooser({
|
||||
colors={ci === value ? activeColors : inactiveColors}
|
||||
style={styles.gradientButton}
|
||||
>
|
||||
<TouchableHighlight style={{padding: 5, borderRadius: 5, }} onPress={() => doChoiceClicked(ci)} key={ci}>
|
||||
<TouchableHighlight style={{padding: 5, }} onPress={() => doChoiceClicked(ci)} key={ci}>
|
||||
<Text style={{padding: 5, fontSize: 16}}>{ci}</Text>
|
||||
</TouchableHighlight>
|
||||
</LinearGradient>
|
||||
@ -60,7 +65,13 @@ export default function UnitChooser({
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
gradientButton: {},
|
||||
gradientButton: {
|
||||
borderRadius: 10,
|
||||
borderWidth: 1,
|
||||
borderColor: "gray",
|
||||
borderStyle: "solid",
|
||||
margin: 1,
|
||||
},
|
||||
unitChooser: {
|
||||
flexDirection: "row",
|
||||
verticalAlign: "middle",
|
||||
|
Reference in New Issue
Block a user