@cdx-ui/components 0.0.1-beta.76 → 0.0.1-beta.78

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.
Files changed (44) hide show
  1. package/lib/commonjs/components/BottomSheet/index.js +68 -25
  2. package/lib/commonjs/components/BottomSheet/index.js.map +1 -1
  3. package/lib/commonjs/components/BottomSheet/styles.js +5 -42
  4. package/lib/commonjs/components/BottomSheet/styles.js.map +1 -1
  5. package/lib/commonjs/components/CurrencyInput/index.js +157 -0
  6. package/lib/commonjs/components/CurrencyInput/index.js.map +1 -0
  7. package/lib/commonjs/components/CurrencyInput/styles.js +31 -0
  8. package/lib/commonjs/components/CurrencyInput/styles.js.map +1 -0
  9. package/lib/commonjs/components/Link/styles.js +1 -1
  10. package/lib/commonjs/components/Select/styles.js +1 -1
  11. package/lib/commonjs/components/Select/styles.js.map +1 -1
  12. package/lib/commonjs/components/index.js +12 -0
  13. package/lib/commonjs/components/index.js.map +1 -1
  14. package/lib/module/components/BottomSheet/index.js +70 -27
  15. package/lib/module/components/BottomSheet/index.js.map +1 -1
  16. package/lib/module/components/BottomSheet/styles.js +4 -42
  17. package/lib/module/components/BottomSheet/styles.js.map +1 -1
  18. package/lib/module/components/CurrencyInput/index.js +154 -0
  19. package/lib/module/components/CurrencyInput/index.js.map +1 -0
  20. package/lib/module/components/CurrencyInput/styles.js +27 -0
  21. package/lib/module/components/CurrencyInput/styles.js.map +1 -0
  22. package/lib/module/components/Link/styles.js +1 -1
  23. package/lib/module/components/Select/styles.js +1 -1
  24. package/lib/module/components/Select/styles.js.map +1 -1
  25. package/lib/module/components/index.js +1 -0
  26. package/lib/module/components/index.js.map +1 -1
  27. package/lib/typescript/components/BottomSheet/index.d.ts.map +1 -1
  28. package/lib/typescript/components/BottomSheet/styles.d.ts +0 -5
  29. package/lib/typescript/components/BottomSheet/styles.d.ts.map +1 -1
  30. package/lib/typescript/components/CurrencyInput/index.d.ts +15 -0
  31. package/lib/typescript/components/CurrencyInput/index.d.ts.map +1 -0
  32. package/lib/typescript/components/CurrencyInput/styles.d.ts +9 -0
  33. package/lib/typescript/components/CurrencyInput/styles.d.ts.map +1 -0
  34. package/lib/typescript/components/Select/styles.d.ts.map +1 -1
  35. package/lib/typescript/components/index.d.ts +1 -0
  36. package/lib/typescript/components/index.d.ts.map +1 -1
  37. package/package.json +4 -4
  38. package/src/components/BottomSheet/index.tsx +99 -42
  39. package/src/components/BottomSheet/styles.ts +7 -47
  40. package/src/components/CurrencyInput/index.tsx +192 -0
  41. package/src/components/CurrencyInput/styles.ts +44 -0
  42. package/src/components/Link/styles.ts +1 -1
  43. package/src/components/Select/styles.ts +0 -2
  44. 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,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAUlE,eAAO,MAAM,4BAA4B,oFAIvC,CAAC;AAIH,eAAO,MAAM,yBAAyB;;8EAiBrC,CAAC;AAIF,eAAO,MAAM,0BAA0B,oFAAuC,CAAC;AAI/E,eAAO,MAAM,yBAAyB,oFAIpC,CAAC;AAIH,eAAO,MAAM,2BAA2B,oFAAyC,CAAC;AAIlF,eAAO,MAAM,6BAA6B,oFAAwD,CAAC;AAEnG,eAAO,MAAM,2BAA2B,oFAAiC,CAAC;AAI1E,eAAO,MAAM,8BAA8B,oFAAmD,CAAC;AAI/F,eAAO,MAAM,4BAA4B,oFAAiC,CAAC;AAK3E,eAAO,MAAM,oBAAoB,EAAE,SAKlC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,YAAY,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
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;;;8EAgDjC,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
+ {"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"}
@@ -7,6 +7,7 @@ export * from './Button';
7
7
  export * from './Card';
8
8
  export * from './Checkbox';
9
9
  export * from './Chip';
10
+ export * from './CurrencyInput';
10
11
  export * from './Dialog';
11
12
  export * from './Divider';
12
13
  export * from './Field';
@@ -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.76",
3
+ "version": "0.0.1-beta.78",
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/primitives": "0.0.1-beta.76",
71
- "@cdx-ui/utils": "0.0.1-beta.76",
72
- "@cdx-ui/icons": "0.0.1-beta.76"
70
+ "@cdx-ui/primitives": "0.0.1-beta.78",
71
+ "@cdx-ui/utils": "0.0.1-beta.78",
72
+ "@cdx-ui/icons": "0.0.1-beta.78"
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, Pressable, View, ViewStyle } from 'react-native';
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 { ArrowBackIosNew, Close } from '@cdx-ui/icons';
16
+ import { ArrowBack, Close } from '@cdx-ui/icons';
16
17
  import { Heading } from '../Heading';
17
- import { Icon } from '../Icon';
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
- {onBack && (
65
- <Pressable
66
- onPress={onBack}
67
- hitSlop={12}
68
- accessibilityRole="button"
69
- accessibilityLabel={backAccessibilityLabel}
70
- className={cn(bottomSheetBackButtonVariants())}
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="xs" className="text-lg">
70
+ <Heading size="md" className="text-center">
80
71
  {title}
81
72
  </Heading>
82
- {subtitle ? <Text className={bottomSheetSubtitleVariants()}>{subtitle}</Text> : null}
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
- {onClose && (
87
- <BottomSheetCloseButton
88
- onPress={onClose}
89
- {...(closeAccessibilityLabel !== undefined && {
90
- accessibilityLabel: closeAccessibilityLabel,
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: () => void;
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
- <Pressable
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={cn(bottomSheetCloseButtonVariants(), 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={(backgroundProps) => (
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 border-t border-x overflow-hidden',
14
- COLOR_BG_PRIMARY,
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
- COLOR_BG_PRIMARY,
24
- COLOR_TEXT_PRIMARY,
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
- // ── Content ────────────────────────────────────────────────
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
- COLOR_BG_PRIMARY,
48
- COLOR_BORDER_DEFAULT,
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-inherit', default: 'text-content-link' })],
49
+ ['size-4', Platform.select({ web: 'text-current', default: 'text-content-link' })],
50
50
  {
51
51
  variants: {
52
52
  visited: {