panelui-native 0.75.1 → 0.76.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/lib/module/components/ai-input/ai-input-growth.js +62 -0
- package/lib/module/components/ai-input/ai-input-growth.js.map +1 -0
- package/lib/module/components/ai-input/index.js +1568 -0
- package/lib/module/components/ai-input/index.js.map +1 -0
- package/lib/module/components/alert/index.js +1 -1
- package/lib/module/components/alert/index.js.map +1 -1
- package/lib/module/components/avatar/avatar-source.js +15 -0
- package/lib/module/components/avatar/avatar-source.js.map +1 -0
- package/lib/module/components/avatar/index.js +32 -31
- package/lib/module/components/avatar/index.js.map +1 -1
- package/lib/module/components/bottom-sheet/index.js +3 -2
- package/lib/module/components/bottom-sheet/index.js.map +1 -1
- package/lib/module/components/button-group/index.js +2 -3
- package/lib/module/components/button-group/index.js.map +1 -1
- package/lib/module/components/carousel/index.js +10 -7
- package/lib/module/components/carousel/index.js.map +1 -1
- package/lib/module/components/collapsible/index.js +1 -1
- package/lib/module/components/collapsible/index.js.map +1 -1
- package/lib/module/components/field/index.js +7 -8
- package/lib/module/components/field/index.js.map +1 -1
- package/lib/module/components/label/index.js +1 -1
- package/lib/module/components/label/index.js.map +1 -1
- package/lib/module/components/marquee/index.js +36 -11
- package/lib/module/components/marquee/index.js.map +1 -1
- package/lib/module/components/marquee/marquee-math.js +22 -6
- package/lib/module/components/marquee/marquee-math.js.map +1 -1
- package/lib/module/components/plan/index.js +2 -1
- package/lib/module/components/plan/index.js.map +1 -1
- package/lib/module/components/plot/index.js +27 -9
- package/lib/module/components/plot/index.js.map +1 -1
- package/lib/module/components/search-bar/index.js +11 -3
- package/lib/module/components/search-bar/index.js.map +1 -1
- package/lib/module/components/split-view/index.js +26 -27
- package/lib/module/components/split-view/index.js.map +1 -1
- package/lib/module/components/split-view/split-view-lifecycle.js +32 -0
- package/lib/module/components/split-view/split-view-lifecycle.js.map +1 -0
- package/lib/module/components/splitter/index.js +7 -13
- package/lib/module/components/splitter/index.js.map +1 -1
- package/lib/module/components/splitter/splitter-math.js +14 -0
- package/lib/module/components/splitter/splitter-math.js.map +1 -1
- package/lib/module/components/tabs/index.js +1 -1
- package/lib/module/components/tabs/index.js.map +1 -1
- package/lib/module/components/timeline/index.js +156 -42
- package/lib/module/components/timeline/index.js.map +1 -1
- package/lib/module/components/timeline/timeline-geometry.js +11 -0
- package/lib/module/components/timeline/timeline-geometry.js.map +1 -1
- package/lib/module/components/toast/index.js +1 -1
- package/lib/module/components/toast/index.js.map +1 -1
- package/lib/module/components/tooltip/index.js +2 -2
- package/lib/module/components/tooltip/index.js.map +1 -1
- package/lib/module/components/tree/index.js +2 -2
- package/lib/module/components/tree/index.js.map +1 -1
- package/lib/module/components/typography/index.js +2 -1
- package/lib/module/components/typography/index.js.map +1 -1
- package/lib/module/components/waterfall-chart/index.js +22 -3
- package/lib/module/components/waterfall-chart/index.js.map +1 -1
- package/lib/module/hooks/use-disclosure.js +10 -8
- package/lib/module/hooks/use-disclosure.js.map +1 -1
- package/lib/module/icons/index.js +135 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +5 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/primitives/controllable-state.js +61 -0
- package/lib/module/primitives/controllable-state.js.map +1 -0
- package/lib/module/primitives/finite-chart.js +15 -0
- package/lib/module/primitives/finite-chart.js.map +1 -0
- package/lib/module/primitives/glass.js +136 -0
- package/lib/module/primitives/glass.js.map +1 -0
- package/lib/module/primitives/scrim.js +8 -1
- package/lib/module/primitives/scrim.js.map +1 -1
- package/lib/typescript/src/components/ai-input/ai-input-growth.d.ts +59 -0
- package/lib/typescript/src/components/ai-input/ai-input-growth.d.ts.map +1 -0
- package/lib/typescript/src/components/ai-input/index.d.ts +423 -0
- package/lib/typescript/src/components/ai-input/index.d.ts.map +1 -0
- package/lib/typescript/src/components/avatar/avatar-source.d.ts +4 -0
- package/lib/typescript/src/components/avatar/avatar-source.d.ts.map +1 -0
- package/lib/typescript/src/components/avatar/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bottom-sheet/index.d.ts +10 -1
- package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/src/components/carousel/index.d.ts.map +1 -1
- package/lib/typescript/src/components/marquee/index.d.ts +15 -6
- package/lib/typescript/src/components/marquee/index.d.ts.map +1 -1
- package/lib/typescript/src/components/marquee/marquee-math.d.ts +6 -0
- package/lib/typescript/src/components/marquee/marquee-math.d.ts.map +1 -1
- package/lib/typescript/src/components/plan/index.d.ts.map +1 -1
- package/lib/typescript/src/components/plot/index.d.ts +2 -1
- package/lib/typescript/src/components/plot/index.d.ts.map +1 -1
- package/lib/typescript/src/components/search-bar/index.d.ts.map +1 -1
- package/lib/typescript/src/components/split-view/index.d.ts.map +1 -1
- package/lib/typescript/src/components/split-view/split-view-lifecycle.d.ts +14 -0
- package/lib/typescript/src/components/split-view/split-view-lifecycle.d.ts.map +1 -0
- package/lib/typescript/src/components/splitter/index.d.ts +2 -2
- package/lib/typescript/src/components/splitter/index.d.ts.map +1 -1
- package/lib/typescript/src/components/splitter/splitter-math.d.ts +2 -0
- package/lib/typescript/src/components/splitter/splitter-math.d.ts.map +1 -1
- package/lib/typescript/src/components/timeline/index.d.ts +24 -2
- package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
- package/lib/typescript/src/components/timeline/timeline-geometry.d.ts +2 -0
- package/lib/typescript/src/components/timeline/timeline-geometry.d.ts.map +1 -1
- package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
- package/lib/typescript/src/components/waterfall-chart/index.d.ts +2 -1
- package/lib/typescript/src/components/waterfall-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-disclosure.d.ts.map +1 -1
- package/lib/typescript/src/icons/index.d.ts +18 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/controllable-state.d.ts +33 -0
- package/lib/typescript/src/primitives/controllable-state.d.ts.map +1 -0
- package/lib/typescript/src/primitives/finite-chart.d.ts +5 -0
- package/lib/typescript/src/primitives/finite-chart.d.ts.map +1 -0
- package/lib/typescript/src/primitives/glass.d.ts +43 -0
- package/lib/typescript/src/primitives/glass.d.ts.map +1 -0
- package/lib/typescript/src/primitives/scrim.d.ts +7 -0
- package/lib/typescript/src/primitives/scrim.d.ts.map +1 -1
- package/package.json +7 -2
- package/src/components/ai-input/ai-input-growth.ts +65 -0
- package/src/components/ai-input/index.tsx +1937 -0
- package/src/components/alert/index.tsx +1 -1
- package/src/components/avatar/avatar-source.ts +18 -0
- package/src/components/avatar/index.tsx +35 -27
- package/src/components/bottom-sheet/index.tsx +15 -3
- package/src/components/button-group/index.tsx +1 -1
- package/src/components/carousel/index.tsx +10 -11
- package/src/components/collapsible/index.tsx +1 -1
- package/src/components/field/index.tsx +4 -4
- package/src/components/label/index.tsx +1 -1
- package/src/components/marquee/index.tsx +56 -12
- package/src/components/marquee/marquee-math.ts +26 -3
- package/src/components/plan/index.tsx +6 -2
- package/src/components/plot/index.tsx +37 -11
- package/src/components/search-bar/index.tsx +9 -3
- package/src/components/split-view/index.tsx +24 -33
- package/src/components/split-view/split-view-lifecycle.ts +43 -0
- package/src/components/splitter/index.tsx +7 -11
- package/src/components/splitter/splitter-math.ts +28 -0
- package/src/components/tabs/index.tsx +1 -1
- package/src/components/timeline/index.tsx +189 -34
- package/src/components/timeline/timeline-geometry.ts +11 -0
- package/src/components/toast/index.tsx +6 -1
- package/src/components/tooltip/index.tsx +1 -1
- package/src/components/tree/index.tsx +2 -2
- package/src/components/typography/index.tsx +2 -2
- package/src/components/waterfall-chart/index.tsx +37 -4
- package/src/hooks/use-disclosure.ts +7 -12
- package/src/icons/index.tsx +106 -0
- package/src/index.ts +27 -0
- package/src/primitives/controllable-state.ts +112 -0
- package/src/primitives/finite-chart.ts +14 -0
- package/src/primitives/glass.tsx +171 -0
- package/src/primitives/scrim.tsx +7 -1
|
@@ -286,10 +286,10 @@ const TreeRoot = forwardRef<View, TreeProps>(
|
|
|
286
286
|
<TreeContext.Provider value={context}>
|
|
287
287
|
<Animated.View
|
|
288
288
|
ref={ref}
|
|
289
|
+
{...props}
|
|
289
290
|
layout={LinearTransition.duration(TRANSITION_DURATION)}
|
|
290
291
|
accessibilityRole="list"
|
|
291
292
|
className={root({ className })}
|
|
292
|
-
{...props}
|
|
293
293
|
>
|
|
294
294
|
{children}
|
|
295
295
|
</Animated.View>
|
|
@@ -394,6 +394,7 @@ const TreeTrigger = forwardRef<View, TreeTriggerProps>(
|
|
|
394
394
|
return (
|
|
395
395
|
<Pressable
|
|
396
396
|
ref={ref}
|
|
397
|
+
{...props}
|
|
397
398
|
accessibilityRole="button"
|
|
398
399
|
accessibilityState={{
|
|
399
400
|
expanded: isBranch ? isExpanded : undefined,
|
|
@@ -403,7 +404,6 @@ const TreeTrigger = forwardRef<View, TreeTriggerProps>(
|
|
|
403
404
|
aria-level={level + 1}
|
|
404
405
|
disabled={triggerDisabled}
|
|
405
406
|
className={trigger({ className })}
|
|
406
|
-
{...props}
|
|
407
407
|
onPress={handlePress}
|
|
408
408
|
>
|
|
409
409
|
<IconColorProvider color={isSelected ? selectedColor : undefined}>
|
|
@@ -290,9 +290,9 @@ export interface TypographyListProps extends ViewProps {
|
|
|
290
290
|
*/
|
|
291
291
|
const TypographyList = forwardRef<View, TypographyListProps>(
|
|
292
292
|
({ className, ordered = false, children, ...props }, ref) => (
|
|
293
|
-
<View ref={ref} accessibilityRole="list" className={cn('gap-2', className)}
|
|
293
|
+
<View ref={ref} {...props} accessibilityRole="list" className={cn('gap-2', className)}>
|
|
294
294
|
{Children.map(children, (child, index) => (
|
|
295
|
-
<View className="w-full flex-row gap-2">
|
|
295
|
+
<View role="listitem" className="w-full flex-row gap-2">
|
|
296
296
|
{ordered ? (
|
|
297
297
|
<Text className="text-base text-muted-foreground">{index + 1}.</Text>
|
|
298
298
|
) : (
|
|
@@ -65,7 +65,12 @@ import Animated, {
|
|
|
65
65
|
} from 'react-native-reanimated';
|
|
66
66
|
import Svg, { Defs, G, Line as SvgLine, LinearGradient, Path, Stop } from 'react-native-svg';
|
|
67
67
|
import { useCSSVariable } from 'uniwind';
|
|
68
|
+
import { ChartAccessibilityData, type ChartAccessibilityProps } from '../../primitives/chart-accessibility';
|
|
68
69
|
import { Text } from '../../primitives/text';
|
|
70
|
+
import {
|
|
71
|
+
finiteChartDomain,
|
|
72
|
+
finiteChartNumber,
|
|
73
|
+
} from '../../primitives/finite-chart';
|
|
69
74
|
import { barPath, compactNumber, type Plot } from '../../utils/chart';
|
|
70
75
|
import { cn } from '../../utils/cn';
|
|
71
76
|
|
|
@@ -196,7 +201,7 @@ export function waterfallSteps(data: WaterfallDatum[]): WaterfallStep[] {
|
|
|
196
201
|
let running = 0;
|
|
197
202
|
|
|
198
203
|
for (const datum of data) {
|
|
199
|
-
const value =
|
|
204
|
+
const value = finiteChartNumber(datum.value) ?? 0;
|
|
200
205
|
|
|
201
206
|
if (datum.total) {
|
|
202
207
|
// A total is a reading, so it is measured from the baseline and the
|
|
@@ -228,7 +233,7 @@ export function waterfallSteps(data: WaterfallDatum[]): WaterfallStep[] {
|
|
|
228
233
|
return steps;
|
|
229
234
|
}
|
|
230
235
|
|
|
231
|
-
export interface WaterfallChartProps extends ViewProps {
|
|
236
|
+
export interface WaterfallChartProps extends ViewProps, ChartAccessibilityProps<WaterfallStep> {
|
|
232
237
|
className?: string;
|
|
233
238
|
/** The steps, in the order they happen. */
|
|
234
239
|
data: WaterfallDatum[];
|
|
@@ -327,6 +332,11 @@ const WaterfallChartRoot = forwardRef<WaterfallChartHandle, WaterfallChartProps>
|
|
|
327
332
|
fallColor,
|
|
328
333
|
totalColor,
|
|
329
334
|
onActiveIndexChange,
|
|
335
|
+
accessible,
|
|
336
|
+
accessibilityLabel,
|
|
337
|
+
accessibilityHint,
|
|
338
|
+
accessibilityLabelForDatum,
|
|
339
|
+
onAccessibilityDatumPress,
|
|
330
340
|
compact = false,
|
|
331
341
|
children,
|
|
332
342
|
...props
|
|
@@ -385,7 +395,8 @@ const WaterfallChartRoot = forwardRef<WaterfallChartHandle, WaterfallChartProps>
|
|
|
385
395
|
};
|
|
386
396
|
|
|
387
397
|
const extent = useMemo<[number, number]>(() => {
|
|
388
|
-
|
|
398
|
+
const explicit = finiteChartDomain(yDomain);
|
|
399
|
+
if (explicit) return explicit;
|
|
389
400
|
|
|
390
401
|
/*
|
|
391
402
|
* Both ends of every bar, not just the values. A step's bar occupies the
|
|
@@ -535,7 +546,29 @@ const WaterfallChartRoot = forwardRef<WaterfallChartHandle, WaterfallChartProps>
|
|
|
535
546
|
<WaterfallChartContext.Provider value={context}>
|
|
536
547
|
<View {...props} style={props.style} className={cn('w-full', className)}>
|
|
537
548
|
{header}
|
|
538
|
-
<
|
|
549
|
+
<ChartAccessibilityData
|
|
550
|
+
chart="Waterfall chart"
|
|
551
|
+
data={steps}
|
|
552
|
+
disabled={accessible === false || loading}
|
|
553
|
+
accessibilityLabel={accessibilityLabel}
|
|
554
|
+
accessibilityHint={accessibilityHint}
|
|
555
|
+
accessibilityLabelForDatum={accessibilityLabelForDatum}
|
|
556
|
+
onAccessibilityDatumPress={onAccessibilityDatumPress}
|
|
557
|
+
valueOf={(step) => [
|
|
558
|
+
['label', step.label],
|
|
559
|
+
['change', step.value],
|
|
560
|
+
['kind', step.kind],
|
|
561
|
+
['starts at', step.start],
|
|
562
|
+
['ends at', step.end],
|
|
563
|
+
]}
|
|
564
|
+
/>
|
|
565
|
+
<View
|
|
566
|
+
onLayout={onLayout}
|
|
567
|
+
style={{ aspectRatio }}
|
|
568
|
+
className="w-full"
|
|
569
|
+
accessibilityElementsHidden
|
|
570
|
+
importantForAccessibility="no-hide-descendants"
|
|
571
|
+
>
|
|
539
572
|
{plot.width > 0 ? (
|
|
540
573
|
<>
|
|
541
574
|
<Svg width="100%" height="100%" style={StyleSheet.absoluteFill}>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { useCallback, useMemo
|
|
1
|
+
import { useCallback, useMemo } from 'react';
|
|
2
|
+
import { useControllableState } from '../primitives/controllable-state';
|
|
2
3
|
|
|
3
4
|
export interface UseDisclosureOptions {
|
|
4
5
|
defaultOpen?: boolean;
|
|
@@ -34,17 +35,11 @@ export function useDisclosure({
|
|
|
34
35
|
open: controlledOpen,
|
|
35
36
|
onOpenChange,
|
|
36
37
|
}: UseDisclosureOptions = {}): UseDisclosureResult {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
(next: boolean) => {
|
|
43
|
-
if (!isControlled) setUncontrolledOpen(next);
|
|
44
|
-
onOpenChange?.(next);
|
|
45
|
-
},
|
|
46
|
-
[isControlled, onOpenChange]
|
|
47
|
-
);
|
|
38
|
+
const { value: isOpen, setValue: setOpen } = useControllableState({
|
|
39
|
+
value: controlledOpen,
|
|
40
|
+
defaultValue: defaultOpen,
|
|
41
|
+
onChange: onOpenChange,
|
|
42
|
+
});
|
|
48
43
|
|
|
49
44
|
const open = useCallback(() => setOpen(true), [setOpen]);
|
|
50
45
|
const close = useCallback(() => setOpen(false), [setOpen]);
|
package/src/icons/index.tsx
CHANGED
|
@@ -852,6 +852,22 @@ export function PaperclipIcon({ size = 16, color, ...props }: IconProps) {
|
|
|
852
852
|
}
|
|
853
853
|
|
|
854
854
|
/** A capsule on a stand — a microphone, for anything voice. */
|
|
855
|
+
/** A voice waveform — dictation, voice mode, a recording that has a shape. */
|
|
856
|
+
export function AudioLinesIcon({ size = 16, color, ...props }: IconProps) {
|
|
857
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
858
|
+
return (
|
|
859
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
860
|
+
<Path
|
|
861
|
+
d="M2 10v3M6 6v11M10 3v18M14 8v7M18 5v13M22 10v3"
|
|
862
|
+
stroke={resolved}
|
|
863
|
+
strokeWidth={2}
|
|
864
|
+
strokeLinecap="round"
|
|
865
|
+
strokeLinejoin="round"
|
|
866
|
+
/>
|
|
867
|
+
</Svg>
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
|
|
855
871
|
export function MicIcon({ size = 16, color, ...props }: IconProps) {
|
|
856
872
|
const resolved = useResolvedColor(color, '#737373');
|
|
857
873
|
return (
|
|
@@ -1078,6 +1094,73 @@ export function CrosshairIcon({ size = 16, color, ...props }: IconProps) {
|
|
|
1078
1094
|
}
|
|
1079
1095
|
|
|
1080
1096
|
/** A compass needle — which way is north. */
|
|
1097
|
+
export function GlobeIcon({ size = 16, color, ...props }: IconProps) {
|
|
1098
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1099
|
+
return (
|
|
1100
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1101
|
+
<Circle
|
|
1102
|
+
cx={12}
|
|
1103
|
+
cy={12}
|
|
1104
|
+
r={10}
|
|
1105
|
+
stroke={resolved}
|
|
1106
|
+
strokeWidth={2}
|
|
1107
|
+
strokeLinecap="round"
|
|
1108
|
+
strokeLinejoin="round"
|
|
1109
|
+
/>
|
|
1110
|
+
<Path
|
|
1111
|
+
d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20M2 12h20"
|
|
1112
|
+
stroke={resolved}
|
|
1113
|
+
strokeWidth={2}
|
|
1114
|
+
strokeLinecap="round"
|
|
1115
|
+
strokeLinejoin="round"
|
|
1116
|
+
/>
|
|
1117
|
+
</Svg>
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
export function CameraIcon({ size = 16, color, ...props }: IconProps) {
|
|
1122
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1123
|
+
return (
|
|
1124
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1125
|
+
<Path
|
|
1126
|
+
d="M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z"
|
|
1127
|
+
stroke={resolved}
|
|
1128
|
+
strokeWidth={2}
|
|
1129
|
+
strokeLinecap="round"
|
|
1130
|
+
strokeLinejoin="round"
|
|
1131
|
+
/>
|
|
1132
|
+
<Circle
|
|
1133
|
+
cx={12}
|
|
1134
|
+
cy={13}
|
|
1135
|
+
r={3}
|
|
1136
|
+
stroke={resolved}
|
|
1137
|
+
strokeWidth={2}
|
|
1138
|
+
strokeLinecap="round"
|
|
1139
|
+
strokeLinejoin="round"
|
|
1140
|
+
/>
|
|
1141
|
+
</Svg>
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
/**
|
|
1146
|
+
* The glyph on a control that opens a list of values — a stacked pair, because
|
|
1147
|
+
* one chevron promises a direction the control does not travel in.
|
|
1148
|
+
*/
|
|
1149
|
+
export function ChevronsUpDownIcon({ size = 16, color, ...props }: IconProps) {
|
|
1150
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1151
|
+
return (
|
|
1152
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1153
|
+
<Path
|
|
1154
|
+
d="m7 15 5 5 5-5M7 9l5-5 5 5"
|
|
1155
|
+
stroke={resolved}
|
|
1156
|
+
strokeWidth={2}
|
|
1157
|
+
strokeLinecap="round"
|
|
1158
|
+
strokeLinejoin="round"
|
|
1159
|
+
/>
|
|
1160
|
+
</Svg>
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1081
1164
|
export function CompassIcon({ size = 16, color, ...props }: IconProps) {
|
|
1082
1165
|
const resolved = useResolvedColor(color, '#737373');
|
|
1083
1166
|
return (
|
|
@@ -1256,6 +1339,29 @@ export interface ToggleIconProps extends IconProps {
|
|
|
1256
1339
|
}
|
|
1257
1340
|
|
|
1258
1341
|
/** Upvote. Filled once the vote is cast. */
|
|
1342
|
+
/**
|
|
1343
|
+
* A plain up arrow, drawn symmetrically about the centre of its box so it sits
|
|
1344
|
+
* dead centre in a circular button.
|
|
1345
|
+
*
|
|
1346
|
+
* Distinct from `ArrowUpIcon`, which is the heavy solid arrow a vote control
|
|
1347
|
+
* wants, and from `SendIcon`, which is a paper plane. This is the one a
|
|
1348
|
+
* composer's send button uses.
|
|
1349
|
+
*/
|
|
1350
|
+
export function SendArrowIcon({ size = 16, color, ...props }: IconProps) {
|
|
1351
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1352
|
+
return (
|
|
1353
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1354
|
+
<Path
|
|
1355
|
+
d="m5 12 7-7 7 7M12 19V5"
|
|
1356
|
+
stroke={resolved}
|
|
1357
|
+
strokeWidth={2.5}
|
|
1358
|
+
strokeLinecap="round"
|
|
1359
|
+
strokeLinejoin="round"
|
|
1360
|
+
/>
|
|
1361
|
+
</Svg>
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1259
1365
|
export function ArrowUpIcon({ size = 16, color, filled, ...props }: ToggleIconProps) {
|
|
1260
1366
|
const resolved = useResolvedColor(color, '#737373');
|
|
1261
1367
|
return (
|
package/src/index.ts
CHANGED
|
@@ -20,6 +20,9 @@ export { Portal, PortalHost, PortalProvider } from './primitives/portal';
|
|
|
20
20
|
// recede. Public so a custom overlay gets the same backdrop — and the same
|
|
21
21
|
// graceful fall back from blur to dim — as the built-in ones.
|
|
22
22
|
export { Scrim, hasBlur, type ScrimProps } from './primitives/scrim';
|
|
23
|
+
// The material iOS draws its floating controls in, with a solid fallback where
|
|
24
|
+
// there is none. Public so a surface of your own gets the same treatment.
|
|
25
|
+
export { Glass, hasGlass, type GlassProps, type GlassVariant, type GlassRadius } from './primitives/glass';
|
|
23
26
|
export { Text, type TextProps } from './primitives/text';
|
|
24
27
|
export { Collapse, type CollapseProps } from './primitives/collapse';
|
|
25
28
|
export {
|
|
@@ -935,6 +938,25 @@ export {
|
|
|
935
938
|
type SignatureToolbarProps,
|
|
936
939
|
type SignatureButtonProps,
|
|
937
940
|
} from './components/signature';
|
|
941
|
+
export {
|
|
942
|
+
AIInput,
|
|
943
|
+
type AIInputProps,
|
|
944
|
+
type AIInputStatus,
|
|
945
|
+
type AIInputFieldProps,
|
|
946
|
+
type AIInputRowProps,
|
|
947
|
+
type AIInputToolbarProps,
|
|
948
|
+
type AIInputActionProps,
|
|
949
|
+
type AIInputPillProps,
|
|
950
|
+
type AIInputSubmitProps,
|
|
951
|
+
type AIInputRecordingProps,
|
|
952
|
+
type AIInputSheetProps,
|
|
953
|
+
type AIInputSheetScreenProps,
|
|
954
|
+
type AIInputSheetGroupProps,
|
|
955
|
+
type AIInputSheetRowProps,
|
|
956
|
+
type AIInputSheetToggleProps,
|
|
957
|
+
type AIInputSheetChoiceProps,
|
|
958
|
+
type AIInputVoiceModeProps,
|
|
959
|
+
} from './components/ai-input';
|
|
938
960
|
export { Shimmer, type ShimmerProps } from './components/shimmer';
|
|
939
961
|
export {
|
|
940
962
|
Reasoning,
|
|
@@ -1190,17 +1212,20 @@ export {
|
|
|
1190
1212
|
ArrowDownIcon,
|
|
1191
1213
|
ArrowUpIcon,
|
|
1192
1214
|
ArrowUpRightIcon,
|
|
1215
|
+
AudioLinesIcon,
|
|
1193
1216
|
BadgeCheckIcon,
|
|
1194
1217
|
BellIcon,
|
|
1195
1218
|
BookmarkIcon,
|
|
1196
1219
|
BoldIcon,
|
|
1197
1220
|
CalendarIcon,
|
|
1221
|
+
CameraIcon,
|
|
1198
1222
|
CardIcon,
|
|
1199
1223
|
CheckCircleIcon,
|
|
1200
1224
|
CheckIcon,
|
|
1201
1225
|
ChevronDownIcon,
|
|
1202
1226
|
ChevronLeftIcon,
|
|
1203
1227
|
ChevronRightIcon,
|
|
1228
|
+
ChevronsUpDownIcon,
|
|
1204
1229
|
ChevronUpIcon,
|
|
1205
1230
|
CircleIcon,
|
|
1206
1231
|
CodeIcon,
|
|
@@ -1215,6 +1240,7 @@ export {
|
|
|
1215
1240
|
FileIcon,
|
|
1216
1241
|
FolderIcon,
|
|
1217
1242
|
FolderOpenIcon,
|
|
1243
|
+
GlobeIcon,
|
|
1218
1244
|
GoogleIcon,
|
|
1219
1245
|
HeartIcon,
|
|
1220
1246
|
HeadingIcon,
|
|
@@ -1247,6 +1273,7 @@ export {
|
|
|
1247
1273
|
RotateCcwIcon,
|
|
1248
1274
|
RotateCwIcon,
|
|
1249
1275
|
SearchIcon,
|
|
1276
|
+
SendArrowIcon,
|
|
1250
1277
|
SendIcon,
|
|
1251
1278
|
ShareNodesIcon,
|
|
1252
1279
|
ShieldAlertIcon,
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useCallback,
|
|
3
|
+
useLayoutEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
type Dispatch,
|
|
8
|
+
type SetStateAction,
|
|
9
|
+
} from 'react';
|
|
10
|
+
|
|
11
|
+
const identity = <T,>(value: T): T => value;
|
|
12
|
+
const objectIs = <T,>(left: T, right: T): boolean => Object.is(left, right);
|
|
13
|
+
|
|
14
|
+
export interface ControllableStateOptions<T> {
|
|
15
|
+
/** Accepted owner state. `undefined` selects uncontrolled ownership. */
|
|
16
|
+
value: T | undefined;
|
|
17
|
+
/** Initial state used only while uncontrolled. */
|
|
18
|
+
defaultValue: T;
|
|
19
|
+
/** Reports a distinct requested value exactly once per setter invocation. */
|
|
20
|
+
onChange?: (value: T) => void;
|
|
21
|
+
/** Reports accepted state changes, whether requested or externally reset. */
|
|
22
|
+
onSettled?: (value: T) => void;
|
|
23
|
+
/** Normalize defaults, controlled values, and requests through one boundary. */
|
|
24
|
+
normalize?: (value: T) => T;
|
|
25
|
+
/** Equality used for no-op requests and accepted settlement. */
|
|
26
|
+
isEqual?: (left: T, right: T) => boolean;
|
|
27
|
+
/** Stable semantic identity for controlled objects recreated by their owner. */
|
|
28
|
+
getValueKey?: (value: T) => unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ControllableStateResult<T> {
|
|
32
|
+
/** State accepted by the current owner; controlled requests never render optimistically. */
|
|
33
|
+
value: T;
|
|
34
|
+
setValue: Dispatch<SetStateAction<T>>;
|
|
35
|
+
isControlled: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Owner-driven controlled/uncontrolled state with an explicit settlement edge.
|
|
40
|
+
*
|
|
41
|
+
* Switching from controlled to uncontrolled retains the last accepted owner
|
|
42
|
+
* value instead of resurrecting a stale default. Switching into controlled
|
|
43
|
+
* mode adopts the prop immediately. `undefined` is the ownership sentinel and
|
|
44
|
+
* therefore cannot itself be a controlled value; use `null` for empty state.
|
|
45
|
+
*/
|
|
46
|
+
export function useControllableState<T>({
|
|
47
|
+
value: valueProp,
|
|
48
|
+
defaultValue,
|
|
49
|
+
onChange,
|
|
50
|
+
onSettled,
|
|
51
|
+
normalize = identity,
|
|
52
|
+
isEqual = objectIs,
|
|
53
|
+
getValueKey,
|
|
54
|
+
}: ControllableStateOptions<T>): ControllableStateResult<T> {
|
|
55
|
+
const [internalValue, setInternalValue] = useState(() =>
|
|
56
|
+
normalize(defaultValue)
|
|
57
|
+
);
|
|
58
|
+
const isControlled = valueProp !== undefined;
|
|
59
|
+
const controlledKey = isControlled
|
|
60
|
+
? getValueKey
|
|
61
|
+
? getValueKey(valueProp as T)
|
|
62
|
+
: valueProp
|
|
63
|
+
: undefined;
|
|
64
|
+
const controlledValue = useMemo(
|
|
65
|
+
() => (isControlled ? normalize(valueProp as T) : undefined),
|
|
66
|
+
// The semantic key deliberately replaces object identity for equivalent owner values.
|
|
67
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
68
|
+
[controlledKey, isControlled, normalize]
|
|
69
|
+
);
|
|
70
|
+
const wasControlled = useRef(isControlled);
|
|
71
|
+
const lastControlledValue = useRef<T | undefined>(controlledValue);
|
|
72
|
+
if (isControlled) lastControlledValue.current = controlledValue;
|
|
73
|
+
|
|
74
|
+
const leavingControlled = wasControlled.current && !isControlled;
|
|
75
|
+
const value = isControlled
|
|
76
|
+
? (controlledValue as T)
|
|
77
|
+
: leavingControlled
|
|
78
|
+
? (lastControlledValue.current as T)
|
|
79
|
+
: internalValue;
|
|
80
|
+
const valueRef = useRef(value);
|
|
81
|
+
valueRef.current = value;
|
|
82
|
+
|
|
83
|
+
useLayoutEffect(() => {
|
|
84
|
+
if (wasControlled.current && !isControlled) {
|
|
85
|
+
setInternalValue(lastControlledValue.current as T);
|
|
86
|
+
}
|
|
87
|
+
wasControlled.current = isControlled;
|
|
88
|
+
}, [isControlled]);
|
|
89
|
+
|
|
90
|
+
const previousSettled = useRef(value);
|
|
91
|
+
useLayoutEffect(() => {
|
|
92
|
+
if (isEqual(previousSettled.current, value)) return;
|
|
93
|
+
previousSettled.current = value;
|
|
94
|
+
onSettled?.(value);
|
|
95
|
+
}, [isEqual, onSettled, value]);
|
|
96
|
+
|
|
97
|
+
const setValue = useCallback<Dispatch<SetStateAction<T>>>(
|
|
98
|
+
(next) => {
|
|
99
|
+
const requested = normalize(
|
|
100
|
+
typeof next === 'function'
|
|
101
|
+
? (next as (current: T) => T)(valueRef.current)
|
|
102
|
+
: next
|
|
103
|
+
);
|
|
104
|
+
if (isEqual(valueRef.current, requested)) return;
|
|
105
|
+
if (!isControlled) setInternalValue(requested);
|
|
106
|
+
onChange?.(requested);
|
|
107
|
+
},
|
|
108
|
+
[isControlled, isEqual, normalize, onChange]
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
return { value, setValue, isControlled };
|
|
112
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Returns a number only when it is safe to use in chart arithmetic. */
|
|
2
|
+
export function finiteChartNumber(value: unknown): number | undefined {
|
|
3
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/** Accepts an explicit domain only when both ends are finite. */
|
|
7
|
+
export function finiteChartDomain(
|
|
8
|
+
domain: readonly [number, number] | undefined
|
|
9
|
+
): [number, number] | undefined {
|
|
10
|
+
if (!domain) return undefined;
|
|
11
|
+
const low = finiteChartNumber(domain[0]);
|
|
12
|
+
const high = finiteChartNumber(domain[1]);
|
|
13
|
+
return low === undefined || high === undefined ? undefined : [low, high];
|
|
14
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glass — the material iOS draws its own floating controls in.
|
|
3
|
+
*
|
|
4
|
+
* A control that floats over content needs to read as *over* it, and a flat
|
|
5
|
+
* fill cannot say that: it either hides what is behind it or disappears into
|
|
6
|
+
* it. The system material does both jobs at once — it refracts what is behind,
|
|
7
|
+
* lifts its own edge, and stays legible over anything.
|
|
8
|
+
*
|
|
9
|
+
* ```tsx
|
|
10
|
+
* <Glass radius={28} className="px-4 py-3">
|
|
11
|
+
* <Text>Over whatever is behind it.</Text>
|
|
12
|
+
* </Glass>
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* ## Where it is real, and where it is not
|
|
16
|
+
*
|
|
17
|
+
* The material exists on iOS 26 and above. Below that, on Android, on web, and
|
|
18
|
+
* for anyone who has switched Reduce Transparency on, this draws
|
|
19
|
+
* `fallbackClassName` instead — a solid token surface. That is deliberate
|
|
20
|
+
* rather than a gap: a hand-drawn approximation of a system material is a
|
|
21
|
+
* near-miss on the one platform that has the real thing, and on Android it is
|
|
22
|
+
* an iOS look pasted onto a platform that never asked for it.
|
|
23
|
+
*
|
|
24
|
+
* So `Glass` is not a promise that the screen will be glass. It is a promise
|
|
25
|
+
* that the container will be drawn correctly either way, and callers should
|
|
26
|
+
* pick a `fallbackClassName` that stands on its own.
|
|
27
|
+
*
|
|
28
|
+
* ## The material is a layer, not the box
|
|
29
|
+
*
|
|
30
|
+
* The native view is rendered as a non-interactive layer filling this
|
|
31
|
+
* container, with the content above it, for the same reason `Scrim` layers a
|
|
32
|
+
* blur: it keeps every layout class, every token and every touch target on an
|
|
33
|
+
* ordinary `View` that behaves the way the rest of the library does.
|
|
34
|
+
*
|
|
35
|
+
* `radius` is a number rather than a class because the material has to round
|
|
36
|
+
* its own corners — clipping a square one to a rounded parent throws away the
|
|
37
|
+
* lit edge that makes it read as glass.
|
|
38
|
+
*
|
|
39
|
+
* ## Do not fade it
|
|
40
|
+
*
|
|
41
|
+
* Setting `opacity` to `0` on the material or on anything above it stops it
|
|
42
|
+
* rendering at all, and it does not come back when the opacity does. Move it,
|
|
43
|
+
* or unmount it; never animate it out.
|
|
44
|
+
*/
|
|
45
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
46
|
+
import { Platform, StyleSheet, View, type StyleProp, type ViewProps, type ViewStyle } from 'react-native';
|
|
47
|
+
import { cn } from '../utils/cn';
|
|
48
|
+
import { useReduceTransparency } from './scrim';
|
|
49
|
+
|
|
50
|
+
/** How the material treats what is behind it. */
|
|
51
|
+
export type GlassVariant = 'regular' | 'clear';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Corner radius in points — one number for all four, or a top and a bottom.
|
|
55
|
+
*
|
|
56
|
+
* The two-sided form is for a surface with an edge that is not a real edge: a
|
|
57
|
+
* sheet docked to the bottom of the screen rounds its top and leaves its
|
|
58
|
+
* bottom square, because the screen edge is where it ends.
|
|
59
|
+
*/
|
|
60
|
+
export type GlassRadius = number | { top?: number; bottom?: number };
|
|
61
|
+
|
|
62
|
+
function shapeOf(radius: GlassRadius | undefined) {
|
|
63
|
+
if (radius === undefined) return null;
|
|
64
|
+
if (typeof radius === 'number') return { borderRadius: radius };
|
|
65
|
+
return {
|
|
66
|
+
borderTopLeftRadius: radius.top ?? 0,
|
|
67
|
+
borderTopRightRadius: radius.top ?? 0,
|
|
68
|
+
borderBottomLeftRadius: radius.bottom ?? 0,
|
|
69
|
+
borderBottomRightRadius: radius.bottom ?? 0,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface GlassViewProps {
|
|
74
|
+
glassEffectStyle?: GlassVariant | 'none';
|
|
75
|
+
tintColor?: string;
|
|
76
|
+
colorScheme?: 'auto' | 'light' | 'dark';
|
|
77
|
+
style?: StyleProp<ViewStyle>;
|
|
78
|
+
pointerEvents?: ViewProps['pointerEvents'];
|
|
79
|
+
children?: ReactNode;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* `expo-glass-effect`'s GlassView, or null when the material cannot be drawn
|
|
84
|
+
* here.
|
|
85
|
+
*
|
|
86
|
+
* Resolved once at module load, behind three gates that all have to pass. The
|
|
87
|
+
* package is optional, so the require can fail; the API is missing from some
|
|
88
|
+
* iOS 26 builds, and reaching for it there crashes; and the design itself is
|
|
89
|
+
* only present when the app was compiled for it. Asking all three once is
|
|
90
|
+
* cheaper than a try/catch on every render, and there is no answer that can
|
|
91
|
+
* change while the process is alive.
|
|
92
|
+
*/
|
|
93
|
+
const GlassView: ComponentType<GlassViewProps> | null = (() => {
|
|
94
|
+
if (Platform.OS !== 'ios') return null;
|
|
95
|
+
try {
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
97
|
+
const mod = require('expo-glass-effect');
|
|
98
|
+
if (typeof mod?.isGlassEffectAPIAvailable === 'function' && !mod.isGlassEffectAPIAvailable()) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
if (typeof mod?.isLiquidGlassAvailable === 'function' && !mod.isLiquidGlassAvailable()) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
return (mod?.GlassView as ComponentType<GlassViewProps>) ?? null;
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
})();
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* True when the real material can be drawn — for a caller that wants to know
|
|
112
|
+
* before it commits to a look. It says nothing about Reduce Transparency, which
|
|
113
|
+
* is a live preference and belongs to the hook.
|
|
114
|
+
*/
|
|
115
|
+
export const hasGlass = GlassView !== null;
|
|
116
|
+
|
|
117
|
+
export interface GlassProps extends ViewProps {
|
|
118
|
+
/**
|
|
119
|
+
* How much of what is behind shows through. `regular` is the everyday
|
|
120
|
+
* material; `clear` is thinner, for something over its own artwork.
|
|
121
|
+
*/
|
|
122
|
+
variant?: GlassVariant;
|
|
123
|
+
/** Tints the material. Takes a colour, not a token name. */
|
|
124
|
+
tint?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Corner radius in points. The material rounds itself to this, so give it
|
|
127
|
+
* the same shape the container is drawn with — clipping a square material
|
|
128
|
+
* to a rounded parent throws away the lit edge that makes it read as glass.
|
|
129
|
+
*/
|
|
130
|
+
radius?: GlassRadius;
|
|
131
|
+
/** Applied only when the material cannot be drawn. Give it a real surface. */
|
|
132
|
+
fallbackClassName?: string;
|
|
133
|
+
className?: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function Glass({
|
|
137
|
+
variant = 'regular',
|
|
138
|
+
tint,
|
|
139
|
+
radius,
|
|
140
|
+
fallbackClassName = 'bg-card',
|
|
141
|
+
className,
|
|
142
|
+
children,
|
|
143
|
+
style,
|
|
144
|
+
...props
|
|
145
|
+
}: GlassProps) {
|
|
146
|
+
const reduceTransparency = useReduceTransparency();
|
|
147
|
+
// Not knowing yet counts as "do not draw it": the material arriving a frame
|
|
148
|
+
// late is invisible, and one flashing at somebody who opted out is not.
|
|
149
|
+
const material = GlassView !== null && reduceTransparency === false;
|
|
150
|
+
const shape = shapeOf(radius);
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<View
|
|
154
|
+
className={cn('overflow-hidden', material ? null : fallbackClassName, className)}
|
|
155
|
+
style={[shape, style]}
|
|
156
|
+
{...props}
|
|
157
|
+
>
|
|
158
|
+
{material && GlassView ? (
|
|
159
|
+
<GlassView
|
|
160
|
+
glassEffectStyle={variant}
|
|
161
|
+
tintColor={tint}
|
|
162
|
+
pointerEvents="none"
|
|
163
|
+
style={[StyleSheet.absoluteFill, shape]}
|
|
164
|
+
/>
|
|
165
|
+
) : null}
|
|
166
|
+
{children}
|
|
167
|
+
</View>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
Glass.displayName = 'Glass';
|
package/src/primitives/scrim.tsx
CHANGED
|
@@ -113,7 +113,13 @@ function observeReduceTransparency(
|
|
|
113
113
|
*/
|
|
114
114
|
let knownReduceTransparency: boolean | null = null;
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Whether the device asks for Reduce Transparency: `true`, `false`, or `null`
|
|
118
|
+
* while the platform has not answered yet. Exported because every material in
|
|
119
|
+
* the library has to respect the same preference, and two copies of this would
|
|
120
|
+
* be two answers.
|
|
121
|
+
*/
|
|
122
|
+
export function useReduceTransparency() {
|
|
117
123
|
// Not knowing is deliberately conservative: draw opaque until the platform
|
|
118
124
|
// says blur is allowed, rather than flashing blur at someone who opted out.
|
|
119
125
|
const [enabled, setEnabled] = useState<boolean | null>(knownReduceTransparency);
|