panelui-native 0.9.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/bottom-sheet/index.js +38 -2
- 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/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/popover/index.js +22 -11
- package/lib/module/components/popover/index.js.map +1 -1
- 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 +34 -7
- 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/slider/index.js +355 -0
- package/lib/module/components/slider/index.js.map +1 -0
- 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/index.js +4 -1
- 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/utils/haptics.js +57 -0
- package/lib/module/utils/haptics.js.map +1 -0
- 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/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/popover/index.d.ts +7 -1
- package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
- 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 +7 -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/slider/index.d.ts +203 -0
- package/lib/typescript/src/components/slider/index.d.ts.map +1 -0
- 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/index.d.ts +6 -3
- 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/utils/haptics.d.ts +12 -0
- package/lib/typescript/src/utils/haptics.d.ts.map +1 -0
- package/package.json +5 -1
- package/src/components/bottom-sheet/index.tsx +38 -2
- package/src/components/button/index.tsx +26 -5
- package/src/components/frame/index.tsx +175 -29
- package/src/components/input/index.tsx +122 -16
- package/src/components/popover/index.tsx +49 -24
- package/src/components/progress/index.tsx +81 -1
- package/src/components/radio-group/index.tsx +55 -8
- package/src/components/section-rail/index.tsx +459 -0
- package/src/components/select/index.tsx +265 -52
- package/src/components/slider/index.tsx +371 -0
- 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/index.ts +37 -6
- package/src/native/index.ts +13 -1
- package/src/primitives/keyboard-avoider.tsx +9 -1
- package/src/utils/haptics.ts +63 -0
- package/theme.css +6 -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
|
@@ -11,6 +11,8 @@ import Animated, {
|
|
|
11
11
|
withTiming,
|
|
12
12
|
} from 'react-native-reanimated';
|
|
13
13
|
import { tv, type VariantProps } from 'tailwind-variants';
|
|
14
|
+
import { Text } from '../../primitives/text';
|
|
15
|
+
import { cn } from '../../utils/cn';
|
|
14
16
|
|
|
15
17
|
const SPRING = { damping: 20, stiffness: 180, mass: 0.6 } as const;
|
|
16
18
|
/** Fraction of the track covered by the sliding bar in indeterminate mode. */
|
|
@@ -18,6 +20,8 @@ const INDETERMINATE_WIDTH = 0.4;
|
|
|
18
20
|
|
|
19
21
|
const progressVariants = tv({
|
|
20
22
|
slots: {
|
|
23
|
+
root: 'w-full gap-2',
|
|
24
|
+
header: 'flex-row items-center justify-between',
|
|
21
25
|
track: 'w-full overflow-hidden rounded-full',
|
|
22
26
|
indicator: 'h-full rounded-full',
|
|
23
27
|
},
|
|
@@ -53,6 +57,30 @@ export interface ProgressProps
|
|
|
53
57
|
indeterminate?: boolean;
|
|
54
58
|
/** Extra classes for the moving indicator. */
|
|
55
59
|
indicatorClassName?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Caption drawn above the track, on the left. Supplying it (or
|
|
62
|
+
* `showValueLabel`) wraps the bar in a header row; the track alone renders
|
|
63
|
+
* otherwise.
|
|
64
|
+
*/
|
|
65
|
+
label?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Draw the percentage above the track, on the right. Hidden while
|
|
68
|
+
* `indeterminate` — there is nothing meaningful to show.
|
|
69
|
+
*/
|
|
70
|
+
showValueLabel?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Text for the value label. Overrides the formatted percentage — use it for
|
|
73
|
+
* a byte count, a step tally, anything that is not a bare percent.
|
|
74
|
+
*/
|
|
75
|
+
valueLabel?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Format the value label as a fraction of the whole with `Intl.NumberFormat`
|
|
78
|
+
* — e.g. `{ style: 'currency', currency: 'USD' }`. Falls back to a rounded
|
|
79
|
+
* percent when omitted.
|
|
80
|
+
*/
|
|
81
|
+
formatOptions?: Intl.NumberFormatOptions;
|
|
82
|
+
/** Extra classes for the label + value-label row. */
|
|
83
|
+
headerClassName?: string;
|
|
56
84
|
}
|
|
57
85
|
|
|
58
86
|
function clamp(value: number) {
|
|
@@ -62,18 +90,43 @@ function clamp(value: number) {
|
|
|
62
90
|
return value;
|
|
63
91
|
}
|
|
64
92
|
|
|
93
|
+
/** The right-hand caption: an explicit override, an Intl fraction, or a percent. */
|
|
94
|
+
function formatValue(
|
|
95
|
+
value: number,
|
|
96
|
+
valueLabel?: string,
|
|
97
|
+
formatOptions?: Intl.NumberFormatOptions
|
|
98
|
+
) {
|
|
99
|
+
if (valueLabel != null) return valueLabel;
|
|
100
|
+
if (formatOptions) {
|
|
101
|
+
try {
|
|
102
|
+
return new Intl.NumberFormat(undefined, formatOptions).format(value / 100);
|
|
103
|
+
} catch {
|
|
104
|
+
// Some engines ship a partial Intl; fall through to the plain percent.
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return `${Math.round(value)}%`;
|
|
108
|
+
}
|
|
109
|
+
|
|
65
110
|
/**
|
|
66
111
|
* Determinate or indeterminate progress bar. The fill width (determinate) and
|
|
67
112
|
* the sliding bar (indeterminate) are both driven on the UI thread, so updates
|
|
68
113
|
* never re-render past the value change itself.
|
|
114
|
+
*
|
|
115
|
+
* Pass `label` or `showValueLabel` to caption the bar with a header row; the
|
|
116
|
+
* bare track renders when neither is set, so existing call sites are untouched.
|
|
69
117
|
*/
|
|
70
118
|
export const Progress = forwardRef<View, ProgressProps>(
|
|
71
119
|
(
|
|
72
120
|
{
|
|
73
121
|
className,
|
|
74
122
|
indicatorClassName,
|
|
123
|
+
headerClassName,
|
|
75
124
|
value = 0,
|
|
76
125
|
indeterminate = false,
|
|
126
|
+
label,
|
|
127
|
+
showValueLabel = false,
|
|
128
|
+
valueLabel,
|
|
129
|
+
formatOptions,
|
|
77
130
|
color,
|
|
78
131
|
size,
|
|
79
132
|
...props
|
|
@@ -124,10 +177,15 @@ export const Progress = forwardRef<View, ProgressProps>(
|
|
|
124
177
|
};
|
|
125
178
|
});
|
|
126
179
|
|
|
127
|
-
|
|
180
|
+
// The value label is meaningless while looping, so it is dropped there.
|
|
181
|
+
const showValue = showValueLabel && !indeterminate;
|
|
182
|
+
const hasHeader = label != null || showValue;
|
|
183
|
+
|
|
184
|
+
const track = (
|
|
128
185
|
<View
|
|
129
186
|
ref={ref}
|
|
130
187
|
accessibilityRole="progressbar"
|
|
188
|
+
accessibilityLabel={label}
|
|
131
189
|
accessibilityValue={
|
|
132
190
|
indeterminate ? undefined : { min: 0, max: 100, now: clamp(value) }
|
|
133
191
|
}
|
|
@@ -141,6 +199,28 @@ export const Progress = forwardRef<View, ProgressProps>(
|
|
|
141
199
|
/>
|
|
142
200
|
</View>
|
|
143
201
|
);
|
|
202
|
+
|
|
203
|
+
if (!hasHeader) return track;
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<View className={slots.root()}>
|
|
207
|
+
<View className={slots.header({ className: headerClassName })}>
|
|
208
|
+
{label != null ? (
|
|
209
|
+
<Text size="sm" weight="medium" numberOfLines={1}>
|
|
210
|
+
{label}
|
|
211
|
+
</Text>
|
|
212
|
+
) : (
|
|
213
|
+
<View />
|
|
214
|
+
)}
|
|
215
|
+
{showValue ? (
|
|
216
|
+
<Text size="sm" muted className={cn(label == null && 'ml-auto')}>
|
|
217
|
+
{formatValue(clamp(value), valueLabel, formatOptions)}
|
|
218
|
+
</Text>
|
|
219
|
+
) : null}
|
|
220
|
+
</View>
|
|
221
|
+
{track}
|
|
222
|
+
</View>
|
|
223
|
+
);
|
|
144
224
|
}
|
|
145
225
|
);
|
|
146
226
|
|
|
@@ -38,10 +38,14 @@ import { cn } from '../../utils/cn';
|
|
|
38
38
|
import { Text } from '../../primitives/text';
|
|
39
39
|
|
|
40
40
|
type RadioVariant = 'dot' | 'card';
|
|
41
|
+
type RadioOrientation = 'vertical' | 'horizontal';
|
|
41
42
|
|
|
42
43
|
const itemVariants = tv({
|
|
43
44
|
slots: {
|
|
44
|
-
|
|
45
|
+
// `self-start` matters: the group is a column, so without it Yoga stretches
|
|
46
|
+
// the row to the full width and the dead space to the right of the label
|
|
47
|
+
// becomes part of the target. Tapping nothing would select the option.
|
|
48
|
+
row: 'flex-row items-center gap-2.5 self-start',
|
|
45
49
|
indicator:
|
|
46
50
|
'h-5 w-5 items-center justify-center rounded-full border border-input bg-background',
|
|
47
51
|
label: 'text-sm text-foreground',
|
|
@@ -52,8 +56,9 @@ const itemVariants = tv({
|
|
|
52
56
|
dot: {},
|
|
53
57
|
card: {
|
|
54
58
|
// The whole surface is the target; the disc moves to the trailing edge
|
|
55
|
-
// as a confirmation of the row, not the affordance for it.
|
|
56
|
-
|
|
59
|
+
// as a confirmation of the row, not the affordance for it. Here the
|
|
60
|
+
// full width is deliberate, so it takes `self-stretch` back.
|
|
61
|
+
row: 'w-full self-stretch items-start justify-between gap-3 rounded-xl border border-border bg-card p-4',
|
|
57
62
|
label: 'text-base font-medium',
|
|
58
63
|
},
|
|
59
64
|
},
|
|
@@ -63,9 +68,19 @@ const itemVariants = tv({
|
|
|
63
68
|
disabled: {
|
|
64
69
|
true: { row: 'opacity-50' },
|
|
65
70
|
},
|
|
71
|
+
/**
|
|
72
|
+
* A card in a row shares the width rather than filling it, so two or three
|
|
73
|
+
* options sit side by side instead of each taking the whole line.
|
|
74
|
+
*/
|
|
75
|
+
horizontal: {
|
|
76
|
+
true: {},
|
|
77
|
+
},
|
|
66
78
|
},
|
|
67
79
|
compoundVariants: [
|
|
68
80
|
{ variant: 'card', selected: true, class: { row: 'border-primary bg-accent' } },
|
|
81
|
+
// Share the row instead of filling it, keeping the disc on the trailing
|
|
82
|
+
// edge of each narrower card.
|
|
83
|
+
{ variant: 'card', horizontal: true, class: { row: 'w-auto flex-1' } },
|
|
69
84
|
],
|
|
70
85
|
defaultVariants: {
|
|
71
86
|
variant: 'dot',
|
|
@@ -77,6 +92,7 @@ interface RadioGroupContextValue {
|
|
|
77
92
|
onValueChange: (value: string) => void;
|
|
78
93
|
disabled?: boolean;
|
|
79
94
|
variant: RadioVariant;
|
|
95
|
+
orientation: RadioOrientation;
|
|
80
96
|
}
|
|
81
97
|
|
|
82
98
|
const RadioGroupContext = createContext<RadioGroupContextValue | null>(null);
|
|
@@ -92,14 +108,32 @@ export interface RadioGroupProps extends ViewProps {
|
|
|
92
108
|
* settings choice where each option carries a description.
|
|
93
109
|
*/
|
|
94
110
|
variant?: RadioVariant;
|
|
111
|
+
/**
|
|
112
|
+
* `horizontal` lays the options out along a row that wraps — for two or
|
|
113
|
+
* three short choices, where a stacked list wastes the width and reads as
|
|
114
|
+
* longer than it is.
|
|
115
|
+
*/
|
|
116
|
+
orientation?: RadioOrientation;
|
|
95
117
|
children: ReactNode;
|
|
96
118
|
}
|
|
97
119
|
|
|
98
120
|
const RadioGroupRoot = forwardRef<View, RadioGroupProps>(
|
|
99
|
-
(
|
|
121
|
+
(
|
|
122
|
+
{
|
|
123
|
+
className,
|
|
124
|
+
value,
|
|
125
|
+
onValueChange,
|
|
126
|
+
disabled,
|
|
127
|
+
variant = 'dot',
|
|
128
|
+
orientation = 'vertical',
|
|
129
|
+
children,
|
|
130
|
+
...props
|
|
131
|
+
},
|
|
132
|
+
ref
|
|
133
|
+
) => {
|
|
100
134
|
const context = useMemo(
|
|
101
|
-
() => ({ value, onValueChange, disabled, variant }),
|
|
102
|
-
[value, onValueChange, disabled, variant]
|
|
135
|
+
() => ({ value, onValueChange, disabled, variant, orientation }),
|
|
136
|
+
[value, onValueChange, disabled, variant, orientation]
|
|
103
137
|
);
|
|
104
138
|
|
|
105
139
|
return (
|
|
@@ -107,7 +141,15 @@ const RadioGroupRoot = forwardRef<View, RadioGroupProps>(
|
|
|
107
141
|
<View
|
|
108
142
|
ref={ref}
|
|
109
143
|
accessibilityRole="radiogroup"
|
|
110
|
-
className={cn(
|
|
144
|
+
className={cn(
|
|
145
|
+
orientation === 'horizontal'
|
|
146
|
+
? // Wrapping, not scrolling: a choice that runs off the edge is
|
|
147
|
+
// a choice nobody knows is there. The cross-axis gap is
|
|
148
|
+
// smaller because wrapped rows already read as separate.
|
|
149
|
+
'flex-row flex-wrap items-stretch gap-x-5 gap-y-3'
|
|
150
|
+
: 'gap-3',
|
|
151
|
+
className
|
|
152
|
+
)}
|
|
111
153
|
{...props}
|
|
112
154
|
>
|
|
113
155
|
{children}
|
|
@@ -156,7 +198,12 @@ const RadioGroupItem = forwardRef<View, RadioGroupItemProps>(
|
|
|
156
198
|
transform: [{ scale: progress.value }],
|
|
157
199
|
}));
|
|
158
200
|
|
|
159
|
-
const slots = itemVariants({
|
|
201
|
+
const slots = itemVariants({
|
|
202
|
+
variant,
|
|
203
|
+
selected,
|
|
204
|
+
disabled: !!disabled,
|
|
205
|
+
horizontal: context.orientation === 'horizontal',
|
|
206
|
+
});
|
|
160
207
|
|
|
161
208
|
const indicator = hideIndicator ? null : (
|
|
162
209
|
<View className={slots.indicator()}>
|