From 4e67d010b02e8a40e6fa3760d6a8acb5df6bce75 Mon Sep 17 00:00:00 2001 From: Tim Roberts Date: Wed, 12 Nov 2025 17:08:36 +0000 Subject: [PATCH 1/9] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20Turn=20off=20eslint=20?= =?UTF-8?q?quotes=20due=20to=20Prettier=20conflict?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 198 +++++++++++++++++++++++++-------------------------- 1 file changed, 99 insertions(+), 99 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 63053cf..147b7ab 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,119 +1,119 @@ module.exports = { env: { - es2021: true, - node: true, + es2021: true, + node: true, }, settings: { - react: { - version: "detect", - }, + react: { + version: "detect", + }, }, extends: [ - "eslint:recommended", - "plugin:react/recommended", - "plugin:@typescript-eslint/recommended", + "eslint:recommended", + "plugin:react/recommended", + "plugin:@typescript-eslint/recommended", ], parser: "@typescript-eslint/parser", parserOptions: { - ecmaFeatures: { - jsx: true, - }, - ecmaVersion: 12, - sourceType: "module", + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 12, + sourceType: "module", }, plugins: [ - "react", - "react-hooks", - "@typescript-eslint", - "typescript-sort-keys", - "sort-destructure-keys", - "import", + "react", + "react-hooks", + "@typescript-eslint", + "typescript-sort-keys", + "sort-destructure-keys", + "import", ], rules: { - "linebreak-style": ["error", "unix"], - quotes: ["warn", "double"], - semi: ["warn", "always"], - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "error", - "react/display-name": "off", - "react/prop-types": "off", - "no-unused-vars": "off", // disable the base rule as it can report incorrect errors - "@typescript-eslint/consistent-type-imports": [ - "warn", - { prefer: "type-imports" }, - ], - "@typescript-eslint/no-unused-vars": [ - "warn", - { - vars: "all", - args: "after-used", - ignoreRestSiblings: true, - }, - ], - "no-unreachable": "warn", - "typescript-sort-keys/interface": "warn", - "typescript-sort-keys/string-enum": "warn", - "sort-destructure-keys/sort-destructure-keys": [ - "warn", - { caseSensitive: false }, - ], - "react/jsx-sort-props": [ - "warn", - { - ignoreCase: true, - reservedFirst: ["key", "children", "ref"], - }, - ], - "import/order": [ - "warn", - { - "newlines-between": "always", - distinctGroup: true, - alphabetize: { - order: "asc", - caseInsensitive: true, - orderImportKind: "desc", - }, - groups: [ - "builtin", - "external", - "parent", - "sibling", - "internal", - "unknown", - ], - pathGroupsExcludedImportTypes: ["react"], - pathGroups: [ - { - pattern: "react", - group: "builtin", - position: "before", - }, + "linebreak-style": ["error", "unix"], + quotes: ["off"], + semi: ["warn", "always"], + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "error", + "react/display-name": "off", + "react/prop-types": "off", + "no-unused-vars": "off", // disable the base rule as it can report incorrect errors + "@typescript-eslint/consistent-type-imports": [ + "warn", + { prefer: "type-imports" }, + ], + "@typescript-eslint/no-unused-vars": [ + "warn", { - pattern: "([a-z]|@)**", - group: "external", + vars: "all", + args: "after-used", + ignoreRestSiblings: true, }, + ], + "no-unreachable": "warn", + "typescript-sort-keys/interface": "warn", + "typescript-sort-keys/string-enum": "warn", + "sort-destructure-keys/sort-destructure-keys": [ + "warn", + { caseSensitive: false }, + ], + "react/jsx-sort-props": [ + "warn", { - pattern: "**/styles", - group: "internal", - position: "before", + ignoreCase: true, + reservedFirst: ["key", "children", "ref"], }, + ], + "import/order": [ + "warn", { - pattern: "**/types", - group: "internal", - position: "before", + "newlines-between": "always", + distinctGroup: true, + alphabetize: { + order: "asc", + caseInsensitive: true, + orderImportKind: "desc", + }, + groups: [ + "builtin", + "external", + "parent", + "sibling", + "internal", + "unknown", + ], + pathGroupsExcludedImportTypes: ["react"], + pathGroups: [ + { + pattern: "react", + group: "builtin", + position: "before", + }, + { + pattern: "([a-z]|@)**", + group: "external", + }, + { + pattern: "**/styles", + group: "internal", + position: "before", + }, + { + pattern: "**/types", + group: "internal", + position: "before", + }, + { + pattern: "**/components/**", + group: "internal", + }, + { + pattern: "**/utils/**", + group: "internal", + position: "after", + }, + ], }, - { - pattern: "**/components/**", - group: "internal", - }, - { - pattern: "**/utils/**", - group: "internal", - position: "after", - }, - ], - }, - ], + ], }, - }; \ No newline at end of file +}; From 19144b99595a29ed7261a3f47f804a6ec1601e4d Mon Sep 17 00:00:00 2001 From: Tim Roberts Date: Wed, 12 Nov 2025 17:09:39 +0000 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=9A=9A=20Move=20PickerItem=20into=20i?= =?UTF-8?q?ts=20own=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/PickerItem/PickerItem.tsx | 71 ++++++++++++++++++++++++ src/components/PickerItem/index.ts | 1 + 2 files changed, 72 insertions(+) create mode 100644 src/components/PickerItem/PickerItem.tsx create mode 100644 src/components/PickerItem/index.ts diff --git a/src/components/PickerItem/PickerItem.tsx b/src/components/PickerItem/PickerItem.tsx new file mode 100644 index 0000000..ccef3a8 --- /dev/null +++ b/src/components/PickerItem/PickerItem.tsx @@ -0,0 +1,71 @@ +import React from "react"; + +import { View, Text } from "react-native"; + +import type { generateStyles } from "../TimerPicker/styles"; + +interface PickerItemProps { + adjustedLimitedMax: number; + adjustedLimitedMin: number; + allowFontScaling: boolean; + amLabel?: string; + is12HourPicker?: boolean; + item: string; + pmLabel?: string; + styles: ReturnType; +} + +const PickerItem = React.memo( + ({ + adjustedLimitedMax, + adjustedLimitedMin, + allowFontScaling, + amLabel, + is12HourPicker, + item, + pmLabel, + styles, + }) => { + let stringItem = item; + let intItem: number; + let isAm: boolean | undefined; + + if (!is12HourPicker) { + intItem = parseInt(item); + } else { + isAm = item.includes("AM"); + stringItem = item.replace(/\s[AP]M/g, ""); + intItem = parseInt(stringItem); + } + + return ( + + adjustedLimitedMax || + intItem < adjustedLimitedMin + ? styles.disabledPickerItem + : {}, + ]}> + {stringItem} + + {is12HourPicker && ( + + + {isAm ? amLabel : pmLabel} + + + )} + + ); + } +); + +export default PickerItem; diff --git a/src/components/PickerItem/index.ts b/src/components/PickerItem/index.ts new file mode 100644 index 0000000..7006f50 --- /dev/null +++ b/src/components/PickerItem/index.ts @@ -0,0 +1 @@ +export { default } from "./PickerItem"; From 07d5ab31ed436beb2f700acbf15d4d1e3e489342 Mon Sep 17 00:00:00 2001 From: Tim Roberts Date: Wed, 12 Nov 2025 17:15:39 +0000 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=92=84=20Pad=20numbers=20with=20figur?= =?UTF-8?q?e=20space=20to=20fix=20alignment=20issue=20when=20padeWithZero?= =?UTF-8?q?=20is=20false?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/padNumber.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/utils/padNumber.ts b/src/utils/padNumber.ts index f754787..bab1814 100644 --- a/src/utils/padNumber.ts +++ b/src/utils/padNumber.ts @@ -1,23 +1,23 @@ /** * Formats a number by optionally padding it with a leading zero or space. * Numbers less than 10 are padded based on the options provided. - * + * * @param {number} value - The number to format * @param {Object} [options] - Optional formatting options * @param {boolean} [options.padWithZero] - Whether to pad with zero (true) or space (false) - * + * * @returns {string} The formatted number string - * + * * @example * // Pad with zero * padNumber(5, { padWithZero: true }) * // Returns: '05' - * + * * @example - * // Pad with space + * // Pad with figure space (same width as zero) * padNumber(5, { padWithZero: false }) * // Returns: ' 5' - * + * * @example * // No padding needed * padNumber(15) @@ -28,7 +28,7 @@ export const padNumber = ( options?: { padWithZero?: boolean } ): string => { if (value < 10) { - return (options?.padWithZero ? "0" : " ") + value; + return (options?.padWithZero ? "0" : "\u2007") + value; } else { return String(value); } From 7d78a868ee9389102264ab7309de41b59004943b Mon Sep 17 00:00:00 2001 From: Tim Roberts Date: Wed, 12 Nov 2025 17:16:58 +0000 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=92=84=20Improve=20styles=20to=20avoi?= =?UTF-8?q?d=20FlatList=20width=20determination=20issues=20and=20remove=20?= =?UTF-8?q?hacky=20sizing=20calcs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example-expo/components/CustomButton.tsx | 2 +- .../DurationScroll/DurationScroll.tsx | 64 +++-------- src/components/TimerPicker/TimerPicker.tsx | 6 +- src/components/TimerPicker/styles.ts | 100 +++++++++++------- src/components/TimerPickerModal/styles.ts | 17 +-- 5 files changed, 84 insertions(+), 105 deletions(-) diff --git a/examples/example-expo/components/CustomButton.tsx b/examples/example-expo/components/CustomButton.tsx index 21d0187..4eb0279 100644 --- a/examples/example-expo/components/CustomButton.tsx +++ b/examples/example-expo/components/CustomButton.tsx @@ -29,7 +29,7 @@ export const CustomButton: React.FC = ({ const styles = StyleSheet.create({ customButtonContainer: { - marginHorizontal: 5, + marginHorizontal: 12, }, customButtonGradient: { borderRadius: 15, diff --git a/src/components/DurationScroll/DurationScroll.tsx b/src/components/DurationScroll/DurationScroll.tsx index 5d770ab..a07b45c 100644 --- a/src/components/DurationScroll/DurationScroll.tsx +++ b/src/components/DurationScroll/DurationScroll.tsx @@ -22,6 +22,7 @@ import { import { getAdjustedLimit } from "../../utils/getAdjustedLimit"; import { getDurationAndIndexFromScrollOffset } from "../../utils/getDurationAndIndexFromScrollOffset"; import { getInitialScrollIndex } from "../../utils/getInitialScrollIndex"; +import PickerItem from "../PickerItem"; import type { DurationScrollProps, @@ -215,49 +216,18 @@ const DurationScroll = forwardRef( const renderItem = useCallback< NonNullable["renderItem"]> >( - ({ item }) => { - let stringItem = item; - let intItem: number; - let isAm: boolean | undefined; - - if (!is12HourPicker) { - intItem = parseInt(item); - } else { - isAm = item.includes("AM"); - stringItem = item.replace(/\s[AP]M/g, ""); - intItem = parseInt(stringItem); - } - - return ( - - adjustedLimited.max || - intItem < adjustedLimited.min - ? styles.disabledPickerItem - : {}, - ]}> - {stringItem} - - {is12HourPicker ? ( - - - {isAm ? amLabel : pmLabel} - - - ) : null} - - ); - }, + ({ item }) => ( + + ), [ adjustedLimited.max, adjustedLimited.min, @@ -265,11 +235,7 @@ const DurationScroll = forwardRef( amLabel, is12HourPicker, pmLabel, - styles.disabledPickerItem, - styles.pickerAmPmContainer, - styles.pickerAmPmLabel, - styles.pickerItem, - styles.pickerItemContainer, + styles, ] ); @@ -577,6 +543,7 @@ const DurationScroll = forwardRef( }, [ FlatList, allowFontScaling, + decelerationRate, flatListRenderKey, getItemLayout, initialScrollIndex, @@ -593,7 +560,6 @@ const DurationScroll = forwardRef( styles.pickerLabel, styles.pickerLabelContainer, viewabilityConfigCallbackPairs, - decelerationRate, ]); const renderLinearGradient = useMemo(() => { diff --git a/src/components/TimerPicker/TimerPicker.tsx b/src/components/TimerPicker/TimerPicker.tsx index f9d5080..652a3f1 100644 --- a/src/components/TimerPicker/TimerPicker.tsx +++ b/src/components/TimerPicker/TimerPicker.tsx @@ -69,17 +69,17 @@ const TimerPicker = forwardRef( useEffect(() => { if (otherProps.Audio) { console.warn( - "The \"Audio\" prop is deprecated and will be removed in a future version. Please use the \"pickerFeedback\" prop instead." + 'The "Audio" prop is deprecated and will be removed in a future version. Please use the "pickerFeedback" prop instead.' ); } if (otherProps.Haptics) { console.warn( - "The \"Haptics\" prop is deprecated and will be removed in a future version. Please use the \"pickerFeedback\" prop instead." + 'The "Haptics" prop is deprecated and will be removed in a future version. Please use the "pickerFeedback" prop instead.' ); } if (otherProps.clickSoundAsset) { console.warn( - "The \"clickSoundAsset\" prop is deprecated and will be removed in a future version. Please use the \"pickerFeedback\" prop instead." + 'The "clickSoundAsset" prop is deprecated and will be removed in a future version. Please use the "pickerFeedback" prop instead.' ); } }, [otherProps.Audio, otherProps.Haptics, otherProps.clickSoundAsset]); diff --git a/src/components/TimerPicker/styles.ts b/src/components/TimerPicker/styles.ts index 4f39bd2..24c9748 100644 --- a/src/components/TimerPicker/styles.ts +++ b/src/components/TimerPicker/styles.ts @@ -8,6 +8,7 @@ export interface CustomTimerPickerStyles { durationScrollFlatList?: ViewStyle; durationScrollFlatListContainer?: ViewStyle; durationScrollFlatListContentContainer?: ViewStyle; + labelOffsetPercentage?: number; pickerAmPmContainer?: ViewStyle; pickerAmPmLabel?: TextStyle; pickerContainer?: ViewStyle & { backgroundColor?: string }; @@ -27,41 +28,67 @@ const LIGHT_MODE_TEXT_COLOR = "#1B1B1B"; export const generateStyles = ( customStyles: CustomTimerPickerStyles | undefined -) => - StyleSheet.create({ +) => { + const backgroundColor = + customStyles?.backgroundColor ?? + (customStyles?.theme === "dark" + ? DARK_MODE_BACKGROUND_COLOR + : LIGHT_MODE_BACKGROUND_COLOR); + + const textColor = + customStyles?.theme === "dark" + ? DARK_MODE_TEXT_COLOR + : LIGHT_MODE_TEXT_COLOR; + + const pickerLabelFontSize = + customStyles?.pickerLabel?.fontSize ?? + customStyles?.text?.fontSize ?? + 18; + const pickerAmPmFontSize = + customStyles?.pickerAmPmLabel?.fontSize ?? + customStyles?.pickerLabel?.fontSize ?? + customStyles?.text?.fontSize ?? + 18; + const pickerItemFontSize = + customStyles?.pickerItem?.fontSize ?? + customStyles?.text?.fontSize ?? + 25; + + // This offset makes the picker label appear to be aligned with the picker item + // despite them having different font sizes + const pickerLabelVerticalOffset = + pickerItemFontSize - pickerLabelFontSize - 1; + const pickerAmPmVerticalOffset = + pickerItemFontSize - pickerAmPmFontSize - 1; + + // The label is absolutely positioned, so we need to offset it for it to appear + // in the correct position. We offset it to the left of the container so that + // the width of the label doesn't impact its position. + const extraLabelOffsetPercentage = customStyles?.labelOffsetPercentage ?? 8; + const baseLeftOffsetPercentage = 70; + const labelOffsetPercentage = + baseLeftOffsetPercentage + extraLabelOffsetPercentage; + + return StyleSheet.create({ pickerContainer: { flexDirection: "row", - marginRight: "8%", - backgroundColor: - customStyles?.backgroundColor ?? - (customStyles?.theme === "dark" - ? DARK_MODE_BACKGROUND_COLOR - : LIGHT_MODE_BACKGROUND_COLOR), + backgroundColor, + width: "100%", ...customStyles?.pickerContainer, }, pickerLabelContainer: { position: "absolute", - right: 4, top: 0, bottom: 0, + left: `${labelOffsetPercentage}%`, justifyContent: "center", - minWidth: - (customStyles?.pickerLabel?.fontSize ?? - customStyles?.text?.fontSize ?? - 25) * 0.65, + marginTop: pickerLabelVerticalOffset, ...customStyles?.pickerLabelContainer, }, pickerLabel: { fontSize: 18, fontWeight: "bold", - marginTop: - (customStyles?.pickerItem?.fontSize ?? - customStyles?.text?.fontSize ?? - 25) / 6, - color: - customStyles?.theme === "dark" - ? DARK_MODE_TEXT_COLOR - : LIGHT_MODE_TEXT_COLOR, + color: textColor, ...customStyles?.text, ...customStyles?.pickerLabel, }, @@ -70,36 +97,30 @@ export const generateStyles = ( height: 50, justifyContent: "center", alignItems: "center", - width: (customStyles?.pickerItem?.fontSize ?? 25) * 3.6, ...customStyles?.pickerItemContainer, }, pickerItem: { textAlignVertical: "center", fontSize: 25, - color: - customStyles?.theme === "dark" - ? DARK_MODE_TEXT_COLOR - : LIGHT_MODE_TEXT_COLOR, + overflow: "visible", + color: textColor, ...customStyles?.text, ...customStyles?.pickerItem, }, pickerAmPmContainer: { position: "absolute", - right: 0, top: 0, bottom: 0, + left: `${labelOffsetPercentage}%`, justifyContent: "center", + marginTop: pickerAmPmVerticalOffset, ...customStyles?.pickerLabelContainer, ...customStyles?.pickerAmPmContainer, }, pickerAmPmLabel: { fontSize: 18, fontWeight: "bold", - marginTop: (customStyles?.pickerItem?.fontSize ?? 25) / 6, - color: - customStyles?.theme === "dark" - ? DARK_MODE_TEXT_COLOR - : LIGHT_MODE_TEXT_COLOR, + color: textColor, ...customStyles?.text, ...customStyles?.pickerLabel, ...customStyles?.pickerAmPmLabel, @@ -121,16 +142,19 @@ export const generateStyles = ( height: "100%", ...customStyles?.pickerGradientOverlay, }, - durationScrollFlatList: { - minWidth: 1, - width: "300%", - ...customStyles?.durationScrollFlatList, - }, durationScrollFlatListContainer: { - overflow: "visible", + flex: 1, ...customStyles?.durationScrollFlatListContainer, }, + durationScrollFlatList: { + // These paddings allow the inner am/pm label to + // spill out of the flatlist + paddingRight: "25%", + marginRight: "-25%", + ...customStyles?.durationScrollFlatList, + }, durationScrollFlatListContentContainer: { ...customStyles?.durationScrollFlatListContentContainer, }, }); +}; diff --git a/src/components/TimerPickerModal/styles.ts b/src/components/TimerPickerModal/styles.ts index 1f1147a..7fb4790 100644 --- a/src/components/TimerPickerModal/styles.ts +++ b/src/components/TimerPickerModal/styles.ts @@ -1,5 +1,5 @@ import { StyleSheet } from "react-native"; -import type { TextStyle, ViewStyle } from "react-native"; +import type { DimensionValue, TextStyle, ViewStyle } from "react-native"; import type { CustomTimerPickerStyles } from "../TimerPicker/styles"; @@ -40,10 +40,6 @@ export const generateStyles = ( justifyContent: "center", overflow: "hidden", ...customContainerStyle, - // disable setting alignItems here because it can affect - // the FlatList's ability to calculate its layout, which can - // stop snapToOffsets working properly - alignItems: undefined, }, contentContainer: { backgroundColor: @@ -55,12 +51,8 @@ export const generateStyles = ( alignItems: "center", borderRadius: 20, overflow: "hidden", + paddingHorizontal: 20, ...customContentContainerStyle, - // disable setting padding here because it can affect - // the FlatList's ability to calculate its layout, which can - // stop snapToOffsets working properly - paddingHorizontal: 0, - paddingVertical: 0, }, buttonContainer: { flexDirection: "row", @@ -116,10 +108,7 @@ export const generateStyles = ( timerPickerStyles: { ...customTimerPickerStyles, pickerContainer: { - // set padding here instead of on modal content container because it can affect - // the FlatList's ability to calculate its layout, which can - // stop snapToOffsets working properly - paddingHorizontal: 20, + marginRight: "8%" as DimensionValue, paddingTop: !variables?.hasModalTitle ? 20 : 0, ...(customTimerPickerStyles?.pickerContainer ?? {}), }, From 9c8f4a0297334da9c5497dc9a8a945560826852f Mon Sep 17 00:00:00 2001 From: Tim Roberts Date: Wed, 12 Nov 2025 17:17:34 +0000 Subject: [PATCH 5/9] =?UTF-8?q?=F0=9F=A7=B5=20Update=20examples=20to=20ref?= =?UTF-8?q?lect=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/example-bare/App.tsx | 149 +++++++++++++++++++++++++--------- examples/example-expo/App.tsx | 85 ++++++------------- 2 files changed, 135 insertions(+), 99 deletions(-) diff --git a/examples/example-bare/App.tsx b/examples/example-bare/App.tsx index 6c08180..9a3bcee 100644 --- a/examples/example-bare/App.tsx +++ b/examples/example-bare/App.tsx @@ -10,6 +10,7 @@ import MaskedView from "@react-native-masked-view/masked-view"; import { LayoutAnimation, Platform, + Pressable, ScrollView, StyleSheet, Text, @@ -238,7 +239,7 @@ export default function App() { styles.page3Container, { width: screenWidth }, ]}> - + {alarmStringExample3 !== null ? "Alarm set for" : "No alarm set"} @@ -248,7 +249,7 @@ export default function App() { onPress={() => setShowPickerExample3(true)}> {alarmStringExample3 !== null ? ( - + {alarmStringExample3} ) : null} @@ -257,7 +258,7 @@ export default function App() { onPress={() => setShowPickerExample3(true)}> + style={[styles.button, styles.buttonLight]}> {"Set Alarm 🔔"} @@ -295,11 +296,7 @@ export default function App() { colors={["#202020", "#220578"]} end={{ x: 1, y: 1 }} start={{ x: 0, y: 0 }} - style={[ - styles.container, - styles.page4Container, - { width: screenWidth }, - ]}> + style={[styles.container, { width: screenWidth }]}> @@ -354,18 +345,15 @@ export default function App() { secondLabel="sec" styles={{ theme: "light", + labelOffsetPercentage: 0, pickerItem: { fontSize: 34, }, pickerLabel: { fontSize: 26, - right: -20, - }, - pickerLabelContainer: { - width: 60, }, - pickerItemContainer: { - width: 150, + pickerContainer: { + paddingHorizontal: 50, }, }} /> @@ -373,18 +361,91 @@ export default function App() { ); }, [pickerFeedback, screenWidth]); + const renderNavigationArrows = useMemo(() => { + const pageIndicesWithDarkBackground = [0, 3]; + const isDarkBackground = + pageIndicesWithDarkBackground.includes(currentPageIndex); + + const isFinalPage = currentPageIndex === 4; + const isFirstPage = currentPageIndex === 0; + + return ( + <> + {!isFinalPage ? ( + { + LayoutAnimation.configureNext( + LayoutAnimation.Presets.easeInEaseOut + ); + setCurrentPageIndex((currentPageIndex) => { + scrollViewRef.current?.scrollTo({ + x: screenWidth * (currentPageIndex + 1), + animated: true, + }); + return currentPageIndex + 1; + }); + }} + style={({ pressed }) => [ + styles.chevronPressable, + { right: 8 }, + pressed && styles.chevronPressable_pressed, + ]}> + + {"›"} + + + ) : null} + {!isFirstPage ? ( + { + LayoutAnimation.configureNext( + LayoutAnimation.Presets.easeInEaseOut + ); + setCurrentPageIndex((currentPageIndex) => { + scrollViewRef.current?.scrollTo({ + x: screenWidth * (currentPageIndex - 1), + animated: true, + }); + return currentPageIndex - 1; + }); + }} + style={({ pressed }) => [ + styles.chevronPressable, + { left: 8 }, + pressed && styles.chevronPressable_pressed, + ]}> + + {"‹"} + + + ) : null} + + ); + }, [currentPageIndex, screenWidth]); + return ( - - {renderExample1} - {renderExample2} - {renderExample3} - {renderExample4} - {renderExample5} - + <> + + {renderExample1} + {renderExample2} + {renderExample3} + {renderExample4} + {renderExample5} + + {renderNavigationArrows} + ); } @@ -402,9 +463,6 @@ const styles = StyleSheet.create({ page3Container: { backgroundColor: "#F1F1F1", }, - page4Container: { - flex: 1, - }, page5Container: { backgroundColor: "#F1F1F1", }, @@ -443,4 +501,15 @@ const styles = StyleSheet.create({ buttonContainer: { marginTop: 30, }, + chevronPressable: { + justifyContent: "center", + alignItems: "center", + position: "absolute", + top: 0, + bottom: 0, + padding: 8, + }, + chevronPressable_pressed: { + opacity: 0.7, + }, }); diff --git a/examples/example-expo/App.tsx b/examples/example-expo/App.tsx index e3d7eb3..bd7b689 100644 --- a/examples/example-expo/App.tsx +++ b/examples/example-expo/App.tsx @@ -12,13 +12,11 @@ import * as Haptics from "expo-haptics"; import { LinearGradient } from "expo-linear-gradient"; import { LayoutAnimation, - Platform, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, - UIManager, View, useWindowDimensions, } from "react-native"; @@ -31,10 +29,6 @@ import { CustomButton } from "./components/CustomButton"; import { formatTime } from "./utils/formatTime"; // import { getClickSound } from "./utils/getClickSound"; -if (Platform.OS === "android") { - UIManager.setLayoutAnimationEnabledExperimental?.(true); -} - export default function App() { const { width: screenWidth } = useWindowDimensions(); @@ -158,9 +152,7 @@ export default function App() { setShowPickerExample1(false)} onConfirm={(pickedDuration) => { @@ -169,9 +161,7 @@ export default function App() { }} pickerFeedback={pickerFeedback} setIsVisible={setShowPickerExample1} - styles={{ - theme: "dark", - }} + styles={{ theme: "dark" }} visible={showPickerExample1} /> @@ -223,9 +213,7 @@ export default function App() { }} pickerFeedback={pickerFeedback} setIsVisible={setShowPickerExample2} - styles={{ - theme: "light", - }} + styles={{ theme: "light" }} use12HourPicker visible={showPickerExample2} /> @@ -272,9 +260,7 @@ export default function App() { closeOnOverlayPress confirmButton={} LinearGradient={LinearGradient} - modalProps={{ - overlayOpacity: 0.2, - }} + modalProps={{ overlayOpacity: 0.2 }} modalTitle="Set Alarm" onCancel={() => setShowPickerExample3(false)} onConfirm={(pickedDuration) => { @@ -283,9 +269,7 @@ export default function App() { }} pickerFeedback={pickerFeedback} setIsVisible={setShowPickerExample3} - styles={{ - theme: "dark", - }} + styles={{ theme: "dark" }} visible={showPickerExample3} /> @@ -298,11 +282,7 @@ export default function App() { colors={["#202020", "#220578"]} end={{ x: 1, y: 1 }} start={{ x: 0, y: 0 }} - style={[ - styles.container, - styles.page4Container, - { width: screenWidth }, - ]}> + style={[styles.container, { width: screenWidth }]}> @@ -357,18 +331,15 @@ export default function App() { secondLabel="sec" styles={{ theme: "light", + labelOffsetPercentage: 0, pickerItem: { fontSize: 34, }, pickerLabel: { fontSize: 26, - right: -20, - }, - pickerLabelContainer: { - width: 60, }, - pickerItemContainer: { - width: 150, + pickerContainer: { + paddingHorizontal: 50, }, }} /> @@ -377,9 +348,16 @@ export default function App() { }, [pickerFeedback, screenWidth]); const renderNavigationArrows = useMemo(() => { + const pageIndicesWithDarkBackground = [0, 3]; + const isDarkBackground = + pageIndicesWithDarkBackground.includes(currentPageIndex); + + const isFinalPage = currentPageIndex === 4; + const isFirstPage = currentPageIndex === 0; + return ( <> - {currentPageIndex !== 4 ? ( + {!isFinalPage ? ( { LayoutAnimation.configureNext( @@ -399,17 +377,13 @@ export default function App() { pressed && styles.chevronPressable_pressed, ]}> ) : null} - {currentPageIndex !== 0 ? ( + {!isFirstPage ? ( { LayoutAnimation.configureNext( @@ -429,11 +403,7 @@ export default function App() { pressed && styles.chevronPressable_pressed, ]}> @@ -475,9 +445,6 @@ const styles = StyleSheet.create({ page3Container: { backgroundColor: "#F1F1F1", }, - page4Container: { - flex: 1, - }, page5Container: { backgroundColor: "#F1F1F1", }, From 025b90fb3d2e8201617d91d8549e78457fc2b87c Mon Sep 17 00:00:00 2001 From: Tim Roberts Date: Wed, 12 Nov 2025 19:13:11 +0000 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=93=9D=20Update=20docs=20to=20reflect?= =?UTF-8?q?=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 203 ++++++++++++++++++++++++++---------------------------- 1 file changed, 97 insertions(+), 106 deletions(-) diff --git a/README.md b/README.md index ac6001c..7ce2e87 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![license](https://img.shields.io/github/license/mashape/apistatus.svg?style=for-the-badge)]() ![platforms](https://img.shields.io/badge/platforms-Android%20%7C%20iOS-brightgreen.svg?style=for-the-badge&colorB=191A17) [![Version](https://img.shields.io/npm/v/react-native-timer-picker.svg?style=for-the-badge)](https://www.npmjs.com/package/react-native-timer-picker) - +[![npm](https://img.shields.io/npm/dt/react-native-timer-picker.svg?style=for-the-badge&cacheSeconds=86400)](https://www.npmjs.com/package/react-native-timer-picker) A simple, flexible, performant duration picker component for React Native apps 🔥 @@ -13,37 +13,37 @@ Works with Expo and bare React Native apps ✅ Includes iOS-style haptic and audio feedback 🍏 -- [Demos 📱](#demos-) -- [Installation 🚀](#installation-) -- [Peer Dependencies 👶](#peer-dependencies-) - - [Linear Gradient](#linear-gradient) - - [Masked View](#masked-view) -- [Examples 😎](#examples-) -- [Timer Picker Modal (Dark Mode) 🌚](#timer-picker-modal-dark-mode-) -- [Timer Picker Modal (Light Mode) 🌞](#timer-picker-modal-light-mode-) -- [Timer Picker Modal with Custom Buttons 🎨](#timer-picker-modal-with-custom-buttons-) -- [Timer Picker with Transparent Fade-Out (Dark Mode) 🌒](#timer-picker-with-transparent-fade-out-dark-mode-) -- [Timer Picker with Customisation (Light Mode) 🌔](#timer-picker-with-customisation-light-mode-) -- [Props 💅](#props-) -- [TimerPicker ⏲️](#timerpicker-️) - - [Custom Styles 👗](#custom-styles-) - - [Performance](#performance) - - [Custom FlatList](#custom-flatlist) -- [TimerPickerModal ⏰](#timerpickermodal-) - - [Custom Styles 👕](#custom-styles--1) -- [Methods 🔄](#methods-) -- [TimerPicker](#timerpicker) -- [TimerPickerModal](#timerpickermodal) -- [Picker Feedback 📳🔉](#picker-feedback-) -- [Audio Feedack](#audio-feedack) -- [Haptic Feedback](#haptic-feedback) -- [Feedback Example](#feedback-example) -- [Expo-Specific Audio/Haptic Feedback (DEPRECATED)](#expo-specific-audiohaptic-feedback-deprecated) -- [Contributing 🧑‍🤝‍🧑](#contributing-) -- [Dev Setup](#dev-setup) -- [GitHub Guidelines](#github-guidelines) -- [Limitations ⚠](#limitations-) -- [License 📝](#license-) + - [Demos 📱](#demos-) + - [Installation 🚀](#installation-) + - [Peer Dependencies 👶](#peer-dependencies-) + - [Linear Gradient](#linear-gradient) + - [Masked View](#masked-view) + - [Examples 😎](#examples-) + - [Timer Picker Modal (Dark Mode) 🌚](#timer-picker-modal-dark-mode-) + - [Timer Picker Modal (Light Mode) 🌞](#timer-picker-modal-light-mode-) + - [Timer Picker Modal with Custom Buttons 🎨](#timer-picker-modal-with-custom-buttons-) + - [Timer Picker with Transparent Fade-Out (Dark Mode) 🌒](#timer-picker-with-transparent-fade-out-dark-mode-) + - [Timer Picker with Customisation (Light Mode) 🌔](#timer-picker-with-customisation-light-mode-) + - [Props 💅](#props-) + - [TimerPicker ⏲️](#timerpicker-️) + - [Custom Styles 👗](#custom-styles-) + - [Performance](#performance) + - [Custom FlatList](#custom-flatlist) + - [TimerPickerModal ⏰](#timerpickermodal-) + - [Custom Styles 👕](#custom-styles--1) + - [Methods 🔄](#methods-) + - [TimerPicker](#timerpicker) + - [TimerPickerModal](#timerpickermodal) + - [Picker Feedback 📳🔉](#picker-feedback-) + - [Audio Feedack](#audio-feedack) + - [Haptic Feedback](#haptic-feedback) + - [Feedback Example](#feedback-example) + - [Expo-Specific Audio/Haptic Feedback (DEPRECATED)](#expo-specific-audiohaptic-feedback-deprecated) + - [Contributing 🧑‍🤝‍🧑](#contributing-) + - [Dev Setup](#dev-setup) + - [GitHub Guidelines](#github-guidelines) + - [Limitations ⚠](#limitations-) + - [License 📝](#license-)
@@ -99,7 +99,7 @@ To make the numbers fade in/out on a transparent background (e.g. if the picker `import MaskedView from "@react-native-masked-view/masked-view";` -**To enable the fade-out on a transparent background, you need to supply the imported `MaskedView` component AND one of the LinearGradient components as props to either TimerPickerModal or TimerPicker. (see [this example](#timer-picker-with-transparent-fade-out-dark-mode-))** +**To enable the fade-out on a transparent background, you need to supply the imported `MaskedView` component AND one of the LinearGradient components as props to either TimerPickerModal or TimerPicker (see [this example](#timer-picker-with-transparent-fade-out-dark-mode-)).**
@@ -146,7 +146,7 @@ const formatTime = ({ return ( - {alarmStringExample !== null + {alarmString !== null ? "Alarm set for" : "No alarm set"} @@ -181,22 +181,22 @@ return ( setShowPicker(false)} onConfirm={(pickedDuration) => { setAlarmString(formatTime(pickedDuration)); setShowPicker(false); }} - modalTitle="Set Alarm" - onCancel={() => setShowPicker(false)} - closeOnOverlayPress - LinearGradient={LinearGradient} + setIsVisible={setShowPicker} styles={{ theme: "dark", }} - modalProps={{ - overlayOpacity: 0.2, - }} + visible={showPicker} /> ) @@ -244,7 +244,7 @@ const formatTime = ({ return ( - {alarmStringExample !== null + {alarmString !== null ? "Alarm set for" : "No alarm set"} @@ -271,27 +271,27 @@ return ( borderColor: "#8C8C8C", color: "#8C8C8C" }}> - Set Alarm 🔔 + {"Set Alarm 🔔"}
setShowPicker(false)} onConfirm={(pickedDuration) => { setAlarmString(formatTime(pickedDuration)); setShowPicker(false); }} - modalTitle="Set Alarm" - onCancel={() => setShowPicker(false)} - closeOnOverlayPress - use12HourPicker - LinearGradient={LinearGradient} + setIsVisible={setShowPicker} styles={{ theme: "light", }} + use12HourPicker + visible={showPicker} /> ) @@ -305,15 +305,15 @@ return ( ```jsx import { TimerPickerModal } from "react-native-timer-picker"; import { LinearGradient } from "expo-linear-gradient"; // or `import LinearGradient from "react-native-linear-gradient"` -import { TouchableOpacity, Text, StyleSheet, Platform } from "react-native"; +import { TouchableOpacity, Text, StyleSheet } from "react-native"; // Custom Button Component -interface MyCustomButtonProps { +interface CustomButtonProps { label: string; onPress?: () => void; } -const MyCustomButton: React.FC = ({ label, onPress }) => { +const CustomButton: React.FC = ({ label, onPress }) => { return ( } + closeOnOverlayPress + confirmButton={} + LinearGradient={LinearGradient} + modalProps={{ + overlayOpacity: 0.2, + }} + modalTitle="Set Alarm" + onCancel={() => setShowPicker(false)} onConfirm={(pickedDuration) => { setAlarmString(formatTime(pickedDuration)); setShowPicker(false); }} - modalTitle="Set Alarm" - onCancel={() => setShowPicker(false)} - closeOnOverlayPress - use12HourPicker - LinearGradient={LinearGradient} - // Custom buttons - cancelButton={} - confirmButton={} + setIsVisible={setShowPicker} styles={{ - theme: "light", + theme: "dark", }} + visible={showPicker} /> ) @@ -443,46 +444,36 @@ import MaskedView from "@react-native-masked-view/masked-view"; // for transpare import { LinearGradient } from "expo-linear-gradient"; // or `import LinearGradient from "react-native-linear-gradient"` .... -const [showPicker, setShowPicker] = useState(false); -const [alarmString, setAlarmString] = useState< - string | null - >(null); return ( @@ -500,33 +491,26 @@ import { TimerPicker } from "react-native-timer-picker"; import { LinearGradient } from "expo-linear-gradient"; // or `import LinearGradient from "react-native-linear-gradient"` .... -const [showPicker, setShowPicker] = useState(false); -const [alarmString, setAlarmString] = useState< - string | null - >(null); return ( @@ -596,15 +580,21 @@ return ( | styles | Custom styles for the timer picker | [CustomTimerPickerStyles](#custom-styles-) | - | false | | decelerationRate | Set how quickly the picker decelerates after the user lifts their finger | 'fast', 'normal', or Number | 0.88 | false | -#### Custom Styles 👗 +#### Custom Styles 👗 -The following custom styles can be supplied to re-style the component in any way. Various styles are applied by default - you can take a look at these [here](src/components/TimerPicker/styles.ts). +The component should look good straight out of the box, but you can use these styles to make it fit in with your App's theme: | Style Prop | Description | Type | | :------------------------------------: | :------------------------------------------------------------------- | :--------------------------------------: | | theme | Theme of the component | "light" \| "dark" | | backgroundColor | Main background color | string | | text | Base text style | TextStyle | +| labelOffsetPercentage | Percentage offset for horizonal label positioning relative to the picker | number | + +For deeper style customization, you can supply the following custom styles to adjust the component in any way. These are applied on top of the default styling so take a look at those [styles](src/components/TimerPicker/styles.ts) if something isn't adjusting in the way you'd expect. + +| Style Prop | Description | Type | +| :------------------------------------: | :------------------------------------------------------------------- | :--------------------------------------: | | pickerContainer | Main container for the picker | ViewStyle & { backgroundColor?: string } | | pickerLabelContainer | Container for the picker's labels | ViewStyle | | pickerLabel | Style for the picker's labels | TextStyle | @@ -619,7 +609,7 @@ The following custom styles can be supplied to re-style the component in any way | durationScrollFlatListContainer | Style for the View that contains the Flatlist in each picker | ViewStyle | | durationScrollFlatListContentContainer | Style for the Flatlist's `contentContainerStyle` prop in each picker | ViewStyle | -Note the minor limitations to the allowed styles for `pickerContainer` and `pickerItemContainer`. These are made because these styles are used for internal calculations and all possible `backgroundColor`/`height` types are not supported. +**Note:** There are minor limitations on `pickerContainer.backgroundColor` and `pickerItemContainer.height`. These properties must be simple values (string and number respectively) as they are used in internal calculations for scroll positioning, gradient overlays, and snap behavior. Complex computed values or union types are not supported for these specific properties. #### Performance @@ -680,17 +670,18 @@ The TimerPickerModal component accepts all [TimerPicker props](#timerpicker-️) #### Custom Styles 👕 -The following custom styles can be supplied to re-style the component in any way. You can also supply all of the styles specified in [CustomTimerPickerStyles](#custom-styles-). Various styles are applied by default - you can take a look at these [here](src/components/TimerPickerModal/styles.ts). +The following custom styles can be supplied to re-style the component in any way. You can also supply all of the styles specified in [CustomTimerPickerStyles](#custom-styles-). These are applied on top of the default styling so take a look at those [styles](src/components/TimerPickerModal/styles.ts) if something isn't adjusting in the way you'd expect. | Style Prop | Description | Type | | :--------------: | :----------------------------------------- | :-------: | -| container | Main container's style | ViewStyle | -| contentContainer | Style for the content's container | ViewStyle | -| buttonContainer | Style for the container around the buttons | ViewStyle | +| container | Style for the modal container | ViewStyle | +| contentContainer | Style for the modal content's container | ViewStyle | +| buttonContainer | Style for the container for the buttons | ViewStyle | | button | General style for both buttons | TextStyle | | cancelButton | Style for the cancel button | TextStyle | | confirmButton | Style for the confirm button | TextStyle | | modalTitle | Style for the title of the modal | TextStyle | +| ... | Supply any of [TimerPicker's custom styles]((#custom-styles-)) | - |
From 11f5fd8715f93578040a94d568304d5dd5be6bac Mon Sep 17 00:00:00 2001 From: Tim Roberts Date: Sat, 13 Dec 2025 18:54:31 +0000 Subject: [PATCH 7/9] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20expo=20examp?= =?UTF-8?q?le=20deps=20and=20bump=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/example-bare/App.tsx | 96 +----- examples/example-expo/package.json | 10 +- package.json | 2 +- yarn.lock | 518 ++++++++++++++++------------- 4 files changed, 297 insertions(+), 329 deletions(-) diff --git a/examples/example-bare/App.tsx b/examples/example-bare/App.tsx index 9a3bcee..3fa18b9 100644 --- a/examples/example-bare/App.tsx +++ b/examples/example-bare/App.tsx @@ -10,7 +10,6 @@ import MaskedView from "@react-native-masked-view/masked-view"; import { LayoutAnimation, Platform, - Pressable, ScrollView, StyleSheet, Text, @@ -361,91 +360,18 @@ export default function App() { ); }, [pickerFeedback, screenWidth]); - const renderNavigationArrows = useMemo(() => { - const pageIndicesWithDarkBackground = [0, 3]; - const isDarkBackground = - pageIndicesWithDarkBackground.includes(currentPageIndex); - - const isFinalPage = currentPageIndex === 4; - const isFirstPage = currentPageIndex === 0; - - return ( - <> - {!isFinalPage ? ( - { - LayoutAnimation.configureNext( - LayoutAnimation.Presets.easeInEaseOut - ); - setCurrentPageIndex((currentPageIndex) => { - scrollViewRef.current?.scrollTo({ - x: screenWidth * (currentPageIndex + 1), - animated: true, - }); - return currentPageIndex + 1; - }); - }} - style={({ pressed }) => [ - styles.chevronPressable, - { right: 8 }, - pressed && styles.chevronPressable_pressed, - ]}> - - {"›"} - - - ) : null} - {!isFirstPage ? ( - { - LayoutAnimation.configureNext( - LayoutAnimation.Presets.easeInEaseOut - ); - setCurrentPageIndex((currentPageIndex) => { - scrollViewRef.current?.scrollTo({ - x: screenWidth * (currentPageIndex - 1), - animated: true, - }); - return currentPageIndex - 1; - }); - }} - style={({ pressed }) => [ - styles.chevronPressable, - { left: 8 }, - pressed && styles.chevronPressable_pressed, - ]}> - - {"‹"} - - - ) : null} - - ); - }, [currentPageIndex, screenWidth]); - return ( - <> - - {renderExample1} - {renderExample2} - {renderExample3} - {renderExample4} - {renderExample5} - - {renderNavigationArrows} - + + {renderExample1} + {renderExample2} + {renderExample3} + {renderExample4} + {renderExample5} + ); } diff --git a/examples/example-expo/package.json b/examples/example-expo/package.json index 06cf3e5..69a06f9 100644 --- a/examples/example-expo/package.json +++ b/examples/example-expo/package.json @@ -13,11 +13,11 @@ "dependencies": { "@expo/vector-icons": "^15.0.3", "@react-native-masked-view/masked-view": "0.3.2", - "expo": "54.0.23", - "expo-asset": "~12.0.9", - "expo-font": "~14.0.9", - "expo-haptics": "~15.0.7", - "expo-linear-gradient": "~15.0.7", + "expo": "~54.0.29", + "expo-asset": "~12.0.11", + "expo-font": "~14.0.10", + "expo-haptics": "~15.0.8", + "expo-linear-gradient": "~15.0.8", "react": "19.1.0", "react-native": "0.81.5", "react-native-audio-api": "0.9.0" diff --git a/package.json b/package.json index d2cda29..583f4ec 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "url": "https://github.com/troberts-28" }, "license": "MIT", - "version": "2.3.1", + "version": "2.4.0", "main": "dist/commonjs/index.js", "module": "dist/module/index.js", "types": "dist/typescript/index.d.ts", diff --git a/yarn.lock b/yarn.lock index 0132c0a..385b6f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1831,26 +1831,25 @@ __metadata: languageName: node linkType: hard -"@expo/cli@npm:54.0.16": - version: 54.0.16 - resolution: "@expo/cli@npm:54.0.16" +"@expo/cli@npm:54.0.19": + version: 54.0.19 + resolution: "@expo/cli@npm:54.0.19" dependencies: "@0no-co/graphql.web": "npm:^1.0.8" "@expo/code-signing-certificates": "npm:^0.0.5" - "@expo/config": "npm:~12.0.10" - "@expo/config-plugins": "npm:~54.0.2" - "@expo/devcert": "npm:^1.1.2" - "@expo/env": "npm:~2.0.7" - "@expo/image-utils": "npm:^0.8.7" - "@expo/json-file": "npm:^10.0.7" - "@expo/mcp-tunnel": "npm:~0.1.0" + "@expo/config": "npm:~12.0.12" + "@expo/config-plugins": "npm:~54.0.3" + "@expo/devcert": "npm:^1.2.1" + "@expo/env": "npm:~2.0.8" + "@expo/image-utils": "npm:^0.8.8" + "@expo/json-file": "npm:^10.0.8" "@expo/metro": "npm:~54.1.0" - "@expo/metro-config": "npm:~54.0.9" - "@expo/osascript": "npm:^2.3.7" - "@expo/package-manager": "npm:^1.9.8" - "@expo/plist": "npm:^0.4.7" - "@expo/prebuild-config": "npm:^54.0.6" - "@expo/schema-utils": "npm:^0.1.7" + "@expo/metro-config": "npm:~54.0.11" + "@expo/osascript": "npm:^2.3.8" + "@expo/package-manager": "npm:^1.9.9" + "@expo/plist": "npm:^0.4.8" + "@expo/prebuild-config": "npm:^54.0.7" + "@expo/schema-utils": "npm:^0.1.8" "@expo/spawn-async": "npm:^1.7.2" "@expo/ws-tunnel": "npm:^1.0.1" "@expo/xcpretty": "npm:^4.3.0" @@ -1868,10 +1867,10 @@ __metadata: connect: "npm:^3.7.0" debug: "npm:^4.3.4" env-editor: "npm:^0.4.1" - expo-server: "npm:^1.0.4" + expo-server: "npm:^1.0.5" freeport-async: "npm:^2.0.0" getenv: "npm:^2.0.0" - glob: "npm:^10.4.2" + glob: "npm:^13.0.0" lan-network: "npm:^0.1.6" minimatch: "npm:^9.0.0" node-forge: "npm:^1.3.1" @@ -1894,7 +1893,7 @@ __metadata: source-map-support: "npm:~0.5.21" stacktrace-parser: "npm:^0.1.10" structured-headers: "npm:^0.4.1" - tar: "npm:^7.4.3" + tar: "npm:^7.5.2" terminal-link: "npm:^2.1.1" undici: "npm:^6.18.2" wrap-ansi: "npm:^7.0.0" @@ -1910,7 +1909,7 @@ __metadata: optional: true bin: expo-internal: build/bin/cli - checksum: 10c0/7921ecf138409dd145e0f906b320b9b2ef569426918bd3b6eadf42af2bd856013ff7ebb050212d4ccb98fa7f13272ca435318929007ceae7386874f294dc5f9b + checksum: 10c0/a5db332daf83060dee3ec70bdc8a19022de9fa9ada5c01fd768df09ff2383f9738e14cd84b7e0c7777dfb379c955217e402bae37cc3cb181e1e45f2bc352fff9 languageName: node linkType: hard @@ -1924,70 +1923,69 @@ __metadata: languageName: node linkType: hard -"@expo/config-plugins@npm:~54.0.2": - version: 54.0.2 - resolution: "@expo/config-plugins@npm:54.0.2" +"@expo/config-plugins@npm:~54.0.3, @expo/config-plugins@npm:~54.0.4": + version: 54.0.4 + resolution: "@expo/config-plugins@npm:54.0.4" dependencies: - "@expo/config-types": "npm:^54.0.8" - "@expo/json-file": "npm:~10.0.7" - "@expo/plist": "npm:^0.4.7" + "@expo/config-types": "npm:^54.0.10" + "@expo/json-file": "npm:~10.0.8" + "@expo/plist": "npm:^0.4.8" "@expo/sdk-runtime-versions": "npm:^1.0.0" chalk: "npm:^4.1.2" debug: "npm:^4.3.5" getenv: "npm:^2.0.0" - glob: "npm:^10.4.2" + glob: "npm:^13.0.0" resolve-from: "npm:^5.0.0" semver: "npm:^7.5.4" slash: "npm:^3.0.0" slugify: "npm:^1.6.6" xcode: "npm:^3.0.1" xml2js: "npm:0.6.0" - checksum: 10c0/99f303c82a80b248ddddd2b5d2e638a83a7865f82b673cf1ff4788cdce8b835f4528e4ebca06612a7fd3aa962b3d748f63de370c9f95f78cb711eee1c6e4c331 + checksum: 10c0/c7537485a0e883d8a98f1fb93335a1f56d4be2c2a4b5676ba09a8e9253190996241022f841c437e64578fa63b20b6ecf843d88b52930b890fa199d7aa188253f languageName: node linkType: hard -"@expo/config-types@npm:^54.0.8": - version: 54.0.8 - resolution: "@expo/config-types@npm:54.0.8" - checksum: 10c0/8ea03fe4b18277b76d40bcd4d64a247013a2a24669e021f489f5b5a4abc06b753a9545d072c3eb12b8946cc51ad99f1c92734b94fef2c151a493a1ca78bdbf84 +"@expo/config-types@npm:^54.0.10, @expo/config-types@npm:^54.0.9": + version: 54.0.10 + resolution: "@expo/config-types@npm:54.0.10" + checksum: 10c0/a304e18314937cbe3a146fe7daf23d5b78049676dabc14b1e181330f9e74ab2f4ada288f23999f254b59ee7c59380f895ffcb536f537e9039cd10336b1c1d7bc languageName: node linkType: hard -"@expo/config@npm:~12.0.10, @expo/config@npm:~12.0.9": - version: 12.0.10 - resolution: "@expo/config@npm:12.0.10" +"@expo/config@npm:~12.0.11, @expo/config@npm:~12.0.12": + version: 12.0.12 + resolution: "@expo/config@npm:12.0.12" dependencies: "@babel/code-frame": "npm:~7.10.4" - "@expo/config-plugins": "npm:~54.0.2" - "@expo/config-types": "npm:^54.0.8" - "@expo/json-file": "npm:^10.0.7" + "@expo/config-plugins": "npm:~54.0.3" + "@expo/config-types": "npm:^54.0.10" + "@expo/json-file": "npm:^10.0.8" deepmerge: "npm:^4.3.1" getenv: "npm:^2.0.0" - glob: "npm:^10.4.2" + glob: "npm:^13.0.0" require-from-string: "npm:^2.0.2" resolve-from: "npm:^5.0.0" resolve-workspace-root: "npm:^2.0.0" semver: "npm:^7.6.0" slugify: "npm:^1.3.4" - sucrase: "npm:3.35.0" - checksum: 10c0/e438cbab74b12e0b0edb4c3434aef2e8b26f57e5cc1573998533cd102f502ab63836decabd93929756e9220795ecd9daf907ac6f7ec221fc7933d20b27825283 + sucrase: "npm:~3.35.1" + checksum: 10c0/4094af9815a1c594a80fd4663db12768840350b03136e5500a7d0103c348cfe6ebe90b89bfe7ca3913293c49f9c6fa85a4b8f87a2154492e415c46e7a8cbdbb4 languageName: node linkType: hard -"@expo/devcert@npm:^1.1.2": - version: 1.2.0 - resolution: "@expo/devcert@npm:1.2.0" +"@expo/devcert@npm:^1.2.1": + version: 1.2.1 + resolution: "@expo/devcert@npm:1.2.1" dependencies: "@expo/sudo-prompt": "npm:^9.3.1" debug: "npm:^3.1.0" - glob: "npm:^10.4.2" - checksum: 10c0/3d6a1ce44918c2e5be3bb89d25cfc80551623e4fe5004d4eb29d1edc8edd676258345e64d2aefe56188bc5d4b33e2b7e733a108b2be225af1f90ca86d7170069 + checksum: 10c0/7c5cb4fa74a14702a44b4772a56f27fd191b6cd08988f3da01323f6d592623c80247171b7d66b2c0a32408f48a0814162dbb2764042444887f27e38b89ad1051 languageName: node linkType: hard -"@expo/devtools@npm:0.1.7": - version: 0.1.7 - resolution: "@expo/devtools@npm:0.1.7" +"@expo/devtools@npm:0.1.8": + version: 0.1.8 + resolution: "@expo/devtools@npm:0.1.8" dependencies: chalk: "npm:^4.1.2" peerDependencies: @@ -1998,33 +1996,33 @@ __metadata: optional: true react-native: optional: true - checksum: 10c0/4525a007db0b3c89d7e0400f8ec9ede679d0ee110e572c9ba12e8430d564b7c9967031b0f316f0379bb5310b9b1b78adccd659cfca904effaa27706d15325fe8 + checksum: 10c0/dc4e095e5f4508370ae2258f23370a295b9400c87f29aee2338caa3ca3733d789ba3ff1bfafbf5fa285ac6974aec89b3cbf363fca5885eb9be3973ac1a7d7fa8 languageName: node linkType: hard -"@expo/env@npm:~2.0.7": - version: 2.0.7 - resolution: "@expo/env@npm:2.0.7" +"@expo/env@npm:~2.0.8": + version: 2.0.8 + resolution: "@expo/env@npm:2.0.8" dependencies: chalk: "npm:^4.0.0" debug: "npm:^4.3.4" dotenv: "npm:~16.4.5" dotenv-expand: "npm:~11.0.6" getenv: "npm:^2.0.0" - checksum: 10c0/029914cfca2f85dd2c159b7331492d26255db7cf1615d02108041f561f3e7286d77f473943dd1d6ede68ee3cea8b63271c8352fa9b728c90e020f50d6d8c5582 + checksum: 10c0/e6e4cd1be45a6873f5ad4a6a2176957897159b08104f04ea8f28a9d9f38abe0d109ded76ac197c0edba1ac606c14a034be19cfda75b44111be85b78a2d209375 languageName: node linkType: hard -"@expo/fingerprint@npm:0.15.3": - version: 0.15.3 - resolution: "@expo/fingerprint@npm:0.15.3" +"@expo/fingerprint@npm:0.15.4": + version: 0.15.4 + resolution: "@expo/fingerprint@npm:0.15.4" dependencies: "@expo/spawn-async": "npm:^1.7.2" arg: "npm:^5.0.2" chalk: "npm:^4.1.2" debug: "npm:^4.3.4" getenv: "npm:^2.0.0" - glob: "npm:^10.4.2" + glob: "npm:^13.0.0" ignore: "npm:^5.3.1" minimatch: "npm:^9.0.0" p-limit: "npm:^3.1.0" @@ -2032,13 +2030,13 @@ __metadata: semver: "npm:^7.6.0" bin: fingerprint: bin/cli.js - checksum: 10c0/bbb3a568987f976c44053f83f29ed86f0d18d4e5e9ac649525bae4657014e86b3f184e80c6dc3baa95b017d8e37c50eafae0f926fe14f2e371243a3edc741a45 + checksum: 10c0/fe5aa9eff4f649cc58aa0b67cd6b93845ddc81b49a61f03c50632405bf2b99fc83ecbb15c527bfddc0156f8fad297f066dd46edf4cfa17580025fb192d7a98ee languageName: node linkType: hard -"@expo/image-utils@npm:^0.8.7": - version: 0.8.7 - resolution: "@expo/image-utils@npm:0.8.7" +"@expo/image-utils@npm:^0.8.8": + version: 0.8.8 + resolution: "@expo/image-utils@npm:0.8.8" dependencies: "@expo/spawn-async": "npm:^1.7.2" chalk: "npm:^4.0.0" @@ -2050,46 +2048,30 @@ __metadata: semver: "npm:^7.6.0" temp-dir: "npm:~2.0.0" unique-string: "npm:~2.0.0" - checksum: 10c0/763fbe6d5e34c6e40b74f1088dd5a3bf8cf54a57b073ec55230a8c2f87deb9204a79ce3750d40745c330cb438e50d854d0177a7f7d19c2055770687ef243660e + checksum: 10c0/d08a93036d865b52d1a4848597bd872192c7023d415f62ed744e9ff20fd9e68308ade06b39c25b0eabbc04745ffa68c4b5a8d79883adb0c32738e1a414d7d62e languageName: node linkType: hard -"@expo/json-file@npm:^10.0.7, @expo/json-file@npm:~10.0.7": - version: 10.0.7 - resolution: "@expo/json-file@npm:10.0.7" +"@expo/json-file@npm:^10.0.8, @expo/json-file@npm:~10.0.8": + version: 10.0.8 + resolution: "@expo/json-file@npm:10.0.8" dependencies: "@babel/code-frame": "npm:~7.10.4" json5: "npm:^2.2.3" - checksum: 10c0/3dfff7fe435d286f6c2f55569a8667f6d52133fc96a263e7421fa49cbf2ad7a4e2952da1fa7a3cdb15c52f11e891335ec784d358c3be554f966fdf5c836cc944 - languageName: node - linkType: hard - -"@expo/mcp-tunnel@npm:~0.1.0": - version: 0.1.0 - resolution: "@expo/mcp-tunnel@npm:0.1.0" - dependencies: - ws: "npm:^8.18.3" - zod: "npm:^3.25.76" - zod-to-json-schema: "npm:^3.24.6" - peerDependencies: - "@modelcontextprotocol/sdk": ^1.13.2 - peerDependenciesMeta: - "@modelcontextprotocol/sdk": - optional: true - checksum: 10c0/db1241d159875c7e8bcc72c121e6b3a287794809c1bbf7781b61bd15673b3a2dd36e9fa6886893ff20d1aea399440f3abafcdf10a955ad9f410750d757ee8158 + checksum: 10c0/dddd7ef7966fe0d29d1cad5a1d517d01c4e6a7b55cd15e901b947e7832d613aa1b982de5336d1305a3714bb4b5e3d78110c0ff799ca33fd54a8cd887f8ba533d languageName: node linkType: hard -"@expo/metro-config@npm:54.0.9, @expo/metro-config@npm:~54.0.9": - version: 54.0.9 - resolution: "@expo/metro-config@npm:54.0.9" +"@expo/metro-config@npm:54.0.11, @expo/metro-config@npm:~54.0.11": + version: 54.0.11 + resolution: "@expo/metro-config@npm:54.0.11" dependencies: "@babel/code-frame": "npm:^7.20.0" "@babel/core": "npm:^7.20.0" "@babel/generator": "npm:^7.20.5" - "@expo/config": "npm:~12.0.10" - "@expo/env": "npm:~2.0.7" - "@expo/json-file": "npm:~10.0.7" + "@expo/config": "npm:~12.0.12" + "@expo/env": "npm:~2.0.8" + "@expo/json-file": "npm:~10.0.8" "@expo/metro": "npm:~54.1.0" "@expo/spawn-async": "npm:^1.7.2" browserslist: "npm:^4.25.0" @@ -2098,7 +2080,7 @@ __metadata: dotenv: "npm:~16.4.5" dotenv-expand: "npm:~11.0.6" getenv: "npm:^2.0.0" - glob: "npm:^10.4.2" + glob: "npm:^13.0.0" hermes-parser: "npm:^0.29.1" jsc-safe-url: "npm:^0.2.4" lightningcss: "npm:^1.30.1" @@ -2110,7 +2092,7 @@ __metadata: peerDependenciesMeta: expo: optional: true - checksum: 10c0/6833c594082a347ba6df0ad6d107c6b8f21363a06b4f5e4ffbac91bbfc14cf1d62b2fbb335bb146dddcad8bd53d7a09920fe928cc7312f9e50bd9138b2d4aacc + checksum: 10c0/be2774a33366607a28d9dc5a19721fc4eb50f1d84fe68d9a33ab41611cc9186e60e340c24c8b4646403995a86e4dc90b4d21653b3388181f146ff1a22b641d70 languageName: node linkType: hard @@ -2134,50 +2116,50 @@ __metadata: languageName: node linkType: hard -"@expo/osascript@npm:^2.3.7": - version: 2.3.7 - resolution: "@expo/osascript@npm:2.3.7" +"@expo/osascript@npm:^2.3.8": + version: 2.3.8 + resolution: "@expo/osascript@npm:2.3.8" dependencies: "@expo/spawn-async": "npm:^1.7.2" exec-async: "npm:^2.2.0" - checksum: 10c0/7778120019f3969e68e2473d8a75e35b03e1b8f573a6d306603b9007953595b28ef042eaf16580f00c48b063fdc808f7a8a6cfd302fedcbe149bd1a3e44c84c9 + checksum: 10c0/5991c363fb33fb1f740eb8bfdb7ccafd1944a6d89a644ec20dd76b546ffa995a3963727150155391287536046f5f47c7efae003a27a86299224d55191e2f65cb languageName: node linkType: hard -"@expo/package-manager@npm:^1.9.8": - version: 1.9.8 - resolution: "@expo/package-manager@npm:1.9.8" +"@expo/package-manager@npm:^1.9.9": + version: 1.9.9 + resolution: "@expo/package-manager@npm:1.9.9" dependencies: - "@expo/json-file": "npm:^10.0.7" + "@expo/json-file": "npm:^10.0.8" "@expo/spawn-async": "npm:^1.7.2" chalk: "npm:^4.0.0" npm-package-arg: "npm:^11.0.0" ora: "npm:^3.4.0" resolve-workspace-root: "npm:^2.0.0" - checksum: 10c0/d9f727a9b02a13d7fac8afccc5608f46690ca3e27f834042e74bc271607a5a085ad4600a19eeda55556251b8f1f0960cf71609680df7fedf76750141cb0d2a9e + checksum: 10c0/f822b49f4b6c6b8e4d95b7d136a515131f85b145fe6bc15eef42ef2c2890474b5bba913d03c098c614373be2e5e85afa24ea3ca237dfd0ec077bdd4b6c5248cc languageName: node linkType: hard -"@expo/plist@npm:^0.4.7": - version: 0.4.7 - resolution: "@expo/plist@npm:0.4.7" +"@expo/plist@npm:^0.4.8": + version: 0.4.8 + resolution: "@expo/plist@npm:0.4.8" dependencies: "@xmldom/xmldom": "npm:^0.8.8" base64-js: "npm:^1.2.3" xmlbuilder: "npm:^15.1.1" - checksum: 10c0/697b3845e7898516de4d25ac28ae9fcbf1e58612bd543b41e11b3d41b9a52b754f2775dc2891cd3068a88f0375a77c2805073c12c5d54f3cd7ef6d315d0a3b92 + checksum: 10c0/5bacdb6f8c5e0e56da07f4504290036e3a5433164a29bea7857e72234137d8eaa04adb319221fcc1ec7f931d40d7f9f6fc9528fa601ed18c308a4cf8179f7783 languageName: node linkType: hard -"@expo/prebuild-config@npm:^54.0.6": - version: 54.0.6 - resolution: "@expo/prebuild-config@npm:54.0.6" +"@expo/prebuild-config@npm:^54.0.7": + version: 54.0.7 + resolution: "@expo/prebuild-config@npm:54.0.7" dependencies: - "@expo/config": "npm:~12.0.10" - "@expo/config-plugins": "npm:~54.0.2" - "@expo/config-types": "npm:^54.0.8" - "@expo/image-utils": "npm:^0.8.7" - "@expo/json-file": "npm:^10.0.7" + "@expo/config": "npm:~12.0.11" + "@expo/config-plugins": "npm:~54.0.3" + "@expo/config-types": "npm:^54.0.9" + "@expo/image-utils": "npm:^0.8.8" + "@expo/json-file": "npm:^10.0.8" "@react-native/normalize-colors": "npm:0.81.5" debug: "npm:^4.3.1" resolve-from: "npm:^5.0.0" @@ -2185,14 +2167,14 @@ __metadata: xml2js: "npm:0.6.0" peerDependencies: expo: "*" - checksum: 10c0/d27cf8c6387115e293f25c102f80124230e425c3326f85350bc758bf0c5019c6ea6ca0f4718f56dc6e7916b42fa65535100147da84a71f12472d2606d50578f8 + checksum: 10c0/8ab94732c51c74823e8377c2f6fd5095615f8f82a4dd1696586bbdd5faed617806ffb52b9ab9fd88dff8244fc6361828eb376143c956a7b6ea30d6e9479e4e9c languageName: node linkType: hard -"@expo/schema-utils@npm:^0.1.7": - version: 0.1.7 - resolution: "@expo/schema-utils@npm:0.1.7" - checksum: 10c0/1099bd8801ff941584bc6d2bb44613f9fb87af663843d629d9ede8315f44f7332c881b70f1681e8f8fc82b27472b4a025341963f0f347e16a0ae90fcb65138cd +"@expo/schema-utils@npm:^0.1.8": + version: 0.1.8 + resolution: "@expo/schema-utils@npm:0.1.8" + checksum: 10c0/9a600ac858bcd1bd24ccac3e86cbef996c2c58cb20ce61fb1fc753f36dce4a000510e61b803ad5cb221a16caa38b54b243f08ac08e0de69e4aa556798d877f02 languageName: node linkType: hard @@ -2292,6 +2274,22 @@ __metadata: languageName: node linkType: hard +"@isaacs/balanced-match@npm:^4.0.1": + version: 4.0.1 + resolution: "@isaacs/balanced-match@npm:4.0.1" + checksum: 10c0/7da011805b259ec5c955f01cee903da72ad97c5e6f01ca96197267d3f33103d5b2f8a1af192140f3aa64526c593c8d098ae366c2b11f7f17645d12387c2fd420 + languageName: node + linkType: hard + +"@isaacs/brace-expansion@npm:^5.0.0": + version: 5.0.0 + resolution: "@isaacs/brace-expansion@npm:5.0.0" + dependencies: + "@isaacs/balanced-match": "npm:^4.0.1" + checksum: 10c0/b4d4812f4be53afc2c5b6c545001ff7a4659af68d4484804e9d514e183d20269bb81def8682c01a22b17c4d6aed14292c8494f7d2ac664e547101c1a905aa977 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -4699,9 +4697,9 @@ __metadata: languageName: node linkType: hard -"babel-preset-expo@npm:~54.0.7": - version: 54.0.7 - resolution: "babel-preset-expo@npm:54.0.7" +"babel-preset-expo@npm:~54.0.8": + version: 54.0.8 + resolution: "babel-preset-expo@npm:54.0.8" dependencies: "@babel/helper-module-imports": "npm:^7.25.9" "@babel/plugin-proposal-decorators": "npm:^7.12.9" @@ -4734,7 +4732,7 @@ __metadata: optional: true expo: optional: true - checksum: 10c0/a4c2de6b1c2a56199e663b99ddc42b75ece556d862e15d1240441a5d06f6f09c93f725e230baf3fab29afb11ee2026569f497e037d1596432cb66a5c1c6cefdc + checksum: 10c0/474b0799f3027d35f72d27f212551e92ac1b1119bac5a3f98c5d4e78be1a1cfcbcd24f52680236bf1400a037e07f7ab69ca4a8311947f0cf6f75c2d44aabde7d languageName: node linkType: hard @@ -6486,11 +6484,11 @@ __metadata: dependencies: "@expo/vector-icons": "npm:^15.0.3" "@react-native-masked-view/masked-view": "npm:0.3.2" - expo: "npm:54.0.23" - expo-asset: "npm:~12.0.9" - expo-font: "npm:~14.0.9" - expo-haptics: "npm:~15.0.7" - expo-linear-gradient: "npm:~15.0.7" + expo: "npm:~54.0.29" + expo-asset: "npm:~12.0.11" + expo-font: "npm:~14.0.10" + expo-haptics: "npm:~15.0.8" + expo-linear-gradient: "npm:~15.0.8" react: "npm:19.1.0" react-native: "npm:0.81.5" react-native-audio-api: "npm:0.9.0" @@ -6558,102 +6556,89 @@ __metadata: languageName: node linkType: hard -"expo-asset@npm:~12.0.9": - version: 12.0.9 - resolution: "expo-asset@npm:12.0.9" +"expo-asset@npm:~12.0.11": + version: 12.0.11 + resolution: "expo-asset@npm:12.0.11" dependencies: - "@expo/image-utils": "npm:^0.8.7" - expo-constants: "npm:~18.0.9" + "@expo/image-utils": "npm:^0.8.8" + expo-constants: "npm:~18.0.11" peerDependencies: expo: "*" react: "*" react-native: "*" - checksum: 10c0/7a66523e26e9868ad7961c9d6436f188e2832a7b3168a47be10ae8468616250803d31ded11b61bcc920e2aa0f0443c69fcb220be16b0b120aa255a840a355032 + checksum: 10c0/d2b452efeb5fbe014e6a17f344b53d57539c79786d26ef32841c89e06ca6eb6f35949c72c6b8c506dc385dda394a344b0810d368090c1d42277b18f37d5b2b08 languageName: node linkType: hard -"expo-constants@npm:~18.0.10": - version: 18.0.10 - resolution: "expo-constants@npm:18.0.10" +"expo-constants@npm:~18.0.11, expo-constants@npm:~18.0.12": + version: 18.0.12 + resolution: "expo-constants@npm:18.0.12" dependencies: - "@expo/config": "npm:~12.0.10" - "@expo/env": "npm:~2.0.7" + "@expo/config": "npm:~12.0.12" + "@expo/env": "npm:~2.0.8" peerDependencies: expo: "*" react-native: "*" - checksum: 10c0/c3878d7a4c438e1bab25bc5267553c06e4c002237f0cf35eb434de70734e31ef1d07c151d9a7771b75be1baa1c794a4c1c731167cbdfa5bd4fe41a499b8dda4b + checksum: 10c0/9dab3f1d685b17c25ae4179823162b763a7edeea8b976f96f25e37fabbfbc190f05cd774431664e5145196f3982e34303ebf5a1f5a82a1409218ae93187f710f languageName: node linkType: hard -"expo-constants@npm:~18.0.9": - version: 18.0.9 - resolution: "expo-constants@npm:18.0.9" - dependencies: - "@expo/config": "npm:~12.0.9" - "@expo/env": "npm:~2.0.7" - peerDependencies: - expo: "*" - react-native: "*" - checksum: 10c0/5e7f2fd366d4c0351d2d30b8f31afc0001e1d0c19f113b9083bae7ee495cb55aeae402497d0a13f856157730d5821c5143acccfc3d8d25b74aa064c6d33ec017 - languageName: node - linkType: hard - -"expo-file-system@npm:~19.0.17": - version: 19.0.17 - resolution: "expo-file-system@npm:19.0.17" +"expo-file-system@npm:~19.0.21": + version: 19.0.21 + resolution: "expo-file-system@npm:19.0.21" peerDependencies: expo: "*" react-native: "*" - checksum: 10c0/c0c4017429f0f43790d5a5587e56deca77015e959a8667c4ee3890a42d47a4389775ebfeb4951d0f7942ef7d416eb487928f6447f64a86cad58326181917e3c8 + checksum: 10c0/0ece34f86beda2048f8bf1f3218e57f6c7c14a875fe8be0f0dd2704d968ee61f979ddf0561b8769f4d46cfeb77ac759da0a030c5a77128cf2a06ce0bb9e3959b languageName: node linkType: hard -"expo-font@npm:~14.0.9": - version: 14.0.9 - resolution: "expo-font@npm:14.0.9" +"expo-font@npm:~14.0.10": + version: 14.0.10 + resolution: "expo-font@npm:14.0.10" dependencies: fontfaceobserver: "npm:^2.1.0" peerDependencies: expo: "*" react: "*" react-native: "*" - checksum: 10c0/7336a6094705c46379d4bb93f34a3b6c8d5fca871b34d62db94739b204639f3eeb6b1f5db4b4209e28a34348360075ccf557fe28ca996182469685427e570a9d + checksum: 10c0/67b5ea924319235426c27a4b69a8409438314bed635ad9c57bfbb4b5dcc873a575d2f44129683ec145785d3cdf591349078cc2e96d750ba2dea3aebf89e19e9f languageName: node linkType: hard -"expo-haptics@npm:~15.0.7": - version: 15.0.7 - resolution: "expo-haptics@npm:15.0.7" +"expo-haptics@npm:~15.0.8": + version: 15.0.8 + resolution: "expo-haptics@npm:15.0.8" peerDependencies: expo: "*" - checksum: 10c0/72daaf272b6ab3f650d76d901b3f744d497c9120b77d274560cccff6da985c867256212108c2057843996fad5ea8905614aae4747a217d6502f0c0156eca6d5d + checksum: 10c0/25f58bbbb5faa0d05701ada7f1247adead98a7e30dcbac136aa3e458773584ac758318a02d906de7ba85d63cbd45e7edde3dc35e61825f0dc1e8ed20668ed594 languageName: node linkType: hard -"expo-keep-awake@npm:~15.0.7": - version: 15.0.7 - resolution: "expo-keep-awake@npm:15.0.7" +"expo-keep-awake@npm:~15.0.8": + version: 15.0.8 + resolution: "expo-keep-awake@npm:15.0.8" peerDependencies: expo: "*" react: "*" - checksum: 10c0/6ca4cb430a97627b5657a220720808e4bd6dd89f4e9f86d52db71b9f91a72af8e63a83005d49af86924fc8f7bd210c312dfcb07212a1fe54334e0b4058943ec9 + checksum: 10c0/23064b18285498e70be0aa525dc875cc809fc723b9a101d51e4721a09b1460eb041c73ebeb6d51e9175bb4c9b7a668bc08a48b99ebddac4cfaadb5a47194d329 languageName: node linkType: hard -"expo-linear-gradient@npm:~15.0.7": - version: 15.0.7 - resolution: "expo-linear-gradient@npm:15.0.7" +"expo-linear-gradient@npm:~15.0.8": + version: 15.0.8 + resolution: "expo-linear-gradient@npm:15.0.8" peerDependencies: expo: "*" react: "*" react-native: "*" - checksum: 10c0/ee9984f4bc8985b3ad32f8a59138fd7336e16e782c1d2d10110f70b2e05b9c563382d2c16cfbe21e6463fa896f573dcc3d70d0afd32f8d435bccd9afadf8b006 + checksum: 10c0/2ac3bff62143083e3d5b894fccc4c76de058277f65ed123232636cf26c88da6d566fa3cd46be4101f86e66b89ede7c058e12c93493b752df562da2fa971dcd66 languageName: node linkType: hard -"expo-modules-autolinking@npm:3.0.21": - version: 3.0.21 - resolution: "expo-modules-autolinking@npm:3.0.21" +"expo-modules-autolinking@npm:3.0.23": + version: 3.0.23 + resolution: "expo-modules-autolinking@npm:3.0.23" dependencies: "@expo/spawn-async": "npm:^1.7.2" chalk: "npm:^4.1.0" @@ -6662,51 +6647,51 @@ __metadata: resolve-from: "npm:^5.0.0" bin: expo-modules-autolinking: bin/expo-modules-autolinking.js - checksum: 10c0/f1a7abb57f9ea0ceb3f530257705c7fd8775547c60618d9f954ac4e8ce8ff4b2928b47da69c1003535febc04f82fb415755b1226cdb9e5f7bbb82b4110a1f37f + checksum: 10c0/c10e5a4b9cbc3cad1ab34745f2903041b5042123663f94cbd724602dbdfc0bfe75dd94f581660a41ffd2baa9c407a5d71a13d415a6214ac6bd9f9d00ba499e8a languageName: node linkType: hard -"expo-modules-core@npm:3.0.25": - version: 3.0.25 - resolution: "expo-modules-core@npm:3.0.25" +"expo-modules-core@npm:3.0.29": + version: 3.0.29 + resolution: "expo-modules-core@npm:3.0.29" dependencies: invariant: "npm:^2.2.4" peerDependencies: react: "*" react-native: "*" - checksum: 10c0/45cc72d2c8c7fdf149be19009f99be1e600aa4619a849accd24b6f20cf64cd461014767e46cace300e096520d4e3c239c3a44a96f4275488bd1b47320ac03ee0 + checksum: 10c0/e32a87b06aa772f523afa5a995523848c61c06370085ae9e724ad24432cac38925ff69976f58b2c562828c2cefba6df00cfef2dbb206111413890ce1c2baee19 languageName: node linkType: hard -"expo-server@npm:^1.0.4": - version: 1.0.4 - resolution: "expo-server@npm:1.0.4" - checksum: 10c0/56330a10e222475126ce4a61676b170cf34951deae4a8b0965dcf8e317b03e99f71d92f908e57bb6196d2bdb0b36d5efb17fb48fd7c3b1d65f690bf2578f7975 +"expo-server@npm:^1.0.5": + version: 1.0.5 + resolution: "expo-server@npm:1.0.5" + checksum: 10c0/0da974f733235d457f7ce51e5452f48f203378687f821bdab7159617a491c0192251423a17a7a3a118486e1cbfffff5d5ad31aeeedcf2cfad6412a1bd7e86877 languageName: node linkType: hard -"expo@npm:54.0.23": - version: 54.0.23 - resolution: "expo@npm:54.0.23" +"expo@npm:~54.0.29": + version: 54.0.29 + resolution: "expo@npm:54.0.29" dependencies: "@babel/runtime": "npm:^7.20.0" - "@expo/cli": "npm:54.0.16" - "@expo/config": "npm:~12.0.10" - "@expo/config-plugins": "npm:~54.0.2" - "@expo/devtools": "npm:0.1.7" - "@expo/fingerprint": "npm:0.15.3" + "@expo/cli": "npm:54.0.19" + "@expo/config": "npm:~12.0.12" + "@expo/config-plugins": "npm:~54.0.4" + "@expo/devtools": "npm:0.1.8" + "@expo/fingerprint": "npm:0.15.4" "@expo/metro": "npm:~54.1.0" - "@expo/metro-config": "npm:54.0.9" + "@expo/metro-config": "npm:54.0.11" "@expo/vector-icons": "npm:^15.0.3" "@ungap/structured-clone": "npm:^1.3.0" - babel-preset-expo: "npm:~54.0.7" - expo-asset: "npm:~12.0.9" - expo-constants: "npm:~18.0.10" - expo-file-system: "npm:~19.0.17" - expo-font: "npm:~14.0.9" - expo-keep-awake: "npm:~15.0.7" - expo-modules-autolinking: "npm:3.0.21" - expo-modules-core: "npm:3.0.25" + babel-preset-expo: "npm:~54.0.8" + expo-asset: "npm:~12.0.11" + expo-constants: "npm:~18.0.12" + expo-file-system: "npm:~19.0.21" + expo-font: "npm:~14.0.10" + expo-keep-awake: "npm:~15.0.8" + expo-modules-autolinking: "npm:3.0.23" + expo-modules-core: "npm:3.0.29" pretty-format: "npm:^29.7.0" react-refresh: "npm:^0.14.2" whatwg-url-without-unicode: "npm:8.0.0-3" @@ -6727,7 +6712,7 @@ __metadata: expo: bin/cli expo-modules-autolinking: bin/autolinking fingerprint: bin/fingerprint - checksum: 10c0/4dd715a2a73387ebb40a5d2427ad643dc03b365ced10705203ebe4c0bac8f26ad76c65758d55b6c43971d6843d328c99212a1af0de926cb83cffdb699194626b + checksum: 10c0/4e550143de61e8cb68d03ba78e34f3f008bc670f8dd042711f38da04c59e8289b97f88c20fc6e5529f838390cd2efc95e06bab36ea49c324af46253df628f929 languageName: node linkType: hard @@ -6813,6 +6798,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -7158,7 +7155,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.4.2": +"glob@npm:^10.2.2": version: 10.4.5 resolution: "glob@npm:10.4.5" dependencies: @@ -7174,6 +7171,17 @@ __metadata: languageName: node linkType: hard +"glob@npm:^13.0.0": + version: 13.0.0 + resolution: "glob@npm:13.0.0" + dependencies: + minimatch: "npm:^10.1.1" + minipass: "npm:^7.1.2" + path-scurry: "npm:^2.0.0" + checksum: 10c0/8e2f5821f3f7c312dd102e23a15b80c79e0837a9872784293ba2e15ec73b3f3749a49a42a31bfcb4e52c84820a474e92331c2eebf18819d20308f5c33876630a + languageName: node + linkType: hard + "glob@npm:^7.1.1, glob@npm:^7.1.3, glob@npm:^7.1.4": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -9165,6 +9173,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^11.0.0": + version: 11.2.4 + resolution: "lru-cache@npm:11.2.4" + checksum: 10c0/4a24f9b17537619f9144d7b8e42cd5a225efdfd7076ebe7b5e7dc02b860a818455201e67fbf000765233fe7e339d3c8229fc815e9b58ee6ede511e07608c19b2 + languageName: node + linkType: hard + "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -10375,6 +10390,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.1.1": + version: 10.1.1 + resolution: "minimatch@npm:10.1.1" + dependencies: + "@isaacs/brace-expansion": "npm:^5.0.0" + checksum: 10c0/c85d44821c71973d636091fddbfbffe62370f5ee3caf0241c5b60c18cd289e916200acb2361b7e987558cd06896d153e25d505db9fc1e43e6b4b6752e2702902 + languageName: node + linkType: hard + "minimatch@npm:^3.0.2, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -10485,6 +10509,15 @@ __metadata: languageName: node linkType: hard +"minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" + dependencies: + minipass: "npm:^7.1.2" + checksum: 10c0/5aad75ab0090b8266069c9aabe582c021ae53eb33c6c691054a13a45db3b4f91a7fb1bd79151e6b4e9e9a86727b522527c0a06ec7d45206b745d54cd3097bcec + languageName: node + linkType: hard + "mkdirp@npm:^0.5.1": version: 0.5.6 resolution: "mkdirp@npm:0.5.6" @@ -11156,6 +11189,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^2.0.0": + version: 2.0.1 + resolution: "path-scurry@npm:2.0.1" + dependencies: + lru-cache: "npm:^11.0.0" + minipass: "npm:^7.1.2" + checksum: 10c0/2a16ed0e81fbc43513e245aa5763354e25e787dab0d539581a6c3f0f967461a159ed6236b2559de23aa5b88e7dc32b469b6c47568833dd142a4b24b4f5cd2620 + languageName: node + linkType: hard + "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -11191,6 +11234,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.3": + version: 4.0.3 + resolution: "picomatch@npm:4.0.3" + checksum: 10c0/9582c951e95eebee5434f59e426cddd228a7b97a0161a375aed4be244bd3fe8e3a31b846808ea14ef2c8a2527a6eeab7b3946a67d5979e81694654f939473ae2 + languageName: node + linkType: hard + "pify@npm:^4.0.1": version: 4.0.1 resolution: "pify@npm:4.0.1" @@ -12946,21 +12996,21 @@ __metadata: languageName: node linkType: hard -"sucrase@npm:3.35.0": - version: 3.35.0 - resolution: "sucrase@npm:3.35.0" +"sucrase@npm:~3.35.1": + version: 3.35.1 + resolution: "sucrase@npm:3.35.1" dependencies: "@jridgewell/gen-mapping": "npm:^0.3.2" commander: "npm:^4.0.0" - glob: "npm:^10.3.10" lines-and-columns: "npm:^1.1.6" mz: "npm:^2.7.0" pirates: "npm:^4.0.1" + tinyglobby: "npm:^0.2.11" ts-interface-checker: "npm:^0.1.9" bin: sucrase: bin/sucrase sucrase-node: bin/sucrase-node - checksum: 10c0/ac85f3359d2c2ecbf5febca6a24ae9bf96c931f05fde533c22a94f59c6a74895e5d5f0e871878dfd59c2697a75ebb04e4b2224ef0bfc24ca1210735c2ec191ef + checksum: 10c0/6fa22329c261371feb9560630d961ad0d0b9c87dce21ea74557c5f3ffbe5c1ee970ea8bcce9962ae9c90c3c47165ffa7dd41865c7414f5d8ea7a40755d612c5c languageName: node linkType: hard @@ -13029,6 +13079,19 @@ __metadata: languageName: node linkType: hard +"tar@npm:^7.5.2": + version: 7.5.2 + resolution: "tar@npm:7.5.2" + dependencies: + "@isaacs/fs-minipass": "npm:^4.0.0" + chownr: "npm:^3.0.0" + minipass: "npm:^7.1.2" + minizlib: "npm:^3.1.0" + yallist: "npm:^5.0.0" + checksum: 10c0/a7d8b801139b52f93a7e34830db0de54c5aa45487c7cb551f6f3d44a112c67f1cb8ffdae856b05fd4f17b1749911f1c26f1e3a23bbe0279e17fd96077f13f467 + languageName: node + linkType: hard + "temp-dir@npm:~2.0.0": version: 2.0.0 resolution: "temp-dir@npm:2.0.0" @@ -13122,6 +13185,16 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.11": + version: 0.2.15 + resolution: "tinyglobby@npm:0.2.15" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.3" + checksum: 10c0/869c31490d0d88eedb8305d178d4c75e7463e820df5a9b9d388291daf93e8b1eb5de1dad1c1e139767e4269fe75f3b10d5009b2cc14db96ff98986920a186844 + languageName: node + linkType: hard + "tinyglobby@npm:^0.2.12": version: 0.2.13 resolution: "tinyglobby@npm:0.2.13" @@ -13817,21 +13890,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.18.3": - version: 8.18.3 - resolution: "ws@npm:8.18.3" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 10c0/eac918213de265ef7cb3d4ca348b891a51a520d839aa51cdb8ca93d4fa7ff9f6ccb339ccee89e4075324097f0a55157c89fa3f7147bde9d8d7e90335dc087b53 - languageName: node - linkType: hard - "xcode@npm:^3.0.1": version: 3.0.1 resolution: "xcode@npm:3.0.1" @@ -13990,19 +14048,3 @@ __metadata: checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f languageName: node linkType: hard - -"zod-to-json-schema@npm:^3.24.6": - version: 3.24.6 - resolution: "zod-to-json-schema@npm:3.24.6" - peerDependencies: - zod: ^3.24.1 - checksum: 10c0/b907ab6d057100bd25a37e5545bf5f0efa5902cd84d3c3ec05c2e51541431a47bd9bf1e5e151a244273409b45f5986d55b26e5d207f98abc5200702f733eb368 - languageName: node - linkType: hard - -"zod@npm:^3.25.76": - version: 3.25.76 - resolution: "zod@npm:3.25.76" - checksum: 10c0/5718ec35e3c40b600316c5b4c5e4976f7fee68151bc8f8d90ec18a469be9571f072e1bbaace10f1e85cf8892ea12d90821b200e980ab46916a6166a4260a983c - languageName: node - linkType: hard From be608841457a53987077151a08d6a4e18fcc4dac Mon Sep 17 00:00:00 2001 From: Tim Roberts Date: Sat, 13 Dec 2025 18:57:47 +0000 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=91=BC=20Make=20midnight=20show=20as?= =?UTF-8?q?=2012=20AM=20on=2012-hour=20picker.=20Closes=20#67?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/generateNumbers.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/utils/generateNumbers.ts b/src/utils/generateNumbers.ts index 3e87cf3..17a6e89 100644 --- a/src/utils/generateNumbers.ts +++ b/src/utils/generateNumbers.ts @@ -107,14 +107,15 @@ export const generate12HourNumbers = (options: { }) => { let numbers: string[] = []; - // Generate numbers from 0 to 11 for AM + // Generate numbers from 12 AM to 11 AM for (let i = 0; i < 12; i += options.interval) { + const hour = i === 0 ? 12 : i; numbers.push( - `${padNumber(i, { padWithZero: options.padNumbersWithZero })} AM` + `${padNumber(hour, { padWithZero: options.padNumbersWithZero })} AM` ); } - // Generate numbers from 12 to 11 for PM + // Generate numbers from 12 PM to 11 PM for (let i = 12; i < 24; i += options.interval) { const hour = i > 12 ? i - 12 : i; numbers.push( From e82fe8a6ea741c50e2d86cb596d0e3637248ed7d Mon Sep 17 00:00:00 2001 From: Tim Roberts Date: Sat, 13 Dec 2025 19:04:53 +0000 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=94=A8=20Fix=20broken=20tests=20resul?= =?UTF-8?q?ting=20from=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tests/generateNumbers.test.ts | 70 +++++++++++++++---------------- src/tests/padNumber.test.ts | 36 ++++++++-------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/tests/generateNumbers.test.ts b/src/tests/generateNumbers.test.ts index 5ded2a4..79afc13 100644 --- a/src/tests/generateNumbers.test.ts +++ b/src/tests/generateNumbers.test.ts @@ -22,7 +22,7 @@ describe("generateNumbers", () => { repeatNTimes: 1, disableInfiniteScroll: true, }); - expect(result).toEqual([" 0", " 2", " 4", " 6", " 8"]); + expect(result).toEqual(["\u20070", "\u20072", "\u20074", "\u20076", "\u20078"]); }); it("generates numbers with interval of 5", () => { @@ -32,7 +32,7 @@ describe("generateNumbers", () => { repeatNTimes: 1, disableInfiniteScroll: true, }); - expect(result).toEqual([" 0", " 5", "10", "15"]); + expect(result).toEqual(["\u20070", "\u20075", "10", "15"]); }); it("returns empty array when numberOfItems is 0", () => { @@ -88,7 +88,7 @@ describe("generateNumbers", () => { padNumbersWithZero: false, disableInfiniteScroll: true, }); - expect(result).toEqual([" 0", " 1", " 2", " 3", " 4"]); + expect(result).toEqual(["\u20070", "\u20071", "\u20072", "\u20073", "\u20074"]); }); }); @@ -100,7 +100,7 @@ describe("generateNumbers", () => { repeatNTimes: 1, disableInfiniteScroll: true, }); - expect(result).toEqual(["", "", " 0", " 1", " 2", "", ""]); + expect(result).toEqual(["", "", "\u20070", "\u20071", "\u20072", "", ""]); expect(result).toHaveLength(7); }); @@ -111,7 +111,7 @@ describe("generateNumbers", () => { repeatNTimes: 1, disableInfiniteScroll: true, }); - expect(result).toEqual(["", " 0", " 1", " 2", ""]); + expect(result).toEqual(["", "\u20070", "\u20071", "\u20072", ""]); }); it("adds padding with 3 items", () => { @@ -121,7 +121,7 @@ describe("generateNumbers", () => { repeatNTimes: 1, disableInfiniteScroll: true, }); - expect(result).toEqual(["", "", "", " 0", " 1", "", "", ""]); + expect(result).toEqual(["", "", "", "\u20070", "\u20071", "", "", ""]); }); }); @@ -133,7 +133,7 @@ describe("generateNumbers", () => { repeatNTimes: 2, disableInfiniteScroll: false, }); - expect(result).toEqual([" 0", " 1", " 2", " 0", " 1", " 2"]); + expect(result).toEqual(["\u20070", "\u20071", "\u20072", "\u20070", "\u20071", "\u20072"]); }); it("repeats numbers when repeatNTimes is 3", () => { @@ -143,7 +143,7 @@ describe("generateNumbers", () => { repeatNTimes: 3, disableInfiniteScroll: false, }); - expect(result).toEqual([" 0", " 1", " 0", " 1", " 0", " 1"]); + expect(result).toEqual(["\u20070", "\u20071", "\u20070", "\u20071", "\u20070", "\u20071"]); }); }); @@ -192,7 +192,7 @@ describe("generate12HourNumbers", () => { disableInfiniteScroll: false, }); expect(result).toHaveLength(24); - expect(result[0]).toBe(" 0 AM"); + expect(result[0]).toBe("12 AM"); expect(result[11]).toBe("11 AM"); expect(result[12]).toBe("12 PM"); expect(result[23]).toBe("11 PM"); @@ -205,7 +205,7 @@ describe("generate12HourNumbers", () => { disableInfiniteScroll: false, }); expect(result).toHaveLength(12); - expect(result[0]).toBe(" 0 AM"); + expect(result[0]).toBe("12 AM"); expect(result[5]).toBe("10 AM"); expect(result[6]).toBe("12 PM"); expect(result[11]).toBe("10 PM"); @@ -218,10 +218,10 @@ describe("generate12HourNumbers", () => { disableInfiniteScroll: false, }); expect(result).toHaveLength(8); - expect(result[0]).toBe(" 0 AM"); - expect(result[3]).toBe(" 9 AM"); + expect(result[0]).toBe("12 AM"); + expect(result[3]).toBe("\u20079 AM"); expect(result[4]).toBe("12 PM"); - expect(result[7]).toBe(" 9 PM"); + expect(result[7]).toBe("\u20079 PM"); }); it("generates hours with 4-hour interval", () => { @@ -231,10 +231,10 @@ describe("generate12HourNumbers", () => { disableInfiniteScroll: false, }); expect(result).toHaveLength(6); - expect(result[0]).toBe(" 0 AM"); - expect(result[2]).toBe(" 8 AM"); + expect(result[0]).toBe("12 AM"); + expect(result[2]).toBe("\u20078 AM"); expect(result[3]).toBe("12 PM"); - expect(result[5]).toBe(" 8 PM"); + expect(result[5]).toBe("\u20078 PM"); }); it("generates hours with 6-hour interval", () => { @@ -244,10 +244,10 @@ describe("generate12HourNumbers", () => { disableInfiniteScroll: false, }); expect(result).toHaveLength(4); - expect(result[0]).toBe(" 0 AM"); - expect(result[1]).toBe(" 6 AM"); + expect(result[0]).toBe("12 AM"); + expect(result[1]).toBe("\u20076 AM"); expect(result[2]).toBe("12 PM"); - expect(result[3]).toBe(" 6 PM"); + expect(result[3]).toBe("\u20076 PM"); }); }); @@ -259,7 +259,7 @@ describe("generate12HourNumbers", () => { padNumbersWithZero: true, disableInfiniteScroll: false, }); - expect(result[0]).toBe("00 AM"); + expect(result[0]).toBe("12 AM"); expect(result[1]).toBe("01 AM"); expect(result[9]).toBe("09 AM"); expect(result[10]).toBe("10 AM"); @@ -287,10 +287,10 @@ describe("generate12HourNumbers", () => { }); expect(result[0]).toBe(""); expect(result[1]).toBe(""); - expect(result[2]).toBe(" 0 AM"); - expect(result[3]).toBe(" 6 AM"); + expect(result[2]).toBe("12 AM"); + expect(result[3]).toBe("\u20076 AM"); expect(result[4]).toBe("12 PM"); - expect(result[5]).toBe(" 6 PM"); + expect(result[5]).toBe("\u20076 PM"); expect(result[6]).toBe(""); expect(result[7]).toBe(""); expect(result).toHaveLength(8); @@ -302,8 +302,8 @@ describe("generate12HourNumbers", () => { padWithNItems: 2, disableInfiniteScroll: false, }); - expect(result[0]).toBe(" 0 AM"); - expect(result[3]).toBe(" 6 PM"); + expect(result[0]).toBe("12 AM"); + expect(result[3]).toBe("\u20076 PM"); expect(result).toHaveLength(4); }); }); @@ -317,10 +317,10 @@ describe("generate12HourNumbers", () => { disableInfiniteScroll: false, }); expect(result).toHaveLength(8); - expect(result[0]).toBe(" 0 AM"); - expect(result[3]).toBe(" 6 PM"); - expect(result[4]).toBe(" 0 AM"); - expect(result[7]).toBe(" 6 PM"); + expect(result[0]).toBe("12 AM"); + expect(result[3]).toBe("\u20076 PM"); + expect(result[4]).toBe("12 AM"); + expect(result[7]).toBe("\u20076 PM"); }); it("repeats hours when repeatNTimes is 3", () => { @@ -331,11 +331,11 @@ describe("generate12HourNumbers", () => { disableInfiniteScroll: false, }); expect(result).toHaveLength(6); - expect(result[0]).toBe(" 0 AM"); + expect(result[0]).toBe("12 AM"); expect(result[1]).toBe("12 PM"); - expect(result[2]).toBe(" 0 AM"); + expect(result[2]).toBe("12 AM"); expect(result[3]).toBe("12 PM"); - expect(result[4]).toBe(" 0 AM"); + expect(result[4]).toBe("12 AM"); expect(result[5]).toBe("12 PM"); }); @@ -357,7 +357,7 @@ describe("generate12HourNumbers", () => { padNumbersWithZero: true, disableInfiniteScroll: true, }); - expect(result).toEqual(["", "00 AM", "12 PM", ""]); + expect(result).toEqual(["", "12 AM", "12 PM", ""]); }); it("combines all options", () => { @@ -369,11 +369,11 @@ describe("generate12HourNumbers", () => { disableInfiniteScroll: false, }); expect(result).toHaveLength(8); - expect(result[0]).toBe("00 AM"); + expect(result[0]).toBe("12 AM"); expect(result[1]).toBe("06 AM"); expect(result[2]).toBe("12 PM"); expect(result[3]).toBe("06 PM"); - expect(result[4]).toBe("00 AM"); + expect(result[4]).toBe("12 AM"); expect(result[7]).toBe("06 PM"); }); }); diff --git a/src/tests/padNumber.test.ts b/src/tests/padNumber.test.ts index 30df52c..a57c8b5 100644 --- a/src/tests/padNumber.test.ts +++ b/src/tests/padNumber.test.ts @@ -29,10 +29,10 @@ describe("padNumber", () => { describe("padding with space", () => { it("pads single digit with space", () => { - expect(padNumber(0, { padWithZero: false })).toBe(" 0"); - expect(padNumber(1, { padWithZero: false })).toBe(" 1"); - expect(padNumber(5, { padWithZero: false })).toBe(" 5"); - expect(padNumber(9, { padWithZero: false })).toBe(" 9"); + expect(padNumber(0, { padWithZero: false })).toBe("\u20070"); + expect(padNumber(1, { padWithZero: false })).toBe("\u20071"); + expect(padNumber(5, { padWithZero: false })).toBe("\u20075"); + expect(padNumber(9, { padWithZero: false })).toBe("\u20079"); }); it("does not pad double digits", () => { @@ -55,10 +55,10 @@ describe("padNumber", () => { describe("no options provided", () => { it("defaults to space padding for single digits", () => { - expect(padNumber(0)).toBe(" 0"); - expect(padNumber(1)).toBe(" 1"); - expect(padNumber(5)).toBe(" 5"); - expect(padNumber(9)).toBe(" 9"); + expect(padNumber(0)).toBe("\u20070"); + expect(padNumber(1)).toBe("\u20071"); + expect(padNumber(5)).toBe("\u20075"); + expect(padNumber(9)).toBe("\u20079"); }); it("does not pad double digits", () => { @@ -76,9 +76,9 @@ describe("padNumber", () => { describe("undefined padWithZero option", () => { it("defaults to space padding", () => { - expect(padNumber(0, {})).toBe(" 0"); - expect(padNumber(5, {})).toBe(" 5"); - expect(padNumber(9, {})).toBe(" 9"); + expect(padNumber(0, {})).toBe("\u20070"); + expect(padNumber(5, {})).toBe("\u20075"); + expect(padNumber(9, {})).toBe("\u20079"); }); it("does not pad double digits", () => { @@ -90,8 +90,8 @@ describe("padNumber", () => { describe("edge cases at boundary", () => { it("handles value exactly 9 (last single digit)", () => { expect(padNumber(9, { padWithZero: true })).toBe("09"); - expect(padNumber(9, { padWithZero: false })).toBe(" 9"); - expect(padNumber(9)).toBe(" 9"); + expect(padNumber(9, { padWithZero: false })).toBe("\u20079"); + expect(padNumber(9)).toBe("\u20079"); }); it("handles value exactly 10 (first double digit)", () => { @@ -102,8 +102,8 @@ describe("padNumber", () => { it("handles value exactly 0", () => { expect(padNumber(0, { padWithZero: true })).toBe("00"); - expect(padNumber(0, { padWithZero: false })).toBe(" 0"); - expect(padNumber(0)).toBe(" 0"); + expect(padNumber(0, { padWithZero: false })).toBe("\u20070"); + expect(padNumber(0)).toBe("\u20070"); }); }); @@ -134,8 +134,8 @@ describe("padNumber", () => { }); it("formats days without padding preference", () => { - expect(padNumber(1, { padWithZero: false })).toBe(" 1"); - expect(padNumber(7, { padWithZero: false })).toBe(" 7"); + expect(padNumber(1, { padWithZero: false })).toBe("\u20071"); + expect(padNumber(7, { padWithZero: false })).toBe("\u20077"); expect(padNumber(30, { padWithZero: false })).toBe("30"); expect(padNumber(365, { padWithZero: false })).toBe("365"); }); @@ -153,7 +153,7 @@ describe("padNumber", () => { it("correctly pads all single digits 0-9 with space", () => { for (let i = 0; i < 10; i++) { const result = padNumber(i, { padWithZero: false }); - expect(result).toBe(` ${i}`); + expect(result).toBe(`\u2007${i}`); expect(result).toHaveLength(2); } });