@retray-dev/ui-kit 1.5.0 → 1.6.0
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/COMPONENTS.md +51 -2
- package/README.md +2 -2
- package/dist/index.d.mts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +46 -2
- package/dist/index.mjs +47 -4
- package/package.json +6 -6
- package/src/components/CurrencyInput/CurrencyInput.tsx +65 -0
- package/src/components/CurrencyInput/index.ts +2 -0
- package/src/components/Toast/Toast.tsx +3 -3
- package/src/index.ts +1 -0
package/COMPONENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @retray-dev/ui-kit — Component Reference
|
|
1
|
+
# @retray-dev/ui-kit — Component Reference (v1.6.0)
|
|
2
2
|
|
|
3
3
|
This file is the AI reference for this package. It is shipped inside the npm package so consuming projects can import it into their `CLAUDE.md` with:
|
|
4
4
|
|
|
@@ -74,7 +74,7 @@ function MyComponent() {
|
|
|
74
74
|
|
|
75
75
|
## Theme Tokens
|
|
76
76
|
|
|
77
|
-
All
|
|
77
|
+
All 20 tokens are available via `useTheme().colors`.
|
|
78
78
|
|
|
79
79
|
| Token | Light | Dark | Semantic Role |
|
|
80
80
|
|-------|-------|------|---------------|
|
|
@@ -95,6 +95,8 @@ All 18 tokens are available via `useTheme().colors`.
|
|
|
95
95
|
| `border` | `#e5e5e5` | `#2a2a2a` | Borders and dividers |
|
|
96
96
|
| `input` | `#e5e5e5` | `#2a2a2a` | Input field border color |
|
|
97
97
|
| `ring` | `#a3a3a3` | `#d4d4d4` | Focus ring color |
|
|
98
|
+
| `success` | `#16a34a` | `#22c55e` | Success state (Toast success variant) |
|
|
99
|
+
| `successForeground` | `#ffffff` | `#ffffff` | Text on success background |
|
|
98
100
|
|
|
99
101
|
---
|
|
100
102
|
|
|
@@ -145,6 +147,8 @@ All 18 tokens are available via `useTheme().colors`.
|
|
|
145
147
|
| loading | `boolean` | `false` | Replaces label with a spinner and forces disabled state |
|
|
146
148
|
| fullWidth | `boolean` | `false` | Stretches to container width (`alignSelf: 'stretch'`) |
|
|
147
149
|
| disabled | `boolean` | — | Reduces opacity to 0.45 |
|
|
150
|
+
| icon | `React.ReactNode` | — | Icon rendered alongside the label |
|
|
151
|
+
| iconPosition | `'left' \| 'right'` | `'left'` | Side the icon appears on |
|
|
148
152
|
|
|
149
153
|
**Variants:**
|
|
150
154
|
- `primary`: filled with `primary` token — main actions
|
|
@@ -160,6 +164,7 @@ All 18 tokens are available via `useTheme().colors`.
|
|
|
160
164
|
<Button label="Cancel" variant="ghost" onPress={onCancel} />
|
|
161
165
|
<Button label="Delete" variant="outline" size="sm" />
|
|
162
166
|
<Button label="Submitting..." loading fullWidth />
|
|
167
|
+
<Button label="Share" icon={<ShareIcon size={16} />} iconPosition="right" />
|
|
163
168
|
```
|
|
164
169
|
|
|
165
170
|
---
|
|
@@ -187,6 +192,50 @@ All 18 tokens are available via `useTheme().colors`.
|
|
|
187
192
|
|
|
188
193
|
---
|
|
189
194
|
|
|
195
|
+
### CurrencyInput
|
|
196
|
+
|
|
197
|
+
**Import:** `import { CurrencyInput } from '@retray-dev/ui-kit'`
|
|
198
|
+
**When to use:** Monetary or numeric inputs that need thousands formatting while typing. Wraps `Input` — shares the same visual design, label, error, and hint behavior.
|
|
199
|
+
|
|
200
|
+
| Prop | Type | Default | Notes |
|
|
201
|
+
|------|------|---------|-------|
|
|
202
|
+
| value | `string` | — | Controlled display value (includes prefix, e.g. `'$1,234'`) |
|
|
203
|
+
| onChangeText | `(formatted: string) => void` | — | Called with the formatted display string |
|
|
204
|
+
| onChangeValue | `(raw: number) => void` | — | Called with the parsed number (no separators, no prefix) |
|
|
205
|
+
| prefix | `string` | `'$'` | Symbol prepended to the formatted value |
|
|
206
|
+
| thousandsSeparator | `'.' \| ','` | `'.'` | Character used to separate groups of three digits |
|
|
207
|
+
| label | `string` | — | Label above the input |
|
|
208
|
+
| error | `string` | — | Error text below; turns border red |
|
|
209
|
+
| hint | `string` | — | Helper text below (hidden when `error` is set) |
|
|
210
|
+
| placeholder | `string` | `'$0'` | Defaults to `prefix + '0'` |
|
|
211
|
+
| editable | `boolean` | — | Pass `false` to disable editing |
|
|
212
|
+
| containerStyle | `ViewStyle` | — | Style for the outer container |
|
|
213
|
+
|
|
214
|
+
**Example:**
|
|
215
|
+
```tsx
|
|
216
|
+
const [display, setDisplay] = useState('')
|
|
217
|
+
const [amount, setAmount] = useState(0)
|
|
218
|
+
|
|
219
|
+
<CurrencyInput
|
|
220
|
+
label="Amount"
|
|
221
|
+
value={display}
|
|
222
|
+
onChangeText={setDisplay}
|
|
223
|
+
onChangeValue={setAmount}
|
|
224
|
+
hint={`Parsed: ${amount}`}
|
|
225
|
+
/>
|
|
226
|
+
|
|
227
|
+
// European format (dot as thousands separator)
|
|
228
|
+
<CurrencyInput
|
|
229
|
+
prefix="€"
|
|
230
|
+
thousandsSeparator="."
|
|
231
|
+
value={display}
|
|
232
|
+
onChangeText={setDisplay}
|
|
233
|
+
onChangeValue={setAmount}
|
|
234
|
+
/>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
190
239
|
### Textarea
|
|
191
240
|
|
|
192
241
|
**Import:** `import { Textarea } from '@retray-dev/ui-kit'`
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A personal React Native / Expo UI component library with a built-in design system, dark mode support, haptic feedback, and smooth animations.
|
|
4
4
|
|
|
5
|
-
-
|
|
5
|
+
- 24 components across 5 categories
|
|
6
6
|
- Light/dark theme with 18 color tokens and full customization
|
|
7
7
|
- Apple HIG–compliant touch targets and haptic feedback
|
|
8
8
|
- Animated interactions: spring press, sliding tabs, accordion easing, animated progress
|
|
@@ -101,7 +101,7 @@ const { colors, colorScheme } = useTheme()
|
|
|
101
101
|
| ----------- | ----------------------------------------------------------------------------------------------- |
|
|
102
102
|
| Display | `Text`, `Badge`, `Avatar`, `Separator`, `Spinner`, `Skeleton`, `Progress` |
|
|
103
103
|
| Surfaces | `Card`, `Alert`, `EmptyState` |
|
|
104
|
-
| Form | `Button`, `Input`, `Textarea`, `Checkbox`, `Switch`, `Toggle`, `RadioGroup`, `Select`, `Slider` |
|
|
104
|
+
| Form | `Button`, `Input`, `CurrencyInput`, `Textarea`, `Checkbox`, `Switch`, `Toggle`, `RadioGroup`, `Select`, `Slider` |
|
|
105
105
|
| Composition | `Tabs`, `Accordion` |
|
|
106
106
|
| Overlays | `Sheet` |
|
|
107
107
|
| Feedback | `Toast` / `ToastProvider` / `useToast` |
|
package/dist/index.d.mts
CHANGED
|
@@ -367,4 +367,23 @@ interface ToastProviderProps {
|
|
|
367
367
|
}
|
|
368
368
|
declare function ToastProvider({ children }: ToastProviderProps): React.JSX.Element;
|
|
369
369
|
|
|
370
|
-
|
|
370
|
+
interface CurrencyInputProps {
|
|
371
|
+
value?: string;
|
|
372
|
+
onChangeText?: (formatted: string) => void;
|
|
373
|
+
/** Called with the parsed numeric value (no separators, no prefix). */
|
|
374
|
+
onChangeValue?: (raw: number) => void;
|
|
375
|
+
/** Symbol prepended to the formatted value. Defaults to `'$'`. */
|
|
376
|
+
prefix?: string;
|
|
377
|
+
/** Character used to separate groups of three digits. Defaults to `'.'`. */
|
|
378
|
+
thousandsSeparator?: '.' | ',';
|
|
379
|
+
label?: string;
|
|
380
|
+
/** Red helper text; also changes input border to destructive color. */
|
|
381
|
+
error?: string;
|
|
382
|
+
hint?: string;
|
|
383
|
+
placeholder?: string;
|
|
384
|
+
editable?: boolean;
|
|
385
|
+
containerStyle?: ViewStyle;
|
|
386
|
+
}
|
|
387
|
+
declare function CurrencyInput({ value, onChangeText, onChangeValue, prefix, thousandsSeparator, label, error, hint, placeholder, editable, containerStyle, }: CurrencyInputProps): React.JSX.Element;
|
|
388
|
+
|
|
389
|
+
export { Accordion, type AccordionItem, type AccordionProps, Alert, type AlertProps, type AlertVariant, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type BadgeVariant, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, type CheckboxProps, type ColorScheme, CurrencyInput, type CurrencyInputProps, EmptyState, type EmptyStateProps, Input, type InputProps, Progress, type ProgressProps, RadioGroup, type RadioGroupProps, type RadioOption, Select, type SelectOption, type SelectProps, Separator, type SeparatorProps, Sheet, type SheetProps, Skeleton, type SkeletonProps, Slider, type SliderProps, Spinner, type SpinnerProps, type SpinnerSize, Switch, type SwitchProps, type TabItem, Tabs, TabsContent, type TabsContentProps, type TabsProps, Text, type TextProps, type TextVariant, Textarea, type TextareaProps, type Theme, type ThemeColors, ThemeProvider, type ThemeProviderProps, type ToastItem, ToastProvider, type ToastProviderProps, type ToastVariant, Toggle, type ToggleProps, type ToggleSize, type ToggleVariant, defaultDark, defaultLight, useTheme, useToast };
|
package/dist/index.d.ts
CHANGED
|
@@ -367,4 +367,23 @@ interface ToastProviderProps {
|
|
|
367
367
|
}
|
|
368
368
|
declare function ToastProvider({ children }: ToastProviderProps): React.JSX.Element;
|
|
369
369
|
|
|
370
|
-
|
|
370
|
+
interface CurrencyInputProps {
|
|
371
|
+
value?: string;
|
|
372
|
+
onChangeText?: (formatted: string) => void;
|
|
373
|
+
/** Called with the parsed numeric value (no separators, no prefix). */
|
|
374
|
+
onChangeValue?: (raw: number) => void;
|
|
375
|
+
/** Symbol prepended to the formatted value. Defaults to `'$'`. */
|
|
376
|
+
prefix?: string;
|
|
377
|
+
/** Character used to separate groups of three digits. Defaults to `'.'`. */
|
|
378
|
+
thousandsSeparator?: '.' | ',';
|
|
379
|
+
label?: string;
|
|
380
|
+
/** Red helper text; also changes input border to destructive color. */
|
|
381
|
+
error?: string;
|
|
382
|
+
hint?: string;
|
|
383
|
+
placeholder?: string;
|
|
384
|
+
editable?: boolean;
|
|
385
|
+
containerStyle?: ViewStyle;
|
|
386
|
+
}
|
|
387
|
+
declare function CurrencyInput({ value, onChangeText, onChangeValue, prefix, thousandsSeparator, label, error, hint, placeholder, editable, containerStyle, }: CurrencyInputProps): React.JSX.Element;
|
|
388
|
+
|
|
389
|
+
export { Accordion, type AccordionItem, type AccordionProps, Alert, type AlertProps, type AlertVariant, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type BadgeVariant, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, type CheckboxProps, type ColorScheme, CurrencyInput, type CurrencyInputProps, EmptyState, type EmptyStateProps, Input, type InputProps, Progress, type ProgressProps, RadioGroup, type RadioGroupProps, type RadioOption, Select, type SelectOption, type SelectProps, Separator, type SeparatorProps, Sheet, type SheetProps, Skeleton, type SkeletonProps, Slider, type SliderProps, Spinner, type SpinnerProps, type SpinnerSize, Switch, type SwitchProps, type TabItem, Tabs, TabsContent, type TabsContentProps, type TabsProps, Text, type TextProps, type TextVariant, Textarea, type TextareaProps, type Theme, type ThemeColors, ThemeProvider, type ThemeProviderProps, type ToastItem, ToastProvider, type ToastProviderProps, type ToastVariant, Toggle, type ToggleProps, type ToggleSize, type ToggleVariant, defaultDark, defaultLight, useTheme, useToast };
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ var Haptics11 = require('expo-haptics');
|
|
|
6
6
|
var expoLinearGradient = require('expo-linear-gradient');
|
|
7
7
|
var ReanimatedAnimated = require('react-native-reanimated');
|
|
8
8
|
var bottomSheet = require('@gorhom/bottom-sheet');
|
|
9
|
+
var reactNativeWorklets = require('react-native-worklets');
|
|
9
10
|
var reactNativeGestureHandler = require('react-native-gesture-handler');
|
|
10
11
|
var reactNativeSafeAreaContext = require('react-native-safe-area-context');
|
|
11
12
|
|
|
@@ -1617,7 +1618,7 @@ function ToastNotification({ item, onDismiss }) {
|
|
|
1617
1618
|
const timer = setTimeout(() => {
|
|
1618
1619
|
translateY.value = ReanimatedAnimated.withTiming(-80, { duration: 200 });
|
|
1619
1620
|
opacity.value = ReanimatedAnimated.withTiming(0, { duration: 200 }, (done) => {
|
|
1620
|
-
if (done)
|
|
1621
|
+
if (done) reactNativeWorklets.scheduleOnRN(onDismiss);
|
|
1621
1622
|
});
|
|
1622
1623
|
}, item.duration ?? 3e3);
|
|
1623
1624
|
return () => clearTimeout(timer);
|
|
@@ -1629,7 +1630,7 @@ function ToastNotification({ item, onDismiss }) {
|
|
|
1629
1630
|
if (shouldDismiss) {
|
|
1630
1631
|
const direction = translateX.value > 0 ? 1 : -1;
|
|
1631
1632
|
translateX.value = ReanimatedAnimated.withTiming(direction * 500, { duration: 200 }, (done) => {
|
|
1632
|
-
if (done)
|
|
1633
|
+
if (done) reactNativeWorklets.scheduleOnRN(onDismiss);
|
|
1633
1634
|
});
|
|
1634
1635
|
opacity.value = ReanimatedAnimated.withTiming(0, { duration: 150 });
|
|
1635
1636
|
} else {
|
|
@@ -1710,6 +1711,48 @@ var styles21 = reactNative.StyleSheet.create({
|
|
|
1710
1711
|
fontSize: 12
|
|
1711
1712
|
}
|
|
1712
1713
|
});
|
|
1714
|
+
function formatCurrency(raw, separator) {
|
|
1715
|
+
const digits = raw.replace(/\D/g, "");
|
|
1716
|
+
if (!digits) return "";
|
|
1717
|
+
return digits.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
|
|
1718
|
+
}
|
|
1719
|
+
function CurrencyInput({
|
|
1720
|
+
value,
|
|
1721
|
+
onChangeText,
|
|
1722
|
+
onChangeValue,
|
|
1723
|
+
prefix = "$",
|
|
1724
|
+
thousandsSeparator = ".",
|
|
1725
|
+
label,
|
|
1726
|
+
error,
|
|
1727
|
+
hint,
|
|
1728
|
+
placeholder,
|
|
1729
|
+
editable,
|
|
1730
|
+
containerStyle
|
|
1731
|
+
}) {
|
|
1732
|
+
const handleChange = (text) => {
|
|
1733
|
+
const withoutPrefix = prefix && text.startsWith(prefix) ? text.slice(prefix.length) : text;
|
|
1734
|
+
const formatted = formatCurrency(withoutPrefix, thousandsSeparator);
|
|
1735
|
+
const display = formatted ? `${prefix}${formatted}` : "";
|
|
1736
|
+
onChangeText?.(display);
|
|
1737
|
+
const separatorRegex = new RegExp(`\\${thousandsSeparator}`, "g");
|
|
1738
|
+
const raw = parseFloat(formatted.replace(separatorRegex, "") || "0");
|
|
1739
|
+
onChangeValue?.(isNaN(raw) ? 0 : raw);
|
|
1740
|
+
};
|
|
1741
|
+
return /* @__PURE__ */ React23__default.default.createElement(
|
|
1742
|
+
Input,
|
|
1743
|
+
{
|
|
1744
|
+
value,
|
|
1745
|
+
onChangeText: handleChange,
|
|
1746
|
+
keyboardType: "numeric",
|
|
1747
|
+
label,
|
|
1748
|
+
error,
|
|
1749
|
+
hint,
|
|
1750
|
+
placeholder: placeholder ?? `${prefix}0`,
|
|
1751
|
+
editable,
|
|
1752
|
+
containerStyle
|
|
1753
|
+
}
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1713
1756
|
|
|
1714
1757
|
Object.defineProperty(exports, "BottomSheetModalProvider", {
|
|
1715
1758
|
enumerable: true,
|
|
@@ -1727,6 +1770,7 @@ exports.CardFooter = CardFooter;
|
|
|
1727
1770
|
exports.CardHeader = CardHeader;
|
|
1728
1771
|
exports.CardTitle = CardTitle;
|
|
1729
1772
|
exports.Checkbox = Checkbox;
|
|
1773
|
+
exports.CurrencyInput = CurrencyInput;
|
|
1730
1774
|
exports.EmptyState = EmptyState;
|
|
1731
1775
|
exports.Input = Input;
|
|
1732
1776
|
exports.Progress = Progress;
|
package/dist/index.mjs
CHANGED
|
@@ -2,9 +2,10 @@ import React23, { createContext, useMemo, useContext, useRef, useState, useEffec
|
|
|
2
2
|
import { StyleSheet, useColorScheme, Animated, TouchableOpacity, ActivityIndicator, Text, View, TextInput, Image, PanResponder } from 'react-native';
|
|
3
3
|
import * as Haptics11 from 'expo-haptics';
|
|
4
4
|
import { LinearGradient } from 'expo-linear-gradient';
|
|
5
|
-
import ReanimatedAnimated, { useSharedValue, useAnimatedStyle, withTiming, Easing,
|
|
5
|
+
import ReanimatedAnimated, { useSharedValue, useAnimatedStyle, withTiming, Easing, withSpring } from 'react-native-reanimated';
|
|
6
6
|
import { BottomSheetModal, BottomSheetView, BottomSheetBackdrop } from '@gorhom/bottom-sheet';
|
|
7
7
|
export { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
|
|
8
|
+
import { scheduleOnRN } from 'react-native-worklets';
|
|
8
9
|
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
9
10
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
10
11
|
|
|
@@ -1592,7 +1593,7 @@ function ToastNotification({ item, onDismiss }) {
|
|
|
1592
1593
|
const timer = setTimeout(() => {
|
|
1593
1594
|
translateY.value = withTiming(-80, { duration: 200 });
|
|
1594
1595
|
opacity.value = withTiming(0, { duration: 200 }, (done) => {
|
|
1595
|
-
if (done)
|
|
1596
|
+
if (done) scheduleOnRN(onDismiss);
|
|
1596
1597
|
});
|
|
1597
1598
|
}, item.duration ?? 3e3);
|
|
1598
1599
|
return () => clearTimeout(timer);
|
|
@@ -1604,7 +1605,7 @@ function ToastNotification({ item, onDismiss }) {
|
|
|
1604
1605
|
if (shouldDismiss) {
|
|
1605
1606
|
const direction = translateX.value > 0 ? 1 : -1;
|
|
1606
1607
|
translateX.value = withTiming(direction * 500, { duration: 200 }, (done) => {
|
|
1607
|
-
if (done)
|
|
1608
|
+
if (done) scheduleOnRN(onDismiss);
|
|
1608
1609
|
});
|
|
1609
1610
|
opacity.value = withTiming(0, { duration: 150 });
|
|
1610
1611
|
} else {
|
|
@@ -1685,5 +1686,47 @@ var styles21 = StyleSheet.create({
|
|
|
1685
1686
|
fontSize: 12
|
|
1686
1687
|
}
|
|
1687
1688
|
});
|
|
1689
|
+
function formatCurrency(raw, separator) {
|
|
1690
|
+
const digits = raw.replace(/\D/g, "");
|
|
1691
|
+
if (!digits) return "";
|
|
1692
|
+
return digits.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
|
|
1693
|
+
}
|
|
1694
|
+
function CurrencyInput({
|
|
1695
|
+
value,
|
|
1696
|
+
onChangeText,
|
|
1697
|
+
onChangeValue,
|
|
1698
|
+
prefix = "$",
|
|
1699
|
+
thousandsSeparator = ".",
|
|
1700
|
+
label,
|
|
1701
|
+
error,
|
|
1702
|
+
hint,
|
|
1703
|
+
placeholder,
|
|
1704
|
+
editable,
|
|
1705
|
+
containerStyle
|
|
1706
|
+
}) {
|
|
1707
|
+
const handleChange = (text) => {
|
|
1708
|
+
const withoutPrefix = prefix && text.startsWith(prefix) ? text.slice(prefix.length) : text;
|
|
1709
|
+
const formatted = formatCurrency(withoutPrefix, thousandsSeparator);
|
|
1710
|
+
const display = formatted ? `${prefix}${formatted}` : "";
|
|
1711
|
+
onChangeText?.(display);
|
|
1712
|
+
const separatorRegex = new RegExp(`\\${thousandsSeparator}`, "g");
|
|
1713
|
+
const raw = parseFloat(formatted.replace(separatorRegex, "") || "0");
|
|
1714
|
+
onChangeValue?.(isNaN(raw) ? 0 : raw);
|
|
1715
|
+
};
|
|
1716
|
+
return /* @__PURE__ */ React23.createElement(
|
|
1717
|
+
Input,
|
|
1718
|
+
{
|
|
1719
|
+
value,
|
|
1720
|
+
onChangeText: handleChange,
|
|
1721
|
+
keyboardType: "numeric",
|
|
1722
|
+
label,
|
|
1723
|
+
error,
|
|
1724
|
+
hint,
|
|
1725
|
+
placeholder: placeholder ?? `${prefix}0`,
|
|
1726
|
+
editable,
|
|
1727
|
+
containerStyle
|
|
1728
|
+
}
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1688
1731
|
|
|
1689
|
-
export { Accordion, Alert, Avatar, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, EmptyState, Input, Progress, RadioGroup, Select, Separator, Sheet, Skeleton, Slider, Spinner, Switch, Tabs, TabsContent, Text2 as Text, Textarea, ThemeProvider, ToastProvider, Toggle, defaultDark, defaultLight, useTheme, useToast };
|
|
1732
|
+
export { Accordion, Alert, Avatar, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CurrencyInput, EmptyState, Input, Progress, RadioGroup, Select, Separator, Sheet, Skeleton, Slider, Spinner, Switch, Tabs, TabsContent, Text2 as Text, Textarea, ThemeProvider, ToastProvider, Toggle, defaultDark, defaultLight, useTheme, useToast };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retray-dev/ui-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Personal UI Kit for React Native / Expo",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@gorhom/bottom-sheet": ">=5.0.0",
|
|
45
45
|
"react-native-reanimated": ">=4.0.0",
|
|
46
46
|
"react-native-gesture-handler": ">=2.0.0",
|
|
47
|
-
"react-native-worklets": ">=0.
|
|
47
|
+
"react-native-worklets": ">=0.8.0",
|
|
48
48
|
"react-native-safe-area-context": ">=4.0.0"
|
|
49
49
|
},
|
|
50
50
|
"pnpm": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"fast-xml-parser": "^5.5.7",
|
|
53
53
|
"react": "19.1.0",
|
|
54
54
|
"react-native": "0.81.5",
|
|
55
|
-
"react-native-worklets": "0.
|
|
55
|
+
"react-native-worklets": "0.8.1"
|
|
56
56
|
},
|
|
57
57
|
"onlyBuiltDependencies": [
|
|
58
58
|
"esbuild"
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"expo-linear-gradient": "~14.1.5",
|
|
66
66
|
"react": "18.2.0",
|
|
67
67
|
"react-native": "0.74.0",
|
|
68
|
-
"react-native-gesture-handler": "~2.
|
|
69
|
-
"react-native-reanimated": "~4.
|
|
70
|
-
"react-native-worklets": "~0.
|
|
68
|
+
"react-native-gesture-handler": "~2.30.0",
|
|
69
|
+
"react-native-reanimated": "~4.3.0",
|
|
70
|
+
"react-native-worklets": "~0.8.1",
|
|
71
71
|
"react-native-safe-area-context": "~5.6.2",
|
|
72
72
|
"eslint": "^9.0.0",
|
|
73
73
|
"@eslint/js": "^9.0.0",
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { ViewStyle } from 'react-native'
|
|
3
|
+
import { Input } from '../Input'
|
|
4
|
+
|
|
5
|
+
export interface CurrencyInputProps {
|
|
6
|
+
value?: string
|
|
7
|
+
onChangeText?: (formatted: string) => void
|
|
8
|
+
/** Called with the parsed numeric value (no separators, no prefix). */
|
|
9
|
+
onChangeValue?: (raw: number) => void
|
|
10
|
+
/** Symbol prepended to the formatted value. Defaults to `'$'`. */
|
|
11
|
+
prefix?: string
|
|
12
|
+
/** Character used to separate groups of three digits. Defaults to `'.'`. */
|
|
13
|
+
thousandsSeparator?: '.' | ','
|
|
14
|
+
label?: string
|
|
15
|
+
/** Red helper text; also changes input border to destructive color. */
|
|
16
|
+
error?: string
|
|
17
|
+
hint?: string
|
|
18
|
+
placeholder?: string
|
|
19
|
+
editable?: boolean
|
|
20
|
+
containerStyle?: ViewStyle
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function formatCurrency(raw: string, separator: '.' | ','): string {
|
|
24
|
+
const digits = raw.replace(/\D/g, '')
|
|
25
|
+
if (!digits) return ''
|
|
26
|
+
return digits.replace(/\B(?=(\d{3})+(?!\d))/g, separator)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function CurrencyInput({
|
|
30
|
+
value,
|
|
31
|
+
onChangeText,
|
|
32
|
+
onChangeValue,
|
|
33
|
+
prefix = '$',
|
|
34
|
+
thousandsSeparator = '.',
|
|
35
|
+
label,
|
|
36
|
+
error,
|
|
37
|
+
hint,
|
|
38
|
+
placeholder,
|
|
39
|
+
editable,
|
|
40
|
+
containerStyle,
|
|
41
|
+
}: CurrencyInputProps) {
|
|
42
|
+
const handleChange = (text: string) => {
|
|
43
|
+
const withoutPrefix = prefix && text.startsWith(prefix) ? text.slice(prefix.length) : text
|
|
44
|
+
const formatted = formatCurrency(withoutPrefix, thousandsSeparator)
|
|
45
|
+
const display = formatted ? `${prefix}${formatted}` : ''
|
|
46
|
+
onChangeText?.(display)
|
|
47
|
+
const separatorRegex = new RegExp(`\\${thousandsSeparator}`, 'g')
|
|
48
|
+
const raw = parseFloat(formatted.replace(separatorRegex, '') || '0')
|
|
49
|
+
onChangeValue?.(isNaN(raw) ? 0 : raw)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Input
|
|
54
|
+
value={value}
|
|
55
|
+
onChangeText={handleChange}
|
|
56
|
+
keyboardType="numeric"
|
|
57
|
+
label={label}
|
|
58
|
+
error={error}
|
|
59
|
+
hint={hint}
|
|
60
|
+
placeholder={placeholder ?? `${prefix}0`}
|
|
61
|
+
editable={editable}
|
|
62
|
+
containerStyle={containerStyle}
|
|
63
|
+
/>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
@@ -5,9 +5,9 @@ import Animated, {
|
|
|
5
5
|
useAnimatedStyle,
|
|
6
6
|
withSpring,
|
|
7
7
|
withTiming,
|
|
8
|
-
runOnJS,
|
|
9
8
|
Easing,
|
|
10
9
|
} from 'react-native-reanimated'
|
|
10
|
+
import { scheduleOnRN } from 'react-native-worklets'
|
|
11
11
|
import { Gesture, GestureDetector } from 'react-native-gesture-handler'
|
|
12
12
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
13
13
|
import * as Haptics from 'expo-haptics'
|
|
@@ -54,7 +54,7 @@ function ToastNotification({ item, onDismiss }: { item: ToastItem; onDismiss: ()
|
|
|
54
54
|
const timer = setTimeout(() => {
|
|
55
55
|
translateY.value = withTiming(-80, { duration: 200 })
|
|
56
56
|
opacity.value = withTiming(0, { duration: 200 }, (done) => {
|
|
57
|
-
if (done)
|
|
57
|
+
if (done) scheduleOnRN(onDismiss)
|
|
58
58
|
})
|
|
59
59
|
}, item.duration ?? 3000)
|
|
60
60
|
|
|
@@ -72,7 +72,7 @@ function ToastNotification({ item, onDismiss }: { item: ToastItem; onDismiss: ()
|
|
|
72
72
|
if (shouldDismiss) {
|
|
73
73
|
const direction = translateX.value > 0 ? 1 : -1
|
|
74
74
|
translateX.value = withTiming(direction * 500, { duration: 200 }, (done) => {
|
|
75
|
-
if (done)
|
|
75
|
+
if (done) scheduleOnRN(onDismiss)
|
|
76
76
|
})
|
|
77
77
|
opacity.value = withTiming(0, { duration: 150 })
|
|
78
78
|
} else {
|
package/src/index.ts
CHANGED