panelui-native 0.7.0 → 0.12.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/README.md +5 -3
- package/lib/module/components/attachment/index.js +219 -0
- package/lib/module/components/attachment/index.js.map +1 -0
- package/lib/module/components/bottom-sheet/index.js +53 -3
- package/lib/module/components/bottom-sheet/index.js.map +1 -1
- package/lib/module/components/button/index.js +19 -1
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/dialog/index.js +6 -2
- package/lib/module/components/dialog/index.js.map +1 -1
- package/lib/module/components/empty-state/index.js +94 -17
- package/lib/module/components/empty-state/index.js.map +1 -1
- package/lib/module/components/frame/index.js +128 -15
- package/lib/module/components/frame/index.js.map +1 -1
- package/lib/module/components/input/index.js +107 -15
- package/lib/module/components/input/index.js.map +1 -1
- package/lib/module/components/line-chart/index.js +1024 -0
- package/lib/module/components/line-chart/index.js.map +1 -0
- package/lib/module/components/marker/index.js +190 -0
- package/lib/module/components/marker/index.js.map +1 -0
- package/lib/module/components/message/index.js +24 -8
- package/lib/module/components/message/index.js.map +1 -1
- package/lib/module/components/message-scroller/index.js +451 -0
- package/lib/module/components/message-scroller/index.js.map +1 -0
- package/lib/module/components/popover/index.js +511 -0
- package/lib/module/components/popover/index.js.map +1 -0
- package/lib/module/components/progress/index.js +52 -2
- package/lib/module/components/progress/index.js.map +1 -1
- package/lib/module/components/radio-group/index.js +132 -17
- package/lib/module/components/radio-group/index.js.map +1 -1
- package/lib/module/components/section-rail/index.js +355 -0
- package/lib/module/components/section-rail/index.js.map +1 -0
- package/lib/module/components/select/index.js +245 -56
- package/lib/module/components/select/index.js.map +1 -1
- package/lib/module/components/separator/index.js +91 -0
- package/lib/module/components/separator/index.js.map +1 -0
- package/lib/module/components/slider/index.js +355 -0
- package/lib/module/components/slider/index.js.map +1 -0
- package/lib/module/components/surface/index.js +21 -2
- package/lib/module/components/surface/index.js.map +1 -1
- package/lib/module/components/switch/index.js +10 -1
- package/lib/module/components/switch/index.js.map +1 -1
- package/lib/module/components/tabs/index.js +186 -16
- package/lib/module/components/tabs/index.js.map +1 -1
- package/lib/module/components/toggle-button/index.js +297 -0
- package/lib/module/components/toggle-button/index.js.map +1 -0
- package/lib/module/hooks/index.js +1 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/use-keyboard-avoidance.js +57 -8
- package/lib/module/hooks/use-keyboard-avoidance.js.map +1 -1
- package/lib/module/hooks/use-scroll-sections.js +89 -0
- package/lib/module/hooks/use-scroll-sections.js.map +1 -0
- package/lib/module/icons/index.js +69 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +11 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/index.js.map +1 -1
- package/lib/module/primitives/keyboard-avoider.js +2 -0
- package/lib/module/primitives/keyboard-avoider.js.map +1 -1
- package/lib/module/primitives/scrim.js +72 -0
- package/lib/module/primitives/scrim.js.map +1 -0
- package/lib/module/utils/haptics.js +57 -0
- package/lib/module/utils/haptics.js.map +1 -0
- package/lib/typescript/src/components/attachment/index.d.ts +93 -0
- package/lib/typescript/src/components/attachment/index.d.ts.map +1 -0
- package/lib/typescript/src/components/bottom-sheet/index.d.ts +6 -1
- package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/src/components/button/index.d.ts +9 -4
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/dialog/index.d.ts +7 -1
- package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
- package/lib/typescript/src/components/empty-state/index.d.ts +152 -2
- package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -1
- package/lib/typescript/src/components/frame/index.d.ts +47 -7
- package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
- package/lib/typescript/src/components/input/index.d.ts +113 -2
- package/lib/typescript/src/components/input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/line-chart/index.d.ts +247 -0
- package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/marker/index.d.ts +137 -0
- package/lib/typescript/src/components/marker/index.d.ts.map +1 -0
- package/lib/typescript/src/components/message/index.d.ts +8 -1
- package/lib/typescript/src/components/message/index.d.ts.map +1 -1
- package/lib/typescript/src/components/message-scroller/index.d.ts +150 -0
- package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -0
- package/lib/typescript/src/components/popover/index.d.ts +136 -0
- package/lib/typescript/src/components/popover/index.d.ts.map +1 -0
- package/lib/typescript/src/components/progress/index.d.ts +33 -0
- package/lib/typescript/src/components/progress/index.d.ts.map +1 -1
- package/lib/typescript/src/components/radio-group/index.d.ts +39 -0
- package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
- package/lib/typescript/src/components/section-rail/index.d.ts +115 -0
- package/lib/typescript/src/components/section-rail/index.d.ts.map +1 -0
- package/lib/typescript/src/components/select/index.d.ts +46 -7
- package/lib/typescript/src/components/select/index.d.ts.map +1 -1
- package/lib/typescript/src/components/separator/index.d.ts +47 -0
- package/lib/typescript/src/components/separator/index.d.ts.map +1 -0
- package/lib/typescript/src/components/slider/index.d.ts +203 -0
- package/lib/typescript/src/components/slider/index.d.ts.map +1 -0
- package/lib/typescript/src/components/surface/index.d.ts +51 -2
- package/lib/typescript/src/components/surface/index.d.ts.map +1 -1
- package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tabs/index.d.ts +46 -4
- package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
- package/lib/typescript/src/components/toggle-button/index.d.ts +244 -0
- package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/index.d.ts +1 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts +10 -1
- package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-scroll-sections.d.ts +31 -0
- package/lib/typescript/src/hooks/use-scroll-sections.d.ts.map +1 -0
- package/lib/typescript/src/icons/index.d.ts +6 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +13 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/index.d.ts +16 -1
- package/lib/typescript/src/native/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/keyboard-avoider.d.ts +8 -1
- package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
- package/lib/typescript/src/primitives/scrim.d.ts +21 -0
- package/lib/typescript/src/primitives/scrim.d.ts.map +1 -0
- package/lib/typescript/src/utils/haptics.d.ts +12 -0
- package/lib/typescript/src/utils/haptics.d.ts.map +1 -0
- package/package.json +9 -1
- package/src/components/attachment/index.tsx +279 -0
- package/src/components/bottom-sheet/index.tsx +58 -2
- package/src/components/button/index.tsx +26 -5
- package/src/components/dialog/index.tsx +10 -1
- package/src/components/empty-state/index.tsx +60 -14
- package/src/components/frame/index.tsx +175 -29
- package/src/components/input/index.tsx +122 -16
- package/src/components/line-chart/index.tsx +1224 -0
- package/src/components/marker/index.tsx +194 -0
- package/src/components/message/index.tsx +29 -5
- package/src/components/message-scroller/index.tsx +574 -0
- package/src/components/popover/index.tsx +624 -0
- package/src/components/progress/index.tsx +81 -1
- package/src/components/radio-group/index.tsx +156 -18
- package/src/components/section-rail/index.tsx +459 -0
- package/src/components/select/index.tsx +265 -52
- package/src/components/separator/index.tsx +97 -0
- package/src/components/slider/index.tsx +371 -0
- package/src/components/surface/index.tsx +29 -3
- package/src/components/switch/index.tsx +8 -1
- package/src/components/tabs/index.tsx +208 -28
- package/src/components/toggle-button/index.tsx +374 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/use-keyboard-avoidance.ts +73 -7
- package/src/hooks/use-scroll-sections.ts +127 -0
- package/src/icons/index.tsx +48 -0
- package/src/index.ts +97 -6
- package/src/native/index.ts +13 -1
- package/src/primitives/keyboard-avoider.tsx +9 -1
- package/src/primitives/scrim.tsx +95 -0
- package/src/utils/haptics.ts +63 -0
- package/theme.css +64 -2
- package/lib/module/components/inline-select/index.js +0 -284
- package/lib/module/components/inline-select/index.js.map +0 -1
- package/lib/typescript/src/components/inline-select/index.d.ts +0 -48
- package/lib/typescript/src/components/inline-select/index.d.ts.map +0 -1
- package/src/components/inline-select/index.tsx +0 -332
|
@@ -1,44 +1,93 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Input — a text field with a label, a description and an error line.
|
|
3
|
+
*
|
|
4
|
+
* The field's focus state is animated rather than switched. A border that
|
|
5
|
+
* snaps between two colours reads as a redraw; one that crosses over in
|
|
6
|
+
* 150ms reads as the field responding to you. The interpolation runs on the
|
|
7
|
+
* UI thread, so it costs nothing and keeps up with a fast tab through a form.
|
|
8
|
+
*
|
|
9
|
+
* Two backgrounds, because a field's job changes with what surrounds it:
|
|
10
|
+
* `outline` sits on the page and draws its own edge; `filled` sits inside a
|
|
11
|
+
* card or a sheet, where a second border next to the container's own reads as
|
|
12
|
+
* a seam.
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <Input label="Email" placeholder="you@example.com" />
|
|
16
|
+
* <Input variant="filled" size="lg" label="Name" isRequired />
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* For a field with an icon or a button attached, reach for `InputGroup` — it
|
|
20
|
+
* measures its decorators and pads this field around them.
|
|
21
|
+
*/
|
|
22
|
+
import { forwardRef, useCallback, useEffect, useState } from 'react';
|
|
2
23
|
import { TextInput, View, type TextInputProps } from 'react-native';
|
|
3
|
-
import {
|
|
24
|
+
import Animated, {
|
|
25
|
+
interpolateColor,
|
|
26
|
+
useAnimatedStyle,
|
|
27
|
+
useSharedValue,
|
|
28
|
+
withTiming,
|
|
29
|
+
} from 'react-native-reanimated';
|
|
30
|
+
import { tv, type VariantProps } from 'tailwind-variants';
|
|
4
31
|
import { useCSSVariable } from 'uniwind';
|
|
5
32
|
import { KeyboardAvoider } from '../../primitives/keyboard-avoider';
|
|
6
33
|
import { Text } from '../../primitives/text';
|
|
34
|
+
import { Label } from '../label';
|
|
35
|
+
|
|
36
|
+
/** Long enough to read as a transition, short enough not to lag a fast tab. */
|
|
37
|
+
const FOCUS_DURATION = 150;
|
|
7
38
|
|
|
8
39
|
const inputVariants = tv({
|
|
9
40
|
slots: {
|
|
10
41
|
container: 'w-full gap-1.5',
|
|
11
|
-
field:
|
|
12
|
-
'h-11 w-full rounded-lg border border-input bg-background px-3 text-base text-foreground',
|
|
13
|
-
label: 'text-sm font-medium text-foreground',
|
|
42
|
+
field: 'w-full rounded-lg border text-foreground',
|
|
14
43
|
description: 'text-sm text-muted-foreground',
|
|
15
44
|
error: 'text-sm text-destructive',
|
|
16
45
|
},
|
|
17
46
|
variants: {
|
|
18
|
-
|
|
19
|
-
|
|
47
|
+
variant: {
|
|
48
|
+
// The border colour is animated, so it is deliberately not set here —
|
|
49
|
+
// only the background and the resting border width belong to the class.
|
|
50
|
+
outline: { field: 'bg-background' },
|
|
51
|
+
filled: { field: 'bg-muted' },
|
|
52
|
+
},
|
|
53
|
+
size: {
|
|
54
|
+
sm: { field: 'h-10 px-3 text-sm' },
|
|
55
|
+
md: { field: 'h-12 px-3.5 text-base' },
|
|
56
|
+
lg: { field: 'h-14 px-4 text-base' },
|
|
20
57
|
},
|
|
21
|
-
|
|
22
|
-
|
|
58
|
+
multiline: {
|
|
59
|
+
// A multiline field grows, so a fixed height would crop it. Text starts
|
|
60
|
+
// at the top rather than floating in the middle of an empty box.
|
|
61
|
+
true: { field: 'h-auto min-h-24 py-3' },
|
|
23
62
|
},
|
|
24
63
|
disabled: {
|
|
25
64
|
true: { field: 'opacity-[0.64]' },
|
|
26
65
|
},
|
|
27
66
|
},
|
|
67
|
+
defaultVariants: {
|
|
68
|
+
variant: 'outline',
|
|
69
|
+
size: 'md',
|
|
70
|
+
},
|
|
28
71
|
});
|
|
29
72
|
|
|
30
|
-
|
|
73
|
+
type InputVariantProps = VariantProps<typeof inputVariants>;
|
|
74
|
+
|
|
75
|
+
export interface InputProps
|
|
76
|
+
extends TextInputProps,
|
|
77
|
+
Omit<InputVariantProps, 'disabled' | 'multiline'> {
|
|
31
78
|
className?: string;
|
|
32
79
|
containerClassName?: string;
|
|
33
80
|
label?: string;
|
|
34
81
|
description?: string;
|
|
35
82
|
/** Error message. When set, the field renders in its invalid state. */
|
|
36
83
|
errorMessage?: string;
|
|
84
|
+
/** Marks the field required — an asterisk on the label, and the a11y state. */
|
|
85
|
+
isRequired?: boolean;
|
|
37
86
|
disabled?: boolean;
|
|
38
87
|
/**
|
|
39
88
|
* Lift the field above the software keyboard when it would otherwise be
|
|
40
89
|
* covered. Moves by exactly the overlap, and not at all when the field is
|
|
41
|
-
* already clear.
|
|
90
|
+
* already clear — or when the keyboard belongs to a different field.
|
|
42
91
|
*
|
|
43
92
|
* Install `react-native-keyboard-controller` for this to behave on Android.
|
|
44
93
|
*
|
|
@@ -50,6 +99,8 @@ export interface InputProps extends TextInputProps {
|
|
|
50
99
|
keyboardOffset?: number;
|
|
51
100
|
}
|
|
52
101
|
|
|
102
|
+
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
|
|
103
|
+
|
|
53
104
|
export const Input = forwardRef<TextInput, InputProps>(
|
|
54
105
|
(
|
|
55
106
|
{
|
|
@@ -58,23 +109,61 @@ export const Input = forwardRef<TextInput, InputProps>(
|
|
|
58
109
|
label,
|
|
59
110
|
description,
|
|
60
111
|
errorMessage,
|
|
112
|
+
isRequired,
|
|
61
113
|
disabled,
|
|
114
|
+
variant,
|
|
115
|
+
size,
|
|
62
116
|
avoidKeyboard = false,
|
|
63
117
|
keyboardOffset = 16,
|
|
64
118
|
onFocus,
|
|
65
119
|
onBlur,
|
|
120
|
+
style,
|
|
66
121
|
...props
|
|
67
122
|
},
|
|
68
123
|
ref
|
|
69
124
|
) => {
|
|
70
125
|
const [focused, setFocused] = useState(false);
|
|
126
|
+
const invalid = !!errorMessage;
|
|
127
|
+
|
|
71
128
|
const placeholderColor = useCSSVariable('--color-muted-foreground');
|
|
129
|
+
const restColor = useCSSVariable('--color-input');
|
|
130
|
+
const focusColor = useCSSVariable('--color-ring');
|
|
131
|
+
const errorColor = useCSSVariable('--color-destructive');
|
|
132
|
+
|
|
72
133
|
const slots = inputVariants({
|
|
73
|
-
|
|
74
|
-
|
|
134
|
+
variant,
|
|
135
|
+
size,
|
|
136
|
+
multiline: !!props.multiline,
|
|
75
137
|
disabled: !!disabled,
|
|
76
138
|
});
|
|
77
139
|
|
|
140
|
+
/*
|
|
141
|
+
* Border colour is driven by one 0..1 value rather than by a class per
|
|
142
|
+
* state. Uniwind can only swap a class wholesale, which is the snap this
|
|
143
|
+
* is here to avoid, and a shared value crosses between the two colours on
|
|
144
|
+
* the UI thread without a re-render.
|
|
145
|
+
*/
|
|
146
|
+
const focus = useSharedValue(0);
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
focus.value = withTiming(focused ? 1 : 0, { duration: FOCUS_DURATION });
|
|
149
|
+
}, [focused, focus]);
|
|
150
|
+
|
|
151
|
+
const resting = typeof restColor === 'string' ? restColor : '#e5e5e5';
|
|
152
|
+
const active = invalid
|
|
153
|
+
? typeof errorColor === 'string'
|
|
154
|
+
? errorColor
|
|
155
|
+
: '#ef4444'
|
|
156
|
+
: typeof focusColor === 'string'
|
|
157
|
+
? focusColor
|
|
158
|
+
: '#a3a3a3';
|
|
159
|
+
// An invalid field is tinted even at rest — the error is a fact about the
|
|
160
|
+
// value, not about whether the field happens to be focused.
|
|
161
|
+
const idle = invalid ? active : resting;
|
|
162
|
+
|
|
163
|
+
const borderStyle = useAnimatedStyle(() => ({
|
|
164
|
+
borderColor: interpolateColor(focus.value, [0, 1], [idle, active]),
|
|
165
|
+
}));
|
|
166
|
+
|
|
78
167
|
const handleFocus = useCallback<NonNullable<TextInputProps['onFocus']>>(
|
|
79
168
|
(event) => {
|
|
80
169
|
setFocused(true);
|
|
@@ -93,15 +182,24 @@ export const Input = forwardRef<TextInput, InputProps>(
|
|
|
93
182
|
|
|
94
183
|
const body = (
|
|
95
184
|
<>
|
|
96
|
-
{label ?
|
|
97
|
-
|
|
185
|
+
{label ? (
|
|
186
|
+
<Label isRequired={isRequired} isInvalid={invalid} isDisabled={!!disabled}>
|
|
187
|
+
{label}
|
|
188
|
+
</Label>
|
|
189
|
+
) : null}
|
|
190
|
+
<AnimatedTextInput
|
|
98
191
|
ref={ref}
|
|
99
192
|
editable={!disabled}
|
|
100
193
|
onFocus={handleFocus}
|
|
101
194
|
onBlur={handleBlur}
|
|
102
195
|
accessibilityLabel={label}
|
|
103
196
|
accessibilityState={{ disabled: !!disabled }}
|
|
197
|
+
aria-required={isRequired}
|
|
198
|
+
aria-invalid={invalid}
|
|
104
199
|
className={slots.field({ className })}
|
|
200
|
+
// Caller styles come last so InputGroup can pad the field around its
|
|
201
|
+
// decorators, but never last enough to drop the animated border.
|
|
202
|
+
style={[borderStyle, style]}
|
|
105
203
|
placeholderTextColor={
|
|
106
204
|
typeof placeholderColor === 'string' ? placeholderColor : undefined
|
|
107
205
|
}
|
|
@@ -127,7 +225,15 @@ export const Input = forwardRef<TextInput, InputProps>(
|
|
|
127
225
|
*/
|
|
128
226
|
if (avoidKeyboard) {
|
|
129
227
|
return (
|
|
130
|
-
<KeyboardAvoider
|
|
228
|
+
<KeyboardAvoider
|
|
229
|
+
// Only while *this* field is the one being typed into. Without it
|
|
230
|
+
// every avoiding field on the screen lifts the moment any field
|
|
231
|
+
// anywhere is tapped, and since they all aim at the same gap above
|
|
232
|
+
// the keyboard, they arrive stacked on top of one another.
|
|
233
|
+
active={focused}
|
|
234
|
+
offset={keyboardOffset}
|
|
235
|
+
className={containerClasses}
|
|
236
|
+
>
|
|
131
237
|
{body}
|
|
132
238
|
</KeyboardAvoider>
|
|
133
239
|
);
|