panelui-native 0.21.0 → 0.22.5
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/README.md +113 -23
- package/lib/module/components/input/index.js +15 -5
- package/lib/module/components/input/index.js.map +1 -1
- package/lib/module/components/number-input/index.js +350 -0
- package/lib/module/components/number-input/index.js.map +1 -0
- package/lib/module/components/switch/index.js +7 -2
- package/lib/module/components/switch/index.js.map +1 -1
- package/lib/module/icons/index.js +10 -1
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/components/input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/number-input/index.d.ts +169 -0
- package/lib/typescript/src/components/number-input/index.d.ts.map +1 -0
- package/lib/typescript/src/components/switch/index.d.ts +7 -0
- package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
- package/lib/typescript/src/icons/index.d.ts +8 -1
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/input/index.tsx +15 -5
- package/src/components/number-input/index.tsx +373 -0
- package/src/components/switch/index.tsx +16 -2
- package/src/icons/index.tsx +9 -2
- package/src/index.ts +4 -0
- package/theme.css +22 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# PanelUI — React Native UI components for Expo, styled with Tailwind CSS
|
|
2
2
|
|
|
3
3
|
**PanelUI** (`panelui-native`) is an accessible, high-performance React Native component
|
|
4
|
-
library for Expo apps.
|
|
4
|
+
library for Expo apps. 53 typed components — buttons, bottom sheets, dialogs, selects,
|
|
5
5
|
toasts, forms — styled with Tailwind CSS v4 and animated on the UI thread with Reanimated.
|
|
6
6
|
Zero native code, so it runs in Expo Go.
|
|
7
7
|
|
|
@@ -24,15 +24,35 @@ Zero native code, so it runs in Expo Go.
|
|
|
24
24
|
state and labels.
|
|
25
25
|
- 📦 **TypeScript, tree-shakeable, zero native modules** — works in Expo Go, no prebuild needed.
|
|
26
26
|
|
|
27
|
-
##
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
Needs an Expo SDK 57+ app and Node 20+. No Xcode, no Android Studio, no `prebuild` — PanelUI has
|
|
30
|
+
no native modules, so it runs in Expo Go. No app yet? `npx create-expo-app@latest my-app`.
|
|
31
|
+
|
|
32
|
+
Five steps. The full walkthrough, with a fix for every error people hit, is at
|
|
33
|
+
**[panelui.dev/docs/installation](https://panelui.dev/docs/installation)**.
|
|
34
|
+
|
|
35
|
+
### 1. Install the packages
|
|
28
36
|
|
|
29
37
|
```sh
|
|
30
|
-
npx expo install panelui-native uniwind tailwindcss react-native-
|
|
38
|
+
npx expo install panelui-native uniwind tailwindcss @react-native-masked-view/masked-view expo-linear-gradient react-native-gesture-handler react-native-reanimated react-native-safe-area-context react-native-svg react-native-worklets
|
|
31
39
|
```
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
Install all of them, including the ones you think you don't need. Metro resolves every import in
|
|
42
|
+
the library when it builds your bundle, so leaving one out fails the **first** bundle with
|
|
43
|
+
`Unable to resolve module …` — for a component you may never use. `npx expo install` (rather than
|
|
44
|
+
`npm install`) picks the versions that match your SDK.
|
|
45
|
+
|
|
46
|
+
Optional, each behind a guarded import: `expo-haptics` (the `haptics` prop), `expo-blur` (blurred
|
|
47
|
+
overlay backdrops), `expo-clipboard` (`useCopyToClipboard`), `react-native-keyboard-controller`
|
|
48
|
+
(keyboard avoidance on Android), `expo-file-system` + `react-native-view-shot` (Signature export),
|
|
49
|
+
`@expo/ui` (native platform controls).
|
|
34
50
|
|
|
35
|
-
|
|
51
|
+
### 2. Add `metro.config.js`
|
|
52
|
+
|
|
53
|
+
**In the root of your project, next to `package.json`** — not in `app/`, not in `src/`. A Metro
|
|
54
|
+
config anywhere else is silently ignored and none of your classes will do anything. A fresh Expo
|
|
55
|
+
app has no such file; `npx expo customize metro.config.js` writes one.
|
|
36
56
|
|
|
37
57
|
```js
|
|
38
58
|
// metro.config.js
|
|
@@ -42,49 +62,100 @@ const { withUniwindConfig } = require('uniwind/metro');
|
|
|
42
62
|
const config = getDefaultConfig(__dirname);
|
|
43
63
|
|
|
44
64
|
module.exports = withUniwindConfig(config, {
|
|
65
|
+
// In an app made by create-expo-app this is './src/global.css' — see step 3.
|
|
45
66
|
cssEntryFile: './global.css',
|
|
46
67
|
dtsFile: './uniwind-types.d.ts',
|
|
47
|
-
// Only needed
|
|
68
|
+
// Only needed to switch to the Moon or Grass themes at runtime.
|
|
48
69
|
extraThemes: ['moon', 'moon-dark', 'grass', 'grass-dark'],
|
|
49
70
|
});
|
|
50
71
|
```
|
|
51
72
|
|
|
52
|
-
|
|
73
|
+
`cssEntryFile` and `dtsFile` are relative to this file.
|
|
74
|
+
|
|
75
|
+
### 3. Add the imports to `global.css`
|
|
76
|
+
|
|
77
|
+
**Look for this file before you create one** — an app made by `create-expo-app` already has
|
|
78
|
+
`src/global.css`. Add these lines at the top of it and leave the rest below. A second CSS file at
|
|
79
|
+
the project root gives you two entries, only one of which is compiled, and nothing gets styled.
|
|
53
80
|
|
|
54
81
|
```css
|
|
82
|
+
/* src/global.css */
|
|
55
83
|
@import 'tailwindcss';
|
|
56
84
|
@import 'uniwind';
|
|
57
85
|
@import 'panelui-native/theme.css';
|
|
58
86
|
|
|
59
|
-
@source '
|
|
87
|
+
@source '../node_modules/panelui-native/src';
|
|
60
88
|
```
|
|
61
89
|
|
|
62
|
-
**
|
|
90
|
+
`@source` is relative to **the CSS file**, and has to land on `node_modules/panelui-native/src` —
|
|
91
|
+
hence the `../` above, from `src/`. From the project root it is `'./node_modules/…'`, from
|
|
92
|
+
`src/styles/` it is `'../../node_modules/…'`. Get it wrong and your own classes work while
|
|
93
|
+
PanelUI's components come out unstyled. Whichever file you used is what `cssEntryFile` must name.
|
|
94
|
+
|
|
95
|
+
### 4. Import the CSS and add the provider
|
|
96
|
+
|
|
97
|
+
At the top of your app's entry file. The default template uses Expo Router with routes under
|
|
98
|
+
`src/`, so there is usually no `App.tsx` — the entry is `src/app/_layout.tsx`, and it already
|
|
99
|
+
returns a navigation `ThemeProvider` to wrap rather than replace:
|
|
63
100
|
|
|
64
101
|
```tsx
|
|
65
|
-
//
|
|
66
|
-
import '
|
|
67
|
-
|
|
102
|
+
// src/app/_layout.tsx
|
|
103
|
+
import '../global.css';
|
|
104
|
+
|
|
105
|
+
import { DarkTheme, DefaultTheme, ThemeProvider } from 'expo-router';
|
|
106
|
+
import { useColorScheme } from 'react-native';
|
|
107
|
+
import { PanelUIProvider } from 'panelui-native';
|
|
108
|
+
|
|
109
|
+
import AppTabs from '@/components/app-tabs';
|
|
110
|
+
|
|
111
|
+
export default function RootLayout() {
|
|
112
|
+
const colorScheme = useColorScheme();
|
|
68
113
|
|
|
69
|
-
export default function App() {
|
|
70
114
|
return (
|
|
71
115
|
<PanelUIProvider>
|
|
72
|
-
<
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
<Card.Description>Deploy your new project in one click.</Card.Description>
|
|
76
|
-
</Card.Header>
|
|
77
|
-
<Card.Footer>
|
|
78
|
-
<Button>Deploy</Button>
|
|
79
|
-
</Card.Footer>
|
|
80
|
-
</Card>
|
|
116
|
+
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
|
117
|
+
<AppTabs />
|
|
118
|
+
</ThemeProvider>
|
|
81
119
|
</PanelUIProvider>
|
|
82
120
|
);
|
|
83
121
|
}
|
|
84
122
|
```
|
|
85
123
|
|
|
124
|
+
That navigation theme paints over PanelUI's background once you switch themes — see
|
|
125
|
+
[Using Expo Router?](#using-expo-router-read-this) below for the version fed from live tokens.
|
|
126
|
+
|
|
86
127
|
`PanelUIProvider` sets up the gesture root, the themed page background, the portal host used by
|
|
87
|
-
overlays, and the toast viewport.
|
|
128
|
+
overlays, and the toast viewport. One at the root is enough — don't nest a second.
|
|
129
|
+
|
|
130
|
+
You do **not** need a `babel.config.js`; Expo's default preset already wires the worklets plugin
|
|
131
|
+
Reanimated needs.
|
|
132
|
+
|
|
133
|
+
### 5. Restart
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
npx expo start --clear
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Metro reads `metro.config.js`, `global.css` and `extraThemes` once, at startup. After changing any
|
|
140
|
+
of them, stop the dev server and start it again — `--clear` on a running one is not enough.
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { Button, Card } from 'panelui-native';
|
|
144
|
+
|
|
145
|
+
<Card>
|
|
146
|
+
<Card.Header>
|
|
147
|
+
<Card.Title>It works</Card.Title>
|
|
148
|
+
<Card.Description>PanelUI is installed and themed.</Card.Description>
|
|
149
|
+
</Card.Header>
|
|
150
|
+
<Card.Footer>
|
|
151
|
+
<Button>Deploy</Button>
|
|
152
|
+
</Card.Footer>
|
|
153
|
+
</Card>;
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
A themed background, a card with a border and radius, and a button that dips when pressed means
|
|
157
|
+
you are done. Unstyled text on a white screen means the styles are not reaching the bundle — see
|
|
158
|
+
[Troubleshooting](https://panelui.dev/docs/installation#troubleshooting).
|
|
88
159
|
|
|
89
160
|
## Components
|
|
90
161
|
|
|
@@ -246,6 +317,25 @@ the named themes, which the OS `Appearance` API knows nothing about. For the sam
|
|
|
246
317
|
|
|
247
318
|
## FAQ
|
|
248
319
|
|
|
320
|
+
### The first bundle fails with `Unable to resolve module …`
|
|
321
|
+
|
|
322
|
+
A required package is missing. Run the [install command](#1-install-the-packages) again — all of
|
|
323
|
+
it, not just the package named in the error. Metro resolves every import in the library, so this
|
|
324
|
+
happens even for components you never use.
|
|
325
|
+
|
|
326
|
+
### `Cannot use @variant with unknown variant: moon` when building
|
|
327
|
+
|
|
328
|
+
Fixed in 0.22.5 — upgrade with `npx expo install panelui-native`. Before that release the Moon and
|
|
329
|
+
Grass token blocks leaned on variants that only existed in the artifact Uniwind generates from your
|
|
330
|
+
Metro config, so the dev server worked while `npx expo export` and EAS builds failed.
|
|
331
|
+
|
|
332
|
+
### None of my classes do anything
|
|
333
|
+
|
|
334
|
+
In order of likelihood: `metro.config.js` is not in the project root; it does not wrap the config
|
|
335
|
+
in `withUniwindConfig`; `cssEntryFile` does not point at your CSS file; `import './global.css'` is
|
|
336
|
+
missing from the entry file; or the dev server was running when you changed one of those. Full
|
|
337
|
+
list at [Troubleshooting](https://panelui.dev/docs/installation#troubleshooting).
|
|
338
|
+
|
|
249
339
|
### How is PanelUI different from NativeWind?
|
|
250
340
|
|
|
251
341
|
NativeWind is a styling engine; PanelUI is a component library. PanelUI is built on **Uniwind**,
|
|
@@ -52,21 +52,31 @@ const inputVariants = tv({
|
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
size: {
|
|
55
|
+
/*
|
|
56
|
+
* The font size is given as a length rather than through a `text-*`
|
|
57
|
+
* step, because those set a size and a line height together — 16px text
|
|
58
|
+
* in a 24px line box. The extra leading lands above the glyphs, so in a
|
|
59
|
+
* field of fixed height the text and the placeholder end up sitting
|
|
60
|
+
* below the middle of the box, a few pixels off whatever is beside them.
|
|
61
|
+
* A length sets the size alone and leaves the line box the font's own.
|
|
62
|
+
*/
|
|
55
63
|
sm: {
|
|
56
|
-
field: 'h-10 px-3 text-
|
|
64
|
+
field: 'h-10 px-3 text-[14px]'
|
|
57
65
|
},
|
|
58
66
|
md: {
|
|
59
|
-
field: 'h-12 px-3.5 text-
|
|
67
|
+
field: 'h-12 px-3.5 text-[16px]'
|
|
60
68
|
},
|
|
61
69
|
lg: {
|
|
62
|
-
field: 'h-14 px-4 text-
|
|
70
|
+
field: 'h-14 px-4 text-[16px]'
|
|
63
71
|
}
|
|
64
72
|
},
|
|
65
73
|
multiline: {
|
|
66
74
|
// A multiline field grows, so a fixed height would crop it. Text starts
|
|
67
|
-
// at the top rather than floating in the middle of an empty box
|
|
75
|
+
// at the top rather than floating in the middle of an empty box — and
|
|
76
|
+
// once there are several lines the leading is wanted back, since here it
|
|
77
|
+
// separates the lines instead of pushing one off centre.
|
|
68
78
|
true: {
|
|
69
|
-
field: 'h-auto min-h-24 py-3'
|
|
79
|
+
field: 'h-auto min-h-24 py-3 leading-normal'
|
|
70
80
|
}
|
|
71
81
|
},
|
|
72
82
|
disabled: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","useCallback","useEffect","useState","TextInput","View","Animated","interpolateColor","useAnimatedStyle","useSharedValue","withTiming","tv","useCSSVariable","KeyboardAvoider","Text","Label","jsx","_jsx","Fragment","_Fragment","jsxs","_jsxs","FOCUS_DURATION","inputVariants","slots","container","field","description","error","variants","variant","outline","filled","size","sm","md","lg","multiline","true","disabled","defaultVariants","AnimatedTextInput","createAnimatedComponent","Input","className","containerClassName","label","errorMessage","isRequired","avoidKeyboard","keyboardMode","keyboardOffset","keyboardBottomInset","onFocus","onBlur","style","props","ref","focused","setFocused","invalid","placeholderColor","restColor","focusColor","errorColor","focus","value","duration","resting","active","idle","borderStyle","borderColor","handleFocus","event","handleBlur","body","children","isInvalid","isDisabled","editable","accessibilityLabel","accessibilityState","placeholderTextColor","undefined","containerClasses","mode","offset","bottomInset","displayName"],"sourceRoot":"../../../../src","sources":["components/input/index.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,UAAU,EAAEC,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,OAAO;AACpE,SAASC,SAAS,EAAEC,IAAI,QAA6B,cAAc;AACnE,OAAOC,QAAQ,IACbC,gBAAgB,EAChBC,gBAAgB,EAChBC,cAAc,EACdC,UAAU,QACL,yBAAyB;AAChC,SAASC,EAAE,QAA2B,mBAAmB;AACzD,SAASC,cAAc,QAAQ,SAAS;AAExC,SAASC,eAAe,QAAQ,sCAAmC;AACnE,SAASC,IAAI,QAAQ,0BAAuB;AAC5C,SAASC,KAAK,QAAQ,mBAAU;;AAEhC;AAAA,SAAAC,GAAA,IAAAC,IAAA,EAAAC,QAAA,IAAAC,SAAA,EAAAC,IAAA,IAAAC,KAAA;AACA,MAAMC,cAAc,GAAG,GAAG;AAE1B,MAAMC,aAAa,GAAGZ,EAAE,CAAC;EACvBa,KAAK,EAAE;IACLC,SAAS,EAAE,gBAAgB;IAC3BC,KAAK,EAAE,0CAA0C;IACjDC,WAAW,EAAE,+BAA+B;IAC5CC,KAAK,EAAE;EACT,CAAC;EACDC,QAAQ,EAAE;IACRC,OAAO,EAAE;MACP;MACA;MACAC,OAAO,EAAE;QAAEL,KAAK,EAAE;MAAgB,CAAC;MACnCM,MAAM,EAAE;QAAEN,KAAK,EAAE;MAAW;IAC9B,CAAC;IACDO,IAAI,EAAE;
|
|
1
|
+
{"version":3,"names":["forwardRef","useCallback","useEffect","useState","TextInput","View","Animated","interpolateColor","useAnimatedStyle","useSharedValue","withTiming","tv","useCSSVariable","KeyboardAvoider","Text","Label","jsx","_jsx","Fragment","_Fragment","jsxs","_jsxs","FOCUS_DURATION","inputVariants","slots","container","field","description","error","variants","variant","outline","filled","size","sm","md","lg","multiline","true","disabled","defaultVariants","AnimatedTextInput","createAnimatedComponent","Input","className","containerClassName","label","errorMessage","isRequired","avoidKeyboard","keyboardMode","keyboardOffset","keyboardBottomInset","onFocus","onBlur","style","props","ref","focused","setFocused","invalid","placeholderColor","restColor","focusColor","errorColor","focus","value","duration","resting","active","idle","borderStyle","borderColor","handleFocus","event","handleBlur","body","children","isInvalid","isDisabled","editable","accessibilityLabel","accessibilityState","placeholderTextColor","undefined","containerClasses","mode","offset","bottomInset","displayName"],"sourceRoot":"../../../../src","sources":["components/input/index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* NumberInput — a value stepped by two buttons or typed by hand.
|
|
5
|
+
*
|
|
6
|
+
* A number is not a slider: past a handful of steps, dragging to 47 is worse
|
|
7
|
+
* than tapping + a few times or just typing it. So the control keeps both
|
|
8
|
+
* doors open — a − and a + at the ends for nudging, and an editable field in
|
|
9
|
+
* the middle for jumping straight to a value — and reconciles them through a
|
|
10
|
+
* single clamped, step-snapped number.
|
|
11
|
+
*
|
|
12
|
+
* The two ends are not just tap targets. Hold one down and it repeats, after a
|
|
13
|
+
* short delay and then faster, the way a native stepper does — so walking from
|
|
14
|
+
* 0 to 200 is a press, not two hundred of them. The repeat reads the live
|
|
15
|
+
* value from a ref rather than a closure, so it never strides off a stale
|
|
16
|
+
* number.
|
|
17
|
+
*
|
|
18
|
+
* Typing is allowed to be briefly invalid — an empty field, a lone `-`, a
|
|
19
|
+
* trailing `.` — because clamping every keystroke fights the person mid-number.
|
|
20
|
+
* The field commits (parses, snaps, clamps) on blur, and falls back to the
|
|
21
|
+
* last good value when what is left cannot be read as a number.
|
|
22
|
+
*
|
|
23
|
+
* ```tsx
|
|
24
|
+
* <NumberInput defaultValue={1} min={0} max={10} />
|
|
25
|
+
* <NumberInput label="Quantity" value={qty} onValueChange={setQty} />
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Runs controlled (`value` + `onValueChange`) or uncontrolled (`defaultValue`).
|
|
29
|
+
*/
|
|
30
|
+
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
|
31
|
+
import { TextInput, View } from 'react-native';
|
|
32
|
+
import { tv } from 'tailwind-variants';
|
|
33
|
+
import { useCSSVariable } from 'uniwind';
|
|
34
|
+
import { MinusIcon, PlusIcon } from "../../icons/index.js";
|
|
35
|
+
import { AnimatedPressable } from "../../primitives/animated-pressable.js";
|
|
36
|
+
import { Text } from "../../primitives/text.js";
|
|
37
|
+
import { selectionTick } from "../../utils/haptics.js";
|
|
38
|
+
import { Label } from "../label/index.js";
|
|
39
|
+
|
|
40
|
+
/** Wait before a held button starts repeating — a tap must not trip it. */
|
|
41
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
42
|
+
const REPEAT_DELAY = 400;
|
|
43
|
+
/** Interval between repeats once holding — fast enough to cover ground. */
|
|
44
|
+
const REPEAT_INTERVAL = 80;
|
|
45
|
+
const numberInputVariants = tv({
|
|
46
|
+
slots: {
|
|
47
|
+
container: 'w-full gap-1.5',
|
|
48
|
+
control: 'w-full flex-row items-center overflow-hidden rounded-lg border',
|
|
49
|
+
button: 'h-full items-center justify-center',
|
|
50
|
+
// The field takes the full height so the digits centre against the same
|
|
51
|
+
// box the two glyphs do, rather than against their own line's height.
|
|
52
|
+
field: 'h-full flex-1 text-center font-medium text-foreground',
|
|
53
|
+
description: 'text-sm text-muted-foreground',
|
|
54
|
+
error: 'text-sm text-destructive'
|
|
55
|
+
},
|
|
56
|
+
variants: {
|
|
57
|
+
variant: {
|
|
58
|
+
// Matches Input: outline draws its own edge on the page; filled sits
|
|
59
|
+
// inside a card, where a second border reads as a seam.
|
|
60
|
+
outline: {
|
|
61
|
+
control: 'border-input bg-background'
|
|
62
|
+
},
|
|
63
|
+
filled: {
|
|
64
|
+
control: 'border-transparent bg-muted'
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
size: {
|
|
68
|
+
sm: {
|
|
69
|
+
control: 'h-10',
|
|
70
|
+
button: 'w-10'
|
|
71
|
+
},
|
|
72
|
+
md: {
|
|
73
|
+
control: 'h-12',
|
|
74
|
+
button: 'w-12'
|
|
75
|
+
},
|
|
76
|
+
lg: {
|
|
77
|
+
control: 'h-14',
|
|
78
|
+
button: 'w-14'
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
invalid: {
|
|
82
|
+
true: {
|
|
83
|
+
control: 'border-destructive'
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
disabled: {
|
|
87
|
+
true: {
|
|
88
|
+
container: 'opacity-[0.64]'
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
defaultVariants: {
|
|
93
|
+
variant: 'outline',
|
|
94
|
+
size: 'md'
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
/** Icon size per control size — the glyph scales with its target. */
|
|
99
|
+
const ICON_SIZE = {
|
|
100
|
+
sm: 16,
|
|
101
|
+
md: 18,
|
|
102
|
+
lg: 20
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The field's job is not to be a box of its own: it has to put the number on
|
|
107
|
+
* exactly the axis the − and the + sit on, and in the exact middle between
|
|
108
|
+
* them. An editable field does not do that unhelped — it arrives with padding
|
|
109
|
+
* and, on Android, with font padding above and below the line, all of which
|
|
110
|
+
* shift the digits off centre. Zeroing both and centring the line in the full
|
|
111
|
+
* height leaves geometry as the only thing placing the number.
|
|
112
|
+
*
|
|
113
|
+
* Tabular figures on top of that, so the digits keep the same advance widths
|
|
114
|
+
* while the value counts — a 1 that is narrower than a 7 makes a centred
|
|
115
|
+
* number appear to shuffle sideways as it steps.
|
|
116
|
+
*/
|
|
117
|
+
const FIELD_BASE = {
|
|
118
|
+
paddingVertical: 0,
|
|
119
|
+
paddingHorizontal: 0,
|
|
120
|
+
includeFontPadding: false,
|
|
121
|
+
textAlignVertical: 'center',
|
|
122
|
+
fontVariant: ['tabular-nums']
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Font size is set here rather than by a text class because those bring a line
|
|
127
|
+
* height with them: a 16px number in a 24px line box. The extra leading is
|
|
128
|
+
* added above the digits, so centring that box leaves the number sitting a
|
|
129
|
+
* couple of pixels below the − and the + it is meant to line up with — close
|
|
130
|
+
* enough to look like a mistake rather than a difference. Sized this way the
|
|
131
|
+
* line box is the font's own, and centring it centres the digits.
|
|
132
|
+
*/
|
|
133
|
+
const FIELD_STYLE = {
|
|
134
|
+
sm: {
|
|
135
|
+
...FIELD_BASE,
|
|
136
|
+
fontSize: 14
|
|
137
|
+
},
|
|
138
|
+
md: {
|
|
139
|
+
...FIELD_BASE,
|
|
140
|
+
fontSize: 16
|
|
141
|
+
},
|
|
142
|
+
lg: {
|
|
143
|
+
...FIELD_BASE,
|
|
144
|
+
fontSize: 16
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/** Decimals implied by `step`, so display and math round to the same place. */
|
|
148
|
+
function precisionOf(step) {
|
|
149
|
+
if (!isFinite(step)) return 0;
|
|
150
|
+
const s = String(step);
|
|
151
|
+
const dot = s.indexOf('.');
|
|
152
|
+
return dot === -1 ? 0 : s.length - dot - 1;
|
|
153
|
+
}
|
|
154
|
+
function clamp(value, min, max) {
|
|
155
|
+
return Math.min(Math.max(value, min), max);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Snaps to the nearest step from a finite base (or 0 when `min` is open), then
|
|
160
|
+
* rounds to the step's precision so 0.1 + 0.2 does not drift to 0.30000004.
|
|
161
|
+
*/
|
|
162
|
+
function normalize(value, min, max, step) {
|
|
163
|
+
const base = isFinite(min) ? min : 0;
|
|
164
|
+
const snapped = isFinite(step) && step > 0 ? Math.round((value - base) / step) * step + base : value;
|
|
165
|
+
const p = precisionOf(step);
|
|
166
|
+
const factor = 10 ** p;
|
|
167
|
+
return clamp(Math.round(snapped * factor) / factor, min, max);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* A number stepped by buttons or typed by hand, reconciled to one clamped,
|
|
172
|
+
* step-snapped value. See the file header for why it keeps both affordances.
|
|
173
|
+
*/
|
|
174
|
+
export const NumberInput = /*#__PURE__*/forwardRef(({
|
|
175
|
+
className,
|
|
176
|
+
containerClassName,
|
|
177
|
+
value: controlledValue,
|
|
178
|
+
defaultValue,
|
|
179
|
+
min = -Infinity,
|
|
180
|
+
max = Infinity,
|
|
181
|
+
step = 1,
|
|
182
|
+
onValueChange,
|
|
183
|
+
formatValue,
|
|
184
|
+
editable = true,
|
|
185
|
+
variant = 'outline',
|
|
186
|
+
size = 'md',
|
|
187
|
+
disabled,
|
|
188
|
+
label,
|
|
189
|
+
description,
|
|
190
|
+
errorMessage,
|
|
191
|
+
isRequired,
|
|
192
|
+
haptics,
|
|
193
|
+
placeholder,
|
|
194
|
+
onFocus,
|
|
195
|
+
onBlur,
|
|
196
|
+
returnKeyType
|
|
197
|
+
}, ref) => {
|
|
198
|
+
const isControlled = controlledValue !== undefined;
|
|
199
|
+
const initial = normalize(controlledValue ?? defaultValue ?? (isFinite(min) ? min : 0), min, max, step);
|
|
200
|
+
const [uncontrolled, setUncontrolled] = useState(initial);
|
|
201
|
+
const value = isControlled ? normalize(controlledValue, min, max, step) : uncontrolled;
|
|
202
|
+
|
|
203
|
+
// The repeat timers read the live value through a ref, so a held button
|
|
204
|
+
// keeps counting from where it is rather than from the value it closed over.
|
|
205
|
+
const valueRef = useRef(value);
|
|
206
|
+
valueRef.current = value;
|
|
207
|
+
const invalid = !!errorMessage;
|
|
208
|
+
const foreground = useCSSVariable('--color-foreground');
|
|
209
|
+
const iconColor = typeof foreground === 'string' ? foreground : undefined;
|
|
210
|
+
const slots = numberInputVariants({
|
|
211
|
+
variant,
|
|
212
|
+
size,
|
|
213
|
+
invalid,
|
|
214
|
+
disabled: !!disabled
|
|
215
|
+
});
|
|
216
|
+
const format = useCallback(v => formatValue ? formatValue(v) : String(v), [formatValue]);
|
|
217
|
+
|
|
218
|
+
// `text` is what the field shows while focused; it is allowed to be
|
|
219
|
+
// half-typed. Away from focus it always mirrors the committed value.
|
|
220
|
+
const [text, setText] = useState(() => format(value));
|
|
221
|
+
const [focused, setFocused] = useState(false);
|
|
222
|
+
useEffect(() => {
|
|
223
|
+
if (!focused) setText(format(value));
|
|
224
|
+
}, [value, focused, format]);
|
|
225
|
+
const commit = useCallback((next, tick) => {
|
|
226
|
+
const clamped = normalize(next, min, max, step);
|
|
227
|
+
if (tick && haptics) selectionTick();
|
|
228
|
+
if (!isControlled) setUncontrolled(clamped);
|
|
229
|
+
if (clamped !== value) onValueChange?.(clamped);
|
|
230
|
+
}, [min, max, step, haptics, isControlled, value, onValueChange]);
|
|
231
|
+
|
|
232
|
+
// --- hold-to-repeat -----------------------------------------------------
|
|
233
|
+
const delayRef = useRef(null);
|
|
234
|
+
const intervalRef = useRef(null);
|
|
235
|
+
const stopRepeat = useCallback(() => {
|
|
236
|
+
if (delayRef.current) clearTimeout(delayRef.current);
|
|
237
|
+
if (intervalRef.current) clearInterval(intervalRef.current);
|
|
238
|
+
delayRef.current = null;
|
|
239
|
+
intervalRef.current = null;
|
|
240
|
+
}, []);
|
|
241
|
+
useEffect(() => stopRepeat, [stopRepeat]);
|
|
242
|
+
const startRepeat = useCallback(direction => {
|
|
243
|
+
const nudge = () => commit(valueRef.current + direction * step, true);
|
|
244
|
+
nudge();
|
|
245
|
+
delayRef.current = setTimeout(() => {
|
|
246
|
+
intervalRef.current = setInterval(nudge, REPEAT_INTERVAL);
|
|
247
|
+
}, REPEAT_DELAY);
|
|
248
|
+
}, [commit, step]);
|
|
249
|
+
const atMin = value <= min;
|
|
250
|
+
const atMax = value >= max;
|
|
251
|
+
const handleEndEditing = useCallback(() => {
|
|
252
|
+
const parsed = parseFloat(text);
|
|
253
|
+
// An empty or unreadable field falls back to the last good value rather
|
|
254
|
+
// than snapping to a surprising 0.
|
|
255
|
+
commit(Number.isNaN(parsed) ? value : parsed, false);
|
|
256
|
+
setFocused(false);
|
|
257
|
+
setText(format(Number.isNaN(parsed) ? value : normalize(parsed, min, max, step)));
|
|
258
|
+
}, [text, value, commit, format, min, max, step]);
|
|
259
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
260
|
+
className: slots.container({
|
|
261
|
+
className: containerClassName
|
|
262
|
+
}),
|
|
263
|
+
children: [label ? /*#__PURE__*/_jsx(Label, {
|
|
264
|
+
isRequired: isRequired,
|
|
265
|
+
isInvalid: invalid,
|
|
266
|
+
isDisabled: !!disabled,
|
|
267
|
+
children: label
|
|
268
|
+
}) : null, /*#__PURE__*/_jsxs(View, {
|
|
269
|
+
className: slots.control({
|
|
270
|
+
className
|
|
271
|
+
}),
|
|
272
|
+
children: [/*#__PURE__*/_jsx(AnimatedPressable, {
|
|
273
|
+
className: slots.button(),
|
|
274
|
+
disabled: disabled || atMin,
|
|
275
|
+
pressOpacity: 0.6,
|
|
276
|
+
onPressIn: () => {
|
|
277
|
+
if (!disabled && !atMin) startRepeat(-1);
|
|
278
|
+
},
|
|
279
|
+
onPressOut: stopRepeat,
|
|
280
|
+
accessibilityRole: "button",
|
|
281
|
+
accessibilityLabel: `Decrease ${label ?? 'value'}`,
|
|
282
|
+
accessibilityState: {
|
|
283
|
+
disabled: disabled || atMin
|
|
284
|
+
},
|
|
285
|
+
style: atMin ? {
|
|
286
|
+
opacity: 0.4
|
|
287
|
+
} : undefined,
|
|
288
|
+
children: /*#__PURE__*/_jsx(MinusIcon, {
|
|
289
|
+
size: ICON_SIZE[size],
|
|
290
|
+
color: iconColor,
|
|
291
|
+
strokeWidth: 2
|
|
292
|
+
})
|
|
293
|
+
}), /*#__PURE__*/_jsx(TextInput, {
|
|
294
|
+
ref: ref,
|
|
295
|
+
editable: editable && !disabled,
|
|
296
|
+
value: text,
|
|
297
|
+
keyboardType: precisionOf(step) > 0 ? 'decimal-pad' : 'number-pad',
|
|
298
|
+
placeholder: placeholder,
|
|
299
|
+
returnKeyType: returnKeyType,
|
|
300
|
+
selectTextOnFocus: true,
|
|
301
|
+
onChangeText: setText,
|
|
302
|
+
onFocus: e => {
|
|
303
|
+
setFocused(true);
|
|
304
|
+
onFocus?.(e);
|
|
305
|
+
},
|
|
306
|
+
onBlur: e => {
|
|
307
|
+
handleEndEditing();
|
|
308
|
+
onBlur?.(e);
|
|
309
|
+
},
|
|
310
|
+
onSubmitEditing: handleEndEditing,
|
|
311
|
+
accessibilityLabel: label,
|
|
312
|
+
accessibilityState: {
|
|
313
|
+
disabled: !!disabled
|
|
314
|
+
},
|
|
315
|
+
"aria-required": isRequired,
|
|
316
|
+
"aria-invalid": invalid,
|
|
317
|
+
className: slots.field(),
|
|
318
|
+
style: FIELD_STYLE[size]
|
|
319
|
+
}), /*#__PURE__*/_jsx(AnimatedPressable, {
|
|
320
|
+
className: slots.button(),
|
|
321
|
+
disabled: disabled || atMax,
|
|
322
|
+
pressOpacity: 0.6,
|
|
323
|
+
onPressIn: () => {
|
|
324
|
+
if (!disabled && !atMax) startRepeat(1);
|
|
325
|
+
},
|
|
326
|
+
onPressOut: stopRepeat,
|
|
327
|
+
accessibilityRole: "button",
|
|
328
|
+
accessibilityLabel: `Increase ${label ?? 'value'}`,
|
|
329
|
+
accessibilityState: {
|
|
330
|
+
disabled: disabled || atMax
|
|
331
|
+
},
|
|
332
|
+
style: atMax ? {
|
|
333
|
+
opacity: 0.4
|
|
334
|
+
} : undefined,
|
|
335
|
+
children: /*#__PURE__*/_jsx(PlusIcon, {
|
|
336
|
+
size: ICON_SIZE[size],
|
|
337
|
+
color: iconColor
|
|
338
|
+
})
|
|
339
|
+
})]
|
|
340
|
+
}), errorMessage ? /*#__PURE__*/_jsx(Text, {
|
|
341
|
+
className: slots.error(),
|
|
342
|
+
children: errorMessage
|
|
343
|
+
}) : description ? /*#__PURE__*/_jsx(Text, {
|
|
344
|
+
className: slots.description(),
|
|
345
|
+
children: description
|
|
346
|
+
}) : null]
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
NumberInput.displayName = 'NumberInput';
|
|
350
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["forwardRef","useCallback","useEffect","useRef","useState","TextInput","View","tv","useCSSVariable","MinusIcon","PlusIcon","AnimatedPressable","Text","selectionTick","Label","jsx","_jsx","jsxs","_jsxs","REPEAT_DELAY","REPEAT_INTERVAL","numberInputVariants","slots","container","control","button","field","description","error","variants","variant","outline","filled","size","sm","md","lg","invalid","true","disabled","defaultVariants","ICON_SIZE","FIELD_BASE","paddingVertical","paddingHorizontal","includeFontPadding","textAlignVertical","fontVariant","FIELD_STYLE","fontSize","precisionOf","step","isFinite","s","String","dot","indexOf","length","clamp","value","min","max","Math","normalize","base","snapped","round","p","factor","NumberInput","className","containerClassName","controlledValue","defaultValue","Infinity","onValueChange","formatValue","editable","label","errorMessage","isRequired","haptics","placeholder","onFocus","onBlur","returnKeyType","ref","isControlled","undefined","initial","uncontrolled","setUncontrolled","valueRef","current","foreground","iconColor","format","v","text","setText","focused","setFocused","commit","next","tick","clamped","delayRef","intervalRef","stopRepeat","clearTimeout","clearInterval","startRepeat","direction","nudge","setTimeout","setInterval","atMin","atMax","handleEndEditing","parsed","parseFloat","Number","isNaN","children","isInvalid","isDisabled","pressOpacity","onPressIn","onPressOut","accessibilityRole","accessibilityLabel","accessibilityState","style","opacity","color","strokeWidth","keyboardType","selectTextOnFocus","onChangeText","e","onSubmitEditing","displayName"],"sourceRoot":"../../../../src","sources":["components/number-input/index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -5,6 +5,7 @@ import { Pressable, View } from 'react-native';
|
|
|
5
5
|
import Animated, { interpolate, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
|
|
6
6
|
import { tv } from 'tailwind-variants';
|
|
7
7
|
import { getNativeUI } from "../../native/index.js";
|
|
8
|
+
import { selectionTick } from "../../utils/haptics.js";
|
|
8
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
10
|
const SPRING = {
|
|
10
11
|
damping: 18,
|
|
@@ -53,7 +54,8 @@ export const Switch = /*#__PURE__*/forwardRef(({
|
|
|
53
54
|
disabled,
|
|
54
55
|
size = 'md',
|
|
55
56
|
native,
|
|
56
|
-
label
|
|
57
|
+
label,
|
|
58
|
+
haptics
|
|
57
59
|
}, ref) => {
|
|
58
60
|
const progress = useSharedValue(value ? 1 : 0);
|
|
59
61
|
const nativeUI = native ? getNativeUI() : null;
|
|
@@ -102,7 +104,10 @@ export const Switch = /*#__PURE__*/forwardRef(({
|
|
|
102
104
|
disabled: !!disabled
|
|
103
105
|
},
|
|
104
106
|
disabled: disabled,
|
|
105
|
-
onPress: () =>
|
|
107
|
+
onPress: () => {
|
|
108
|
+
if (haptics) selectionTick();
|
|
109
|
+
onValueChange?.(!value);
|
|
110
|
+
},
|
|
106
111
|
hitSlop: 8,
|
|
107
112
|
children: /*#__PURE__*/_jsxs(View, {
|
|
108
113
|
className: slots.track({
|