@cdx-ui/components 0.0.1-beta.76 → 0.0.1-beta.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/BottomSheet/index.js +68 -25
- package/lib/commonjs/components/BottomSheet/index.js.map +1 -1
- package/lib/commonjs/components/BottomSheet/styles.js +5 -42
- package/lib/commonjs/components/BottomSheet/styles.js.map +1 -1
- package/lib/commonjs/components/CurrencyInput/index.js +157 -0
- package/lib/commonjs/components/CurrencyInput/index.js.map +1 -0
- package/lib/commonjs/components/CurrencyInput/styles.js +31 -0
- package/lib/commonjs/components/CurrencyInput/styles.js.map +1 -0
- package/lib/commonjs/components/Link/styles.js +1 -1
- package/lib/commonjs/components/Select/styles.js +1 -1
- package/lib/commonjs/components/Select/styles.js.map +1 -1
- package/lib/commonjs/components/index.js +12 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/module/components/BottomSheet/index.js +70 -27
- package/lib/module/components/BottomSheet/index.js.map +1 -1
- package/lib/module/components/BottomSheet/styles.js +4 -42
- package/lib/module/components/BottomSheet/styles.js.map +1 -1
- package/lib/module/components/CurrencyInput/index.js +154 -0
- package/lib/module/components/CurrencyInput/index.js.map +1 -0
- package/lib/module/components/CurrencyInput/styles.js +27 -0
- package/lib/module/components/CurrencyInput/styles.js.map +1 -0
- package/lib/module/components/Link/styles.js +1 -1
- package/lib/module/components/Select/styles.js +1 -1
- package/lib/module/components/Select/styles.js.map +1 -1
- package/lib/module/components/index.js +1 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/typescript/components/BottomSheet/index.d.ts.map +1 -1
- package/lib/typescript/components/BottomSheet/styles.d.ts +0 -5
- package/lib/typescript/components/BottomSheet/styles.d.ts.map +1 -1
- package/lib/typescript/components/CurrencyInput/index.d.ts +15 -0
- package/lib/typescript/components/CurrencyInput/index.d.ts.map +1 -0
- package/lib/typescript/components/CurrencyInput/styles.d.ts +9 -0
- package/lib/typescript/components/CurrencyInput/styles.d.ts.map +1 -0
- package/lib/typescript/components/Select/styles.d.ts.map +1 -1
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/components/BottomSheet/index.tsx +99 -42
- package/src/components/BottomSheet/styles.ts +7 -47
- package/src/components/CurrencyInput/index.tsx +192 -0
- package/src/components/CurrencyInput/styles.ts +44 -0
- package/src/components/Link/styles.ts +1 -1
- package/src/components/Select/styles.ts +0 -2
- package/src/components/index.ts +1 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type ViewStyle } from 'react-native';
|
|
2
1
|
import { type VariantProps } from 'class-variance-authority';
|
|
3
2
|
export declare const bottomSheetContainerVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
4
3
|
export declare const bottomSheetHeaderVariants: (props?: ({
|
|
@@ -6,11 +5,7 @@ export declare const bottomSheetHeaderVariants: (props?: ({
|
|
|
6
5
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
6
|
export declare const bottomSheetContentVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
8
7
|
export declare const bottomSheetFooterVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
9
|
-
export declare const bottomSheetSubtitleVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
10
|
-
export declare const bottomSheetBackButtonVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
11
8
|
export declare const bottomSheetBackIconVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
12
|
-
export declare const bottomSheetCloseButtonVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
13
9
|
export declare const bottomSheetCloseIconVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
14
|
-
export declare const handleIndicatorStyle: ViewStyle;
|
|
15
10
|
export type BottomSheetHeaderVariants = VariantProps<typeof bottomSheetHeaderVariants>;
|
|
16
11
|
//# sourceMappingURL=styles.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../src/components/BottomSheet/styles.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../src/components/BottomSheet/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAElE,eAAO,MAAM,4BAA4B,oFAGvC,CAAC;AAEH,eAAO,MAAM,yBAAyB;;8EAiBrC,CAAC;AAEF,eAAO,MAAM,0BAA0B,oFAA0C,CAAC;AAElF,eAAO,MAAM,yBAAyB,oFAIpC,CAAC;AAEH,eAAO,MAAM,2BAA2B,oFAAiC,CAAC;AAE1E,eAAO,MAAM,4BAA4B,oFAAiC,CAAC;AAE3E,MAAM,MAAM,yBAAyB,GAAG,YAAY,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type TextInput, type TextInputProps, type ViewProps } from 'react-native';
|
|
2
|
+
import { type IInputFieldProps } from '@cdx-ui/primitives';
|
|
3
|
+
import { type CurrencyInputVariantProps } from './styles';
|
|
4
|
+
export interface CurrencyInputProps extends IInputFieldProps, CurrencyInputVariantProps, Pick<TextInputProps, 'accessibilityLabel' | 'accessibilityHint' | 'testID' | 'onFocus' | 'onBlur'> {
|
|
5
|
+
/** Current numeric value. Renders formatted zero when undefined or null. */
|
|
6
|
+
value?: number;
|
|
7
|
+
/** Called with the parsed number when the user changes the amount. */
|
|
8
|
+
onChangeValue?: (value: number) => void;
|
|
9
|
+
/** Currency symbol rendered as an inline prefix. Defaults to `"$"`. */
|
|
10
|
+
currencySymbol?: string;
|
|
11
|
+
className?: string;
|
|
12
|
+
style?: ViewProps['style'];
|
|
13
|
+
}
|
|
14
|
+
export declare const CurrencyInput: import("react").ForwardRefExoticComponent<CurrencyInputProps & import("react").RefAttributes<TextInput>>;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/CurrencyInput/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,KAAK,yBAAyB,EAG/B,MAAM,UAAU,CAAC;AAqBlB,MAAM,WAAW,kBACf,SACE,gBAAgB,EAChB,yBAAyB,EACzB,IAAI,CACF,cAAc,EACd,oBAAoB,GAAG,mBAAmB,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAC7E;IACH,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED,eAAO,MAAM,aAAa,0GAyIzB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type VariantProps } from 'class-variance-authority';
|
|
2
|
+
export declare const currencyInputRootVariants: (props?: ({
|
|
3
|
+
size?: "small" | "default" | null | undefined;
|
|
4
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
5
|
+
export declare const currencyInputFieldVariants: (props?: ({
|
|
6
|
+
size?: "small" | "default" | null | undefined;
|
|
7
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
|
+
export type CurrencyInputVariantProps = VariantProps<typeof currencyInputRootVariants>;
|
|
9
|
+
//# sourceMappingURL=styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../src/components/CurrencyInput/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGlE,eAAO,MAAM,yBAAyB;;8EAkBrC,CAAC;AAEF,eAAO,MAAM,0BAA0B;;8EAkBtC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,YAAY,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../src/components/Select/styles.ts"],"names":[],"mappings":"AACA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAKlE,eAAO,MAAM,qBAAqB;;;
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../src/components/Select/styles.ts"],"names":[],"mappings":"AACA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAKlE,eAAO,MAAM,qBAAqB;;;8EA8CjC,CAAC;AAIF,eAAO,MAAM,mBAAmB;;8EAkB/B,CAAC;AAIF,eAAO,MAAM,kBAAkB;;8EAiB9B,CAAC;AAIF,eAAO,MAAM,qBAAqB,oFAWhC,CAAC;AAIH,eAAO,MAAM,kBAAkB;;8EAsB9B,CAAC;AAIF,eAAO,MAAM,uBAAuB;;8EAanC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,YAAY,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACzC,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACzC,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdx-ui/components",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.77",
|
|
4
4
|
"main": "lib/commonjs/index.js",
|
|
5
5
|
"module": "lib/module/index.js",
|
|
6
6
|
"react-native": "src/index.ts",
|
|
@@ -67,9 +67,9 @@
|
|
|
67
67
|
"@gorhom/bottom-sheet": "^5.2.6",
|
|
68
68
|
"class-variance-authority": "^0.7.1",
|
|
69
69
|
"uniwind": "1.10.0",
|
|
70
|
-
"@cdx-ui/
|
|
71
|
-
"@cdx-ui/
|
|
72
|
-
"@cdx-ui/icons": "0.0.1-beta.
|
|
70
|
+
"@cdx-ui/utils": "0.0.1-beta.77",
|
|
71
|
+
"@cdx-ui/primitives": "0.0.1-beta.77",
|
|
72
|
+
"@cdx-ui/icons": "0.0.1-beta.77"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@react-navigation/bottom-tabs": "*",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { forwardRef, useCallback, useRef, type ReactNode } from 'react';
|
|
2
|
-
import { Dimensions,
|
|
2
|
+
import { Dimensions, View, ViewStyle } from 'react-native';
|
|
3
3
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
4
|
+
import { useResolveClassNames } from 'uniwind';
|
|
4
5
|
import {
|
|
5
6
|
BottomSheetModal,
|
|
6
7
|
BottomSheetModalProvider,
|
|
@@ -12,21 +13,15 @@ import {
|
|
|
12
13
|
type BottomSheetFooterProps as GorhomBottomSheetFooterProps,
|
|
13
14
|
} from '@gorhom/bottom-sheet';
|
|
14
15
|
import { cn } from '@cdx-ui/utils';
|
|
15
|
-
import {
|
|
16
|
+
import { ArrowBack, Close } from '@cdx-ui/icons';
|
|
16
17
|
import { Heading } from '../Heading';
|
|
17
|
-
import {
|
|
18
|
+
import { IconButton } from '../IconButton';
|
|
18
19
|
import { Text } from '../Text';
|
|
19
20
|
import {
|
|
20
|
-
bottomSheetBackButtonVariants,
|
|
21
|
-
bottomSheetBackIconVariants,
|
|
22
|
-
bottomSheetCloseButtonVariants,
|
|
23
|
-
bottomSheetCloseIconVariants,
|
|
24
21
|
bottomSheetContainerVariants,
|
|
25
22
|
bottomSheetContentVariants,
|
|
26
23
|
bottomSheetFooterVariants,
|
|
27
24
|
bottomSheetHeaderVariants,
|
|
28
|
-
bottomSheetSubtitleVariants,
|
|
29
|
-
handleIndicatorStyle,
|
|
30
25
|
} from './styles';
|
|
31
26
|
|
|
32
27
|
/** Ref type for BottomSheet; forwards the @gorhom modal ref (present, dismiss, snapToIndex, minimize, restore, etc.). */
|
|
@@ -61,36 +56,35 @@ function BottomSheetHeaderComponent(
|
|
|
61
56
|
const computedClassName = cn(bottomSheetHeaderVariants({ hasTitle: hasContent }), className);
|
|
62
57
|
return (
|
|
63
58
|
<View ref={ref} className={computedClassName}>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
>
|
|
72
|
-
<Icon as={ArrowBackIosNew} className={bottomSheetBackIconVariants()} />
|
|
73
|
-
</Pressable>
|
|
74
|
-
)}
|
|
59
|
+
<BottomSheetBackButton
|
|
60
|
+
className={cn(!onBack && 'opacity-0')}
|
|
61
|
+
onPress={onBack}
|
|
62
|
+
{...(backAccessibilityLabel !== undefined && {
|
|
63
|
+
accessibilityLabel: backAccessibilityLabel,
|
|
64
|
+
})}
|
|
65
|
+
/>
|
|
75
66
|
<View className="flex-1">
|
|
76
67
|
{children ??
|
|
77
68
|
(hasTitle ? (
|
|
78
69
|
<View>
|
|
79
|
-
<Heading size="
|
|
70
|
+
<Heading size="md" className="text-center">
|
|
80
71
|
{title}
|
|
81
72
|
</Heading>
|
|
82
|
-
{subtitle ?
|
|
73
|
+
{subtitle ? (
|
|
74
|
+
<Text size="sm" className="text-center text-content-secondary">
|
|
75
|
+
{subtitle}
|
|
76
|
+
</Text>
|
|
77
|
+
) : null}
|
|
83
78
|
</View>
|
|
84
79
|
) : null)}
|
|
85
80
|
</View>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)}
|
|
81
|
+
<BottomSheetCloseButton
|
|
82
|
+
className={cn(!onClose && 'opacity-0')}
|
|
83
|
+
onPress={onClose}
|
|
84
|
+
{...(closeAccessibilityLabel !== undefined && {
|
|
85
|
+
accessibilityLabel: closeAccessibilityLabel,
|
|
86
|
+
})}
|
|
87
|
+
/>
|
|
94
88
|
</View>
|
|
95
89
|
);
|
|
96
90
|
}
|
|
@@ -100,11 +94,41 @@ BottomSheetHeaderComponent.displayName = 'BottomSheet.Header';
|
|
|
100
94
|
const BottomSheetHeader = forwardRef<View, BottomSheetHeaderProps>(BottomSheetHeaderComponent);
|
|
101
95
|
BottomSheetHeader.displayName = 'BottomSheet.Header';
|
|
102
96
|
|
|
97
|
+
// =============================================================================
|
|
98
|
+
// BACKBUTTON
|
|
99
|
+
// =============================================================================
|
|
100
|
+
interface BottomSheetBackButtonProps {
|
|
101
|
+
onPress?: () => void;
|
|
102
|
+
className?: string;
|
|
103
|
+
accessibilityLabel?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function BottomSheetBackButton({
|
|
107
|
+
onPress,
|
|
108
|
+
className,
|
|
109
|
+
accessibilityLabel = 'Go back',
|
|
110
|
+
}: Readonly<BottomSheetBackButtonProps>) {
|
|
111
|
+
return (
|
|
112
|
+
<IconButton
|
|
113
|
+
as={ArrowBack}
|
|
114
|
+
variant="solid"
|
|
115
|
+
color="neutral"
|
|
116
|
+
size="default"
|
|
117
|
+
onPress={onPress}
|
|
118
|
+
hitSlop={12}
|
|
119
|
+
accessibilityLabel={accessibilityLabel}
|
|
120
|
+
className={className}
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
BottomSheetBackButton.displayName = 'BottomSheet.BackButton';
|
|
126
|
+
|
|
103
127
|
// =============================================================================
|
|
104
128
|
// CLOSEBUTTON
|
|
105
129
|
// =============================================================================
|
|
106
130
|
interface BottomSheetCloseButtonProps {
|
|
107
|
-
onPress
|
|
131
|
+
onPress?: () => void;
|
|
108
132
|
className?: string;
|
|
109
133
|
accessibilityLabel?: string;
|
|
110
134
|
}
|
|
@@ -113,17 +137,18 @@ function BottomSheetCloseButton({
|
|
|
113
137
|
onPress,
|
|
114
138
|
className,
|
|
115
139
|
accessibilityLabel = 'Close',
|
|
116
|
-
}: BottomSheetCloseButtonProps) {
|
|
140
|
+
}: Readonly<BottomSheetCloseButtonProps>) {
|
|
117
141
|
return (
|
|
118
|
-
<
|
|
142
|
+
<IconButton
|
|
143
|
+
as={Close}
|
|
144
|
+
variant="solid"
|
|
145
|
+
color="neutral"
|
|
146
|
+
size="default"
|
|
119
147
|
onPress={onPress}
|
|
120
148
|
hitSlop={12}
|
|
121
|
-
accessibilityRole="button"
|
|
122
149
|
accessibilityLabel={accessibilityLabel}
|
|
123
|
-
className={
|
|
124
|
-
|
|
125
|
-
<Icon as={Close} className={bottomSheetCloseIconVariants()} />
|
|
126
|
-
</Pressable>
|
|
150
|
+
className={className}
|
|
151
|
+
/>
|
|
127
152
|
);
|
|
128
153
|
}
|
|
129
154
|
|
|
@@ -202,6 +227,13 @@ export interface BottomSheetModalProps extends React.ComponentPropsWithoutRef<
|
|
|
202
227
|
|
|
203
228
|
const MAX_DYNAMIC_CONTENT_SIZE = Dimensions.get('window').height * 0.9;
|
|
204
229
|
|
|
230
|
+
const SHEET_BORDER_RADIUS = 24;
|
|
231
|
+
const sheetContainerStyle: ViewStyle = {
|
|
232
|
+
overflow: 'hidden',
|
|
233
|
+
borderTopLeftRadius: SHEET_BORDER_RADIUS,
|
|
234
|
+
borderTopRightRadius: SHEET_BORDER_RADIUS,
|
|
235
|
+
};
|
|
236
|
+
|
|
205
237
|
const CustomBackdrop = (props: BottomSheetBackdropProps) => {
|
|
206
238
|
return (
|
|
207
239
|
<BottomSheetBackdrop
|
|
@@ -214,10 +246,14 @@ const CustomBackdrop = (props: BottomSheetBackdropProps) => {
|
|
|
214
246
|
);
|
|
215
247
|
};
|
|
216
248
|
|
|
249
|
+
const CustomBackground = (props: React.ComponentPropsWithoutRef<typeof View>) => (
|
|
250
|
+
<View {...props} className={cn(bottomSheetContainerVariants())} />
|
|
251
|
+
);
|
|
252
|
+
|
|
217
253
|
const BottomSheetModalStyled = forwardRef<
|
|
218
254
|
React.ComponentRef<typeof BottomSheetModal>,
|
|
219
255
|
BottomSheetModalProps
|
|
220
|
-
>(({ handleVisible = true, footer, ...rest }, ref) => {
|
|
256
|
+
>(({ handleVisible = true, footer, onDismiss, onChange, ...rest }, ref) => {
|
|
221
257
|
const renderFooter = useCallback(
|
|
222
258
|
(props: GorhomBottomSheetFooterProps) => (
|
|
223
259
|
<GorhomBottomSheetFooter {...props}>{footer}</GorhomBottomSheetFooter>
|
|
@@ -225,18 +261,39 @@ const BottomSheetModalStyled = forwardRef<
|
|
|
225
261
|
[footer],
|
|
226
262
|
);
|
|
227
263
|
|
|
264
|
+
const dismissedRef = useRef(false);
|
|
265
|
+
|
|
266
|
+
const handleDismiss = useCallback(() => {
|
|
267
|
+
if (dismissedRef.current) return;
|
|
268
|
+
dismissedRef.current = true;
|
|
269
|
+
onDismiss?.();
|
|
270
|
+
}, [onDismiss]);
|
|
271
|
+
|
|
272
|
+
const handleChange = useCallback(
|
|
273
|
+
(...args: Parameters<NonNullable<BottomSheetModalProps['onChange']>>) => {
|
|
274
|
+
dismissedRef.current = false;
|
|
275
|
+
onChange?.(...args);
|
|
276
|
+
},
|
|
277
|
+
[onChange],
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
const handleIndicatorStyle = useResolveClassNames(
|
|
281
|
+
'bg-surface-neutral-subtle w-9 h-1 rounded-full',
|
|
282
|
+
);
|
|
283
|
+
|
|
228
284
|
return (
|
|
229
285
|
<BottomSheetModal
|
|
230
286
|
ref={ref}
|
|
231
287
|
enableDynamicSizing
|
|
232
288
|
maxDynamicContentSize={MAX_DYNAMIC_CONTENT_SIZE}
|
|
289
|
+
style={sheetContainerStyle}
|
|
233
290
|
handleComponent={handleVisible ? undefined : null}
|
|
234
291
|
handleIndicatorStyle={handleVisible ? handleIndicatorStyle : undefined}
|
|
235
292
|
backdropComponent={CustomBackdrop}
|
|
236
|
-
backgroundComponent={
|
|
237
|
-
<View {...backgroundProps} className={cn(bottomSheetContainerVariants())} />
|
|
238
|
-
)}
|
|
293
|
+
backgroundComponent={CustomBackground}
|
|
239
294
|
{...rest}
|
|
295
|
+
onDismiss={handleDismiss}
|
|
296
|
+
onChange={handleChange}
|
|
240
297
|
{...(footer != null && { footerComponent: renderFooter })}
|
|
241
298
|
/>
|
|
242
299
|
);
|
|
@@ -1,27 +1,15 @@
|
|
|
1
|
-
import { type ViewStyle } from 'react-native';
|
|
2
1
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
3
|
-
import {
|
|
4
|
-
COLOR_BG_PRIMARY,
|
|
5
|
-
COLOR_BORDER_DEFAULT,
|
|
6
|
-
COLOR_TEXT_PRIMARY,
|
|
7
|
-
COLOR_TEXT_SECONDARY,
|
|
8
|
-
} from '../../styles/primitives';
|
|
9
|
-
|
|
10
|
-
// ── Container ──────────────────────────────────────────────
|
|
11
2
|
|
|
12
3
|
export const bottomSheetContainerVariants = cva([
|
|
13
|
-
'rounded-t-3xl
|
|
14
|
-
|
|
15
|
-
COLOR_BORDER_DEFAULT,
|
|
4
|
+
'rounded-t-3xl overflow-hidden',
|
|
5
|
+
'bg-surface-primary',
|
|
16
6
|
]);
|
|
17
7
|
|
|
18
|
-
// ── Header ─────────────────────────────────────────────────
|
|
19
|
-
|
|
20
8
|
export const bottomSheetHeaderVariants = cva(
|
|
21
9
|
[
|
|
22
10
|
'flex-row items-center justify-between min-h-12 px-4 pt-3 pb-2',
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
'bg-surface-primary',
|
|
12
|
+
'text-content-primary',
|
|
25
13
|
],
|
|
26
14
|
{
|
|
27
15
|
variants: {
|
|
@@ -36,44 +24,16 @@ export const bottomSheetHeaderVariants = cva(
|
|
|
36
24
|
},
|
|
37
25
|
);
|
|
38
26
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
export const bottomSheetContentVariants = cva([COLOR_BG_PRIMARY, 'px-4 pb-6']);
|
|
42
|
-
|
|
43
|
-
// ── Footer ─────────────────────────────────────────────────
|
|
27
|
+
export const bottomSheetContentVariants = cva(['bg-surface-primary', 'p-5 pb-6']);
|
|
44
28
|
|
|
45
29
|
export const bottomSheetFooterVariants = cva([
|
|
46
30
|
'px-4 pt-3 pb-4 border-t',
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
'bg-surface-primary',
|
|
32
|
+
'border-stroke-secondary',
|
|
49
33
|
]);
|
|
50
34
|
|
|
51
|
-
// ── Subtitle ────────────────────────────────────────────────
|
|
52
|
-
|
|
53
|
-
export const bottomSheetSubtitleVariants = cva(['text-sm', COLOR_TEXT_SECONDARY]);
|
|
54
|
-
|
|
55
|
-
// ── Back Button ─────────────────────────────────────────────
|
|
56
|
-
|
|
57
|
-
export const bottomSheetBackButtonVariants = cva(['p-1 -m-1 mr-2 rounded-full active:opacity-70']);
|
|
58
|
-
|
|
59
35
|
export const bottomSheetBackIconVariants = cva(['size-5 text-slate-500']);
|
|
60
36
|
|
|
61
|
-
// ── Close Button ───────────────────────────────────────────
|
|
62
|
-
|
|
63
|
-
export const bottomSheetCloseButtonVariants = cva(['p-1 -m-1 rounded-full active:opacity-70']);
|
|
64
|
-
|
|
65
|
-
// ── Close Icon ─────────────────────────────────────────────
|
|
66
|
-
|
|
67
37
|
export const bottomSheetCloseIconVariants = cva(['size-5 text-slate-500']);
|
|
68
38
|
|
|
69
|
-
// ── Handle indicator ────────────────────────────────────────
|
|
70
|
-
// Plain ViewStyle — gorhom's handleIndicatorStyle doesn't accept classNames.
|
|
71
|
-
// slate-300 (#cbd5e1) maps to COLOR_BORDER_STRONG; update when raw token palette is available (TODO).
|
|
72
|
-
export const handleIndicatorStyle: ViewStyle = {
|
|
73
|
-
backgroundColor: '#cbd5e1',
|
|
74
|
-
width: 36,
|
|
75
|
-
height: 4,
|
|
76
|
-
borderRadius: 2,
|
|
77
|
-
};
|
|
78
|
-
|
|
79
39
|
export type BottomSheetHeaderVariants = VariantProps<typeof bottomSheetHeaderVariants>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { forwardRef, useState } from 'react';
|
|
2
|
+
import { type TextInput, type TextInputProps, type ViewProps } from 'react-native';
|
|
3
|
+
import {
|
|
4
|
+
InputField as InputFieldPrimitive,
|
|
5
|
+
InputRoot as InputRootPrimitive,
|
|
6
|
+
dataAttributes,
|
|
7
|
+
type IInputFieldProps,
|
|
8
|
+
} from '@cdx-ui/primitives';
|
|
9
|
+
import { cn } from '@cdx-ui/utils';
|
|
10
|
+
import {
|
|
11
|
+
type CurrencyInputVariantProps,
|
|
12
|
+
currencyInputFieldVariants,
|
|
13
|
+
currencyInputRootVariants,
|
|
14
|
+
} from './styles';
|
|
15
|
+
|
|
16
|
+
// Hoisted at module scope — Intl instantiation is expensive (parses locale data).
|
|
17
|
+
const currencyFormatter = new Intl.NumberFormat('en-US', {
|
|
18
|
+
minimumFractionDigits: 2,
|
|
19
|
+
maximumFractionDigits: 2,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Cap the accumulated cents to 15 digits so the integer stays below
|
|
23
|
+
// Number.MAX_SAFE_INTEGER (16 digits). Past that, parsing loses precision and the
|
|
24
|
+
// amount garbles or resets — 15 digits already allows up to $9,999,999,999,999.99.
|
|
25
|
+
const MAX_DIGITS = 15;
|
|
26
|
+
|
|
27
|
+
function centsToAmountDisplay(cents: number): string {
|
|
28
|
+
return currencyFormatter.format(cents / 100);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function valueToCents(value: number | undefined | null): number {
|
|
32
|
+
return Math.round((value ?? 0) * 100);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CurrencyInputProps
|
|
36
|
+
extends
|
|
37
|
+
IInputFieldProps,
|
|
38
|
+
CurrencyInputVariantProps,
|
|
39
|
+
Pick<
|
|
40
|
+
TextInputProps,
|
|
41
|
+
'accessibilityLabel' | 'accessibilityHint' | 'testID' | 'onFocus' | 'onBlur'
|
|
42
|
+
> {
|
|
43
|
+
/** Current numeric value. Renders formatted zero when undefined or null. */
|
|
44
|
+
value?: number;
|
|
45
|
+
/** Called with the parsed number when the user changes the amount. */
|
|
46
|
+
onChangeValue?: (value: number) => void;
|
|
47
|
+
/** Currency symbol rendered as an inline prefix. Defaults to `"$"`. */
|
|
48
|
+
currencySymbol?: string;
|
|
49
|
+
className?: string;
|
|
50
|
+
style?: ViewProps['style'];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const CurrencyInput = forwardRef<TextInput, CurrencyInputProps>(
|
|
54
|
+
(
|
|
55
|
+
{
|
|
56
|
+
value,
|
|
57
|
+
onChangeValue,
|
|
58
|
+
currencySymbol = '$',
|
|
59
|
+
size = 'default',
|
|
60
|
+
isDisabled,
|
|
61
|
+
isInvalid,
|
|
62
|
+
isReadOnly,
|
|
63
|
+
isRequired,
|
|
64
|
+
isFocused,
|
|
65
|
+
isHovered,
|
|
66
|
+
isFullWidth,
|
|
67
|
+
className,
|
|
68
|
+
style,
|
|
69
|
+
accessibilityLabel,
|
|
70
|
+
accessibilityHint,
|
|
71
|
+
testID,
|
|
72
|
+
onFocus,
|
|
73
|
+
onBlur,
|
|
74
|
+
},
|
|
75
|
+
ref,
|
|
76
|
+
) => {
|
|
77
|
+
// null = display mode; number = active editing cents.
|
|
78
|
+
// Calculator-style cents accumulation: digits are always extracted from whatever
|
|
79
|
+
// the user types, interpreted as integer cents, then reformatted. The caret is
|
|
80
|
+
// kept at the end at all times so entry behaves like a banking amount pad — new
|
|
81
|
+
// digits append, backspace removes the rightmost digit.
|
|
82
|
+
const [editingCents, setEditingCents] = useState<number | null>(null);
|
|
83
|
+
|
|
84
|
+
// Controlled caret, pinned to the end of the formatted string. `undefined` in
|
|
85
|
+
// display mode so we never fight the platform when the field is not being edited.
|
|
86
|
+
const [selection, setSelection] = useState<{ start: number; end: number }>();
|
|
87
|
+
|
|
88
|
+
type FocusHandler = NonNullable<TextInputProps['onFocus']>;
|
|
89
|
+
type BlurHandler = NonNullable<TextInputProps['onBlur']>;
|
|
90
|
+
type SelectionChangeHandler = NonNullable<TextInputProps['onSelectionChange']>;
|
|
91
|
+
|
|
92
|
+
const displayCents = editingCents !== null ? editingCents : valueToCents(value);
|
|
93
|
+
const amountDisplay = centsToAmountDisplay(displayCents);
|
|
94
|
+
const fullDisplay = `${currencySymbol}${amountDisplay}`;
|
|
95
|
+
const isEmpty = displayCents === 0;
|
|
96
|
+
|
|
97
|
+
// Index just past the last character of the formatted string for a given cents.
|
|
98
|
+
// Computed from `cents` (not the render-closure `fullDisplay`) so the caret is
|
|
99
|
+
// correct in the same commit as a value change, even when a reformat adds or
|
|
100
|
+
// removes a grouping comma (e.g. 444.44 → 3,444.44) and changes the length.
|
|
101
|
+
const caretIndexFor = (cents: number) =>
|
|
102
|
+
`${currencySymbol}${centsToAmountDisplay(cents)}`.length;
|
|
103
|
+
|
|
104
|
+
const pinCaret = (end: number) => setSelection({ start: end, end });
|
|
105
|
+
|
|
106
|
+
const handleFocus: FocusHandler = (e) => {
|
|
107
|
+
// Initialise from the current value so existing amounts are preserved on focus.
|
|
108
|
+
const cents = valueToCents(value);
|
|
109
|
+
setEditingCents(cents);
|
|
110
|
+
pinCaret(caretIndexFor(cents));
|
|
111
|
+
onFocus?.(e);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const handleBlur: BlurHandler = (e) => {
|
|
115
|
+
setEditingCents(null);
|
|
116
|
+
setSelection(undefined);
|
|
117
|
+
onBlur?.(e);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const handleChangeText = (text: string) => {
|
|
121
|
+
// Extract only digit characters, interpret the resulting integer as cents.
|
|
122
|
+
// e.g. user typing '1' after '0.00' → digits '0001' → 1 cent → 0.01
|
|
123
|
+
// Backspace on '1.23' → '1.2' → digits '12' → 12 cents → 0.12
|
|
124
|
+
// Digits past the cap are dropped, so extra keystrokes are ignored rather
|
|
125
|
+
// than overflowing into an unsafe integer.
|
|
126
|
+
const digits = text.replace(/\D/g, '').slice(0, MAX_DIGITS);
|
|
127
|
+
const cents = parseInt(digits || '0', 10);
|
|
128
|
+
setEditingCents(cents);
|
|
129
|
+
pinCaret(caretIndexFor(cents));
|
|
130
|
+
onChangeValue?.(cents / 100);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const handleSelectionChange: SelectionChangeHandler = (e) => {
|
|
134
|
+
// Re-pin the caret to the end ONLY when it drifts to the LEFT of the end while
|
|
135
|
+
// editing — e.g. web hardware-keyboard arrow keys or a click/tap into the middle
|
|
136
|
+
// (native decimal-pad keyboards have no arrow keys, so those platforms rarely
|
|
137
|
+
// trigger this). Value-driven changes already pin the caret via
|
|
138
|
+
// `handleChangeText`/`handleFocus`.
|
|
139
|
+
//
|
|
140
|
+
// Crucially we do NOT touch the caret when it sits at or past the last-rendered
|
|
141
|
+
// end. While typing, the native caret momentarily lands one past our last render
|
|
142
|
+
// (the new value hasn't committed yet); re-pinning there would tug it back to the
|
|
143
|
+
// stale, one-shorter end and yank it one position left — the classic controlled-
|
|
144
|
+
// selection race. Ignoring forward positions sidesteps that entirely, and also
|
|
145
|
+
// prevents a feedback loop (the pin re-emits a selectionChange already at the end).
|
|
146
|
+
if (editingCents === null) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const end = fullDisplay.length;
|
|
150
|
+
const { selection: current } = e.nativeEvent;
|
|
151
|
+
if (current.end < end) {
|
|
152
|
+
pinCaret(end);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<InputRootPrimitive
|
|
158
|
+
isDisabled={isDisabled}
|
|
159
|
+
isInvalid={isInvalid}
|
|
160
|
+
isReadOnly={isReadOnly}
|
|
161
|
+
isRequired={isRequired}
|
|
162
|
+
isFocused={isFocused}
|
|
163
|
+
isHovered={isHovered}
|
|
164
|
+
isFullWidth={isFullWidth}
|
|
165
|
+
className={cn(currencyInputRootVariants({ size }), className)}
|
|
166
|
+
style={style}
|
|
167
|
+
{...dataAttributes({ slot: 'currency-input' })}
|
|
168
|
+
>
|
|
169
|
+
<InputFieldPrimitive
|
|
170
|
+
ref={ref}
|
|
171
|
+
value={fullDisplay}
|
|
172
|
+
selection={selection}
|
|
173
|
+
onChangeText={handleChangeText}
|
|
174
|
+
onSelectionChange={handleSelectionChange}
|
|
175
|
+
keyboardType="decimal-pad"
|
|
176
|
+
accessibilityLabel={accessibilityLabel}
|
|
177
|
+
accessibilityHint={accessibilityHint}
|
|
178
|
+
accessibilityValue={{ text: fullDisplay }}
|
|
179
|
+
testID={testID}
|
|
180
|
+
onFocus={handleFocus}
|
|
181
|
+
onBlur={handleBlur}
|
|
182
|
+
className={cn(
|
|
183
|
+
currencyInputFieldVariants({ size }),
|
|
184
|
+
isEmpty ? 'text-content-tertiary' : 'text-content-primary',
|
|
185
|
+
)}
|
|
186
|
+
/>
|
|
187
|
+
</InputRootPrimitive>
|
|
188
|
+
);
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
CurrencyInput.displayName = 'CurrencyInput';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
2
|
+
import { DISABLED_CURSOR } from '../../styles/primitives';
|
|
3
|
+
|
|
4
|
+
export const currencyInputRootVariants = cva(
|
|
5
|
+
[
|
|
6
|
+
'w-full items-center justify-center',
|
|
7
|
+
'data-[disabled=true]:opacity-[var(--opacity-disabled)]',
|
|
8
|
+
DISABLED_CURSOR,
|
|
9
|
+
'data-[readonly=true]:cursor-default',
|
|
10
|
+
],
|
|
11
|
+
{
|
|
12
|
+
variants: {
|
|
13
|
+
size: {
|
|
14
|
+
default: 'py-1',
|
|
15
|
+
small: 'py-0.5',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
size: 'default',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export const currencyInputFieldVariants = cva(
|
|
25
|
+
[
|
|
26
|
+
'w-full bg-transparent p-0 text-center',
|
|
27
|
+
'placeholder:text-content-tertiary',
|
|
28
|
+
'web:outline-none',
|
|
29
|
+
'data-[invalid=true]:text-content-danger',
|
|
30
|
+
],
|
|
31
|
+
{
|
|
32
|
+
variants: {
|
|
33
|
+
size: {
|
|
34
|
+
default: 'heading-xl',
|
|
35
|
+
small: 'heading-lg',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: {
|
|
39
|
+
size: 'default',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export type CurrencyInputVariantProps = VariantProps<typeof currencyInputRootVariants>;
|
|
@@ -46,7 +46,7 @@ export const linkNativeUnderlineVariants = cva(['border-b border-content-link pb
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
export const linkIconVariants = cva(
|
|
49
|
-
['size-4', Platform.select({ web: 'text-
|
|
49
|
+
['size-4', Platform.select({ web: 'text-current', default: 'text-content-link' })],
|
|
50
50
|
{
|
|
51
51
|
variants: {
|
|
52
52
|
visited: {
|