@xpufx/paseo-top 0.4.3 → 0.4.4
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/client/pill-labels.ts +30 -6
- package/client/pill.tsx +40 -61
- package/client/settings-events.ts +33 -0
- package/client/surface.tsx +6 -2
- package/client/vendor/paseo-plugin-helper/components/Badge.tsx +12 -0
- package/client/vendor/paseo-plugin-helper/components/Button.tsx +22 -2
- package/client/vendor/paseo-plugin-helper/components/Card.tsx +32 -0
- package/client/vendor/paseo-plugin-helper/components/DataTable.tsx +17 -1
- package/client/vendor/paseo-plugin-helper/components/InlineButton.tsx +14 -1
- package/client/vendor/paseo-plugin-helper/components/InteractiveRow.tsx +142 -0
- package/client/vendor/paseo-plugin-helper/components/SearchInput.tsx +16 -2
- package/client/vendor/paseo-plugin-helper/components/SectionHeader.tsx +7 -0
- package/client/vendor/paseo-plugin-helper/components/Select.tsx +131 -63
- package/client/vendor/paseo-plugin-helper/components/index.ts +1 -0
- package/client/vendor/paseo-plugin-helper/custom-pills.tsx +4 -3
- package/client/vendor/paseo-plugin-helper/host.ts +12 -58
- package/client/vendor/paseo-plugin-helper/index.ts +1 -0
- package/client/vendor/paseo-plugin-helper/layout/ModalBody.tsx +17 -27
- package/client/vendor/paseo-plugin-helper/pill.tsx +68 -321
- package/client/vendor/paseo-plugin-helper/surface.tsx +62 -2
- package/client/vendor/paseo-plugin-helper/tickets.tsx +766 -0
- package/index.server.ts +7 -0
- package/package.json +11 -2
- package/server/vendor/paseo-plugin-helper/index.ts +2 -0
- package/server/vendor/paseo-plugin-helper/mcp-injection.ts +20 -2
- package/server/vendor/paseo-plugin-helper/plugins.ts +184 -19
- package/server/vendor/paseo-plugin-helper/tickets.ts +1281 -0
- package/shared/helper-version.ts +20 -0
- package/shared/multi-host.ts +11 -4
- package/shared/resources.ts +1 -1
- package/shared/vendor/paseo-plugin-helper/index.ts +1 -0
- package/shared/vendor/paseo-plugin-helper/tickets.ts +1010 -0
- package/shared/version.ts +1 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import React, { useCallback, useState, type ReactNode } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Pressable,
|
|
4
|
+
type AccessibilityRole,
|
|
5
|
+
type GestureResponderEvent,
|
|
6
|
+
type StyleProp,
|
|
7
|
+
type ViewStyle,
|
|
8
|
+
} from "react-native";
|
|
9
|
+
import { usePluginTheme } from "../theme/provider";
|
|
10
|
+
|
|
11
|
+
export interface InteractiveRowProps {
|
|
12
|
+
/** Arbitrary row content — status dots, badges, text, metric readouts. */
|
|
13
|
+
children?: ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* Press handler. Receives the raw event so callers can control propagation
|
|
16
|
+
* (e.g. a nested status light calling `event.stopPropagation()`).
|
|
17
|
+
*/
|
|
18
|
+
onPress?: (event: GestureResponderEvent) => void;
|
|
19
|
+
/** Native / RN-web tooltip text attached to the hit area. */
|
|
20
|
+
title?: string;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
accessibilityRole?: AccessibilityRole;
|
|
23
|
+
accessibilityLabel?: string;
|
|
24
|
+
accessibilityHint?: string;
|
|
25
|
+
testID?: string;
|
|
26
|
+
/** Base layout style, merged before the interaction layers. */
|
|
27
|
+
style?: StyleProp<ViewStyle>;
|
|
28
|
+
/** Extra style layered only while hovered (and not disabled). */
|
|
29
|
+
hoverStyle?: StyleProp<ViewStyle>;
|
|
30
|
+
/** Apply a subtle theme accent tint while hovered. Default: false. */
|
|
31
|
+
hoverTint?: boolean;
|
|
32
|
+
/** Accent opacity used by `hoverTint`. Default: 0.05. */
|
|
33
|
+
hoverTintOpacity?: number;
|
|
34
|
+
/** Opacity while pressed. Default: 0.7. */
|
|
35
|
+
pressedOpacity?: number;
|
|
36
|
+
/** Opacity while hovered but not pressed. Defaults to `opacity`. */
|
|
37
|
+
hoveredOpacity?: number;
|
|
38
|
+
/** Opacity at rest. Default: 1. */
|
|
39
|
+
opacity?: number;
|
|
40
|
+
/** Opacity when disabled. Default: 0.45. */
|
|
41
|
+
disabledOpacity?: number;
|
|
42
|
+
/** Expanded touch target beyond the visual bounds. */
|
|
43
|
+
hitSlop?: number;
|
|
44
|
+
/** Notified whenever the hover state flips, for callers that tint their own children. */
|
|
45
|
+
onHoverChange?: (hovered: boolean) => void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Hover-aware, pressable row container for dense interactive content (#580).
|
|
50
|
+
*
|
|
51
|
+
* Owns the interaction concerns a labeled `Button` cannot: a hover state (with
|
|
52
|
+
* optional accent tint), an RN-web `title` tooltip, pressed opacity, a pointer
|
|
53
|
+
* cursor, and an `onPress` that forwards the event for `stopPropagation`.
|
|
54
|
+
* Content is arbitrary, so a status dot, badges, and metric readouts compose
|
|
55
|
+
* inside without wrapping each in its own control.
|
|
56
|
+
*/
|
|
57
|
+
export function InteractiveRow({
|
|
58
|
+
children,
|
|
59
|
+
onPress,
|
|
60
|
+
title,
|
|
61
|
+
disabled = false,
|
|
62
|
+
accessibilityRole,
|
|
63
|
+
accessibilityLabel,
|
|
64
|
+
accessibilityHint,
|
|
65
|
+
testID,
|
|
66
|
+
style,
|
|
67
|
+
hoverStyle,
|
|
68
|
+
hoverTint = false,
|
|
69
|
+
hoverTintOpacity = 0.05,
|
|
70
|
+
pressedOpacity = 0.7,
|
|
71
|
+
hoveredOpacity,
|
|
72
|
+
opacity = 1,
|
|
73
|
+
disabledOpacity = 0.45,
|
|
74
|
+
hitSlop,
|
|
75
|
+
onHoverChange,
|
|
76
|
+
}: InteractiveRowProps) {
|
|
77
|
+
const { colors, alpha } = usePluginTheme();
|
|
78
|
+
const [hovered, setHovered] = useState(false);
|
|
79
|
+
const [pressed, setPressed] = useState(false);
|
|
80
|
+
|
|
81
|
+
const handleMouseEnter = useCallback(() => {
|
|
82
|
+
if (disabled) return;
|
|
83
|
+
setHovered(true);
|
|
84
|
+
onHoverChange?.(true);
|
|
85
|
+
}, [disabled, onHoverChange]);
|
|
86
|
+
|
|
87
|
+
const handleMouseLeave = useCallback(() => {
|
|
88
|
+
setHovered(false);
|
|
89
|
+
onHoverChange?.(false);
|
|
90
|
+
}, [onHoverChange]);
|
|
91
|
+
|
|
92
|
+
// `title` is an RN-web DOM passthrough absent from the native prop types, and
|
|
93
|
+
// `onMouseEnter`/`onMouseLeave` are the RN-web hover hooks the dense rows
|
|
94
|
+
// already relied on; attach both via an untyped spread to keep the runtime
|
|
95
|
+
// semantics identical to the hand-rolled rows (#580).
|
|
96
|
+
const webProps = {
|
|
97
|
+
onMouseEnter: handleMouseEnter,
|
|
98
|
+
onMouseLeave: handleMouseLeave,
|
|
99
|
+
...(title ? { title } : {}),
|
|
100
|
+
} as any;
|
|
101
|
+
|
|
102
|
+
const interactive = hovered && !disabled;
|
|
103
|
+
const hoverBackground = interactive && hoverTint
|
|
104
|
+
? { backgroundColor: alpha(colors.accent, hoverTintOpacity) }
|
|
105
|
+
: null;
|
|
106
|
+
const restingOpacity = interactive ? (hoveredOpacity ?? opacity) : opacity;
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<Pressable
|
|
110
|
+
{...webProps}
|
|
111
|
+
onPress={onPress}
|
|
112
|
+
onPressIn={() => setPressed(true)}
|
|
113
|
+
onPressOut={() => setPressed(false)}
|
|
114
|
+
disabled={disabled}
|
|
115
|
+
accessibilityRole={accessibilityRole ?? (onPress ? "button" : undefined)}
|
|
116
|
+
accessibilityLabel={accessibilityLabel}
|
|
117
|
+
accessibilityHint={accessibilityHint}
|
|
118
|
+
testID={testID}
|
|
119
|
+
hitSlop={hitSlop}
|
|
120
|
+
style={[
|
|
121
|
+
{ cursor: onPress && !disabled ? "pointer" : "auto" },
|
|
122
|
+
// A pressable wraps arbitrary content (titles, badges, metric readouts)
|
|
123
|
+
// and must never widen its parent: Yoga defaults `flexShrink` to 0, so
|
|
124
|
+
// without a shrink budget a long label forces the whole row past the
|
|
125
|
+
// viewport. Declared before `style` so callers can still override.
|
|
126
|
+
{ flexShrink: 1, maxWidth: "100%" },
|
|
127
|
+
style,
|
|
128
|
+
hoverBackground,
|
|
129
|
+
interactive ? hoverStyle : null,
|
|
130
|
+
{
|
|
131
|
+
opacity: disabled
|
|
132
|
+
? disabledOpacity
|
|
133
|
+
: pressed
|
|
134
|
+
? pressedOpacity
|
|
135
|
+
: restingOpacity,
|
|
136
|
+
},
|
|
137
|
+
]}
|
|
138
|
+
>
|
|
139
|
+
{children}
|
|
140
|
+
</Pressable>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
@@ -18,6 +18,15 @@ export interface SearchInputProps {
|
|
|
18
18
|
onChangeText: (text: string) => void;
|
|
19
19
|
placeholder?: string;
|
|
20
20
|
onClear?: () => void;
|
|
21
|
+
/**
|
|
22
|
+
* Painted box height, opt-in per call site. Unset keeps the original
|
|
23
|
+
* `isCompact ? 36 : 40` box so no untouched surface changes appearance.
|
|
24
|
+
* 26 matches the compact `size="sm"` button recipe (~26px) so a search field
|
|
25
|
+
* can sit beside filter pills in one row without inflating it (#645); a
|
|
26
|
+
* height of 26 or less also switches the input to the 12px `sm` label size,
|
|
27
|
+
* anything taller keeps the prior 13/14.
|
|
28
|
+
*/
|
|
29
|
+
height?: number;
|
|
21
30
|
style?: StyleProp<ViewStyle>;
|
|
22
31
|
inputStyle?: StyleProp<TextStyle>;
|
|
23
32
|
testID?: string;
|
|
@@ -31,6 +40,7 @@ export function SearchInput({
|
|
|
31
40
|
onChangeText,
|
|
32
41
|
placeholder = "Search...",
|
|
33
42
|
onClear,
|
|
43
|
+
height,
|
|
34
44
|
style,
|
|
35
45
|
inputStyle,
|
|
36
46
|
testID,
|
|
@@ -40,6 +50,10 @@ export function SearchInput({
|
|
|
40
50
|
RNTextInput) as ComponentType<RNTextInputProps & { ref?: Ref<RNTextInputInstance> }>;
|
|
41
51
|
const { colors, resolveRadius, isCompact } = usePluginTheme();
|
|
42
52
|
const radius = resolveRadius("sm");
|
|
53
|
+
// Unspecified keeps the original compact-aware default so every call site
|
|
54
|
+
// that does not opt in renders exactly as before (#645's scope was the
|
|
55
|
+
// uppidi-fleet filter rows only).
|
|
56
|
+
const resolvedHeight = height ?? (isCompact ? 36 : 40);
|
|
43
57
|
|
|
44
58
|
const handleClear = () => {
|
|
45
59
|
onChangeText("");
|
|
@@ -54,7 +68,7 @@ export function SearchInput({
|
|
|
54
68
|
backgroundColor: colors.surface1,
|
|
55
69
|
borderColor: colors.border,
|
|
56
70
|
borderRadius: radius,
|
|
57
|
-
height:
|
|
71
|
+
height: resolvedHeight,
|
|
58
72
|
},
|
|
59
73
|
style,
|
|
60
74
|
]}
|
|
@@ -72,7 +86,7 @@ export function SearchInput({
|
|
|
72
86
|
styles.input,
|
|
73
87
|
{
|
|
74
88
|
color: colors.foreground,
|
|
75
|
-
fontSize: isCompact ? 13 : 14,
|
|
89
|
+
fontSize: height !== undefined && height <= 26 ? 12 : isCompact ? 13 : 14,
|
|
76
90
|
},
|
|
77
91
|
inputStyle,
|
|
78
92
|
]}
|
|
@@ -41,6 +41,8 @@ export function SectionHeader({
|
|
|
41
41
|
return (
|
|
42
42
|
<View style={[styles.container, style]}>
|
|
43
43
|
<Text
|
|
44
|
+
numberOfLines={1}
|
|
45
|
+
ellipsizeMode="tail"
|
|
44
46
|
style={[
|
|
45
47
|
styles.title,
|
|
46
48
|
{
|
|
@@ -73,8 +75,13 @@ const styles = StyleSheet.create({
|
|
|
73
75
|
gap: 8,
|
|
74
76
|
marginTop: 8,
|
|
75
77
|
marginBottom: 2,
|
|
78
|
+
flexShrink: 1,
|
|
79
|
+
maxWidth: "100%",
|
|
76
80
|
},
|
|
77
81
|
title: {
|
|
78
82
|
letterSpacing: 0.8,
|
|
83
|
+
// Section titles are caller-supplied and unbounded; the title must be
|
|
84
|
+
// allowed to compress for `numberOfLines` to engage.
|
|
85
|
+
flexShrink: 1,
|
|
79
86
|
},
|
|
80
87
|
});
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useRef, useState } from "react";
|
|
2
2
|
import {
|
|
3
|
+
Modal,
|
|
3
4
|
Pressable,
|
|
4
5
|
ScrollView as FallbackScrollView,
|
|
5
6
|
StyleSheet,
|
|
6
7
|
Text,
|
|
8
|
+
TouchableWithoutFeedback,
|
|
7
9
|
View,
|
|
8
10
|
type StyleProp,
|
|
9
11
|
type ViewStyle,
|
|
@@ -32,10 +34,25 @@ export interface SelectProps {
|
|
|
32
34
|
|
|
33
35
|
const OPTION_LIST_MAX_HEIGHT = 216;
|
|
34
36
|
|
|
37
|
+
interface TriggerCoords {
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// When `measureInWindow` is unavailable or never invokes its callback (node
|
|
45
|
+
// test renderers, host shims), the menu still opens anchored to a zeroed box
|
|
46
|
+
// rather than swallowing the press.
|
|
47
|
+
const FALLBACK_COORDS: TriggerCoords = { x: 0, y: 0, width: 0, height: 0 };
|
|
48
|
+
|
|
35
49
|
/**
|
|
36
50
|
* Compact single-choice picker sized to sit inside a {@link FormRow}. The
|
|
37
|
-
* closed trigger stays one line tall; opening
|
|
38
|
-
*
|
|
51
|
+
* closed trigger stays one line tall; opening mounts the menu in a root
|
|
52
|
+
* transparent `<Modal>` overlay portal, so a long list never expands the
|
|
53
|
+
* trigger's parent container, is never clipped by an ancestor
|
|
54
|
+
* `overflow: "hidden"` (Card/Tabs/Modal), and always paints above later
|
|
55
|
+
* siblings regardless of local stacking context.
|
|
39
56
|
*/
|
|
40
57
|
export function Select({
|
|
41
58
|
value,
|
|
@@ -48,12 +65,14 @@ export function Select({
|
|
|
48
65
|
style,
|
|
49
66
|
}: SelectProps) {
|
|
50
67
|
const { Icon } = getClientHost();
|
|
51
|
-
// The
|
|
52
|
-
//
|
|
53
|
-
//
|
|
68
|
+
// The option list is mounted in the root modal overlay, so it scrolls with
|
|
69
|
+
// a plain React Native ScrollView — never the host sheet-gesture scroller
|
|
70
|
+
// (#219) and never a scroller owned by the trigger's parent.
|
|
54
71
|
const { colors, resolveRadius, typography, isCompact, touchTargetMin, alpha } =
|
|
55
72
|
usePluginTheme();
|
|
56
73
|
const [open, setOpen] = useState(false);
|
|
74
|
+
const [coords, setCoords] = useState<TriggerCoords | null>(null);
|
|
75
|
+
const triggerRef = useRef<View>(null);
|
|
57
76
|
|
|
58
77
|
const caption = typography?.caption ?? {
|
|
59
78
|
fontSize: 11,
|
|
@@ -76,14 +95,40 @@ export function Select({
|
|
|
76
95
|
|
|
77
96
|
const triggerMinHeight = size === "sm" ? 28 : Math.max(34, touchTargetMin);
|
|
78
97
|
|
|
98
|
+
const openAt = (next: TriggerCoords) => {
|
|
99
|
+
setCoords(next);
|
|
100
|
+
setOpen(true);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const handleToggle = () => {
|
|
104
|
+
if (isOpen) {
|
|
105
|
+
setOpen(false);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const node = triggerRef.current;
|
|
110
|
+
// A node/shim without `measureInWindow` (node test renderers, stripped
|
|
111
|
+
// host shims) still opens anchored to a zeroed box rather than swallowing
|
|
112
|
+
// the press (#520).
|
|
113
|
+
if (!node || typeof node.measureInWindow !== "function") {
|
|
114
|
+
openAt(FALLBACK_COORDS);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
node.measureInWindow((x, y, width, height) => {
|
|
119
|
+
openAt({ x, y, width, height });
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
|
|
79
123
|
return (
|
|
80
124
|
<View style={[styles.container, style]}>
|
|
81
125
|
<Pressable
|
|
126
|
+
ref={triggerRef}
|
|
82
127
|
accessibilityRole="button"
|
|
83
128
|
accessibilityLabel={label ? `${label}: ${display}` : display}
|
|
84
129
|
accessibilityState={{ expanded: isOpen, disabled }}
|
|
85
130
|
disabled={!canOpen}
|
|
86
|
-
onPress={
|
|
131
|
+
onPress={handleToggle}
|
|
87
132
|
style={({ pressed }) => [
|
|
88
133
|
styles.trigger,
|
|
89
134
|
{
|
|
@@ -122,67 +167,84 @@ export function Select({
|
|
|
122
167
|
/>
|
|
123
168
|
</Pressable>
|
|
124
169
|
|
|
125
|
-
{isOpen ? (
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
borderColor: colors.border,
|
|
132
|
-
backgroundColor: colors.surface0,
|
|
133
|
-
marginTop: spacing.xs,
|
|
134
|
-
},
|
|
135
|
-
]}
|
|
170
|
+
{isOpen && coords ? (
|
|
171
|
+
<Modal
|
|
172
|
+
transparent
|
|
173
|
+
visible={isOpen}
|
|
174
|
+
animationType="none"
|
|
175
|
+
onRequestClose={() => setOpen(false)}
|
|
136
176
|
>
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
177
|
+
<TouchableWithoutFeedback
|
|
178
|
+
accessibilityRole="button"
|
|
179
|
+
accessibilityLabel="Dismiss options"
|
|
180
|
+
onPress={() => setOpen(false)}
|
|
181
|
+
>
|
|
182
|
+
<View style={StyleSheet.absoluteFill} />
|
|
183
|
+
</TouchableWithoutFeedback>
|
|
184
|
+
|
|
185
|
+
<View
|
|
186
|
+
style={[
|
|
187
|
+
styles.overlayList,
|
|
188
|
+
{
|
|
189
|
+
top: coords.y + coords.height + spacing.xs,
|
|
190
|
+
left: coords.x,
|
|
191
|
+
minWidth: coords.width,
|
|
192
|
+
borderRadius: radius,
|
|
193
|
+
borderColor: colors.border,
|
|
194
|
+
backgroundColor: colors.surface0,
|
|
195
|
+
},
|
|
196
|
+
]}
|
|
141
197
|
>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
: pressed
|
|
162
|
-
? alpha(colors.surface2, 0.5)
|
|
163
|
-
: "transparent",
|
|
164
|
-
},
|
|
165
|
-
]}
|
|
166
|
-
>
|
|
167
|
-
<Text
|
|
168
|
-
numberOfLines={1}
|
|
169
|
-
style={[
|
|
170
|
-
styles.optionText,
|
|
198
|
+
<FallbackScrollView
|
|
199
|
+
nestedScrollEnabled
|
|
200
|
+
keyboardShouldPersistTaps="handled"
|
|
201
|
+
style={{ maxHeight: OPTION_LIST_MAX_HEIGHT }}
|
|
202
|
+
>
|
|
203
|
+
{options.map((option) => {
|
|
204
|
+
const isSelected = option.value === value;
|
|
205
|
+
return (
|
|
206
|
+
<Pressable
|
|
207
|
+
key={option.value}
|
|
208
|
+
accessibilityRole="button"
|
|
209
|
+
accessibilityLabel={option.label}
|
|
210
|
+
accessibilityState={{ selected: isSelected }}
|
|
211
|
+
onPress={() => {
|
|
212
|
+
onValueChange(option.value);
|
|
213
|
+
setOpen(false);
|
|
214
|
+
}}
|
|
215
|
+
style={({ pressed }) => [
|
|
216
|
+
styles.option,
|
|
171
217
|
{
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
218
|
+
paddingVertical,
|
|
219
|
+
paddingHorizontal,
|
|
220
|
+
backgroundColor: isSelected
|
|
221
|
+
? colors.surface2
|
|
222
|
+
: pressed
|
|
223
|
+
? alpha(colors.surface2, 0.5)
|
|
224
|
+
: "transparent",
|
|
176
225
|
},
|
|
177
226
|
]}
|
|
178
227
|
>
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
228
|
+
<Text
|
|
229
|
+
numberOfLines={1}
|
|
230
|
+
style={[
|
|
231
|
+
styles.optionText,
|
|
232
|
+
{
|
|
233
|
+
color: isSelected ? colors.foreground : colors.foregroundMuted,
|
|
234
|
+
fontSize: isCompact ? fontSize : fontSize + 1,
|
|
235
|
+
lineHeight,
|
|
236
|
+
fontWeight: isSelected ? "600" : "400",
|
|
237
|
+
},
|
|
238
|
+
]}
|
|
239
|
+
>
|
|
240
|
+
{option.label}
|
|
241
|
+
</Text>
|
|
242
|
+
</Pressable>
|
|
243
|
+
);
|
|
244
|
+
})}
|
|
245
|
+
</FallbackScrollView>
|
|
246
|
+
</View>
|
|
247
|
+
</Modal>
|
|
186
248
|
) : null}
|
|
187
249
|
</View>
|
|
188
250
|
);
|
|
@@ -203,9 +265,15 @@ const styles = StyleSheet.create({
|
|
|
203
265
|
flexShrink: 1,
|
|
204
266
|
minWidth: 0,
|
|
205
267
|
},
|
|
206
|
-
|
|
268
|
+
overlayList: {
|
|
269
|
+
position: "absolute",
|
|
207
270
|
borderWidth: 1,
|
|
208
271
|
overflow: "hidden",
|
|
272
|
+
elevation: 20,
|
|
273
|
+
shadowColor: "#000",
|
|
274
|
+
shadowOffset: { width: 0, height: 4 },
|
|
275
|
+
shadowOpacity: 0.25,
|
|
276
|
+
shadowRadius: 8,
|
|
209
277
|
},
|
|
210
278
|
option: {},
|
|
211
279
|
optionText: {
|
|
@@ -17,8 +17,10 @@ export interface CustomPillBodyProps {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Standard pill body renderer for a custom metric pill
|
|
21
|
-
*
|
|
20
|
+
* Standard pill body renderer for a custom metric pill. The host renders the
|
|
21
|
+
* composer pill body itself, so reach for this from a pill's own surface
|
|
22
|
+
* (`renderModal`) rather than from the trackbar; it adapts to responsive
|
|
23
|
+
* compact/mobile modes and shows threshold status.
|
|
22
24
|
*/
|
|
23
25
|
export function CustomPillBody({ state }: CustomPillBodyProps) {
|
|
24
26
|
const { Icon } = getClientHost();
|
|
@@ -145,7 +147,6 @@ export function registerCustomPills(
|
|
|
145
147
|
flair: options.flair,
|
|
146
148
|
resolveLabel: () =>
|
|
147
149
|
pill.displayValue ? `${pill.title} ${pill.displayValue}` : pill.title,
|
|
148
|
-
renderPill: () => <CustomPillBody state={pill} />,
|
|
149
150
|
renderModal: () => {
|
|
150
151
|
const [refreshing, setRefreshing] = useState(false);
|
|
151
152
|
const [currentOutput, setCurrentOutput] = useState<string | undefined>(
|
|
@@ -14,20 +14,23 @@ import type {
|
|
|
14
14
|
TextInput as RNTextInput,
|
|
15
15
|
TextInputProps,
|
|
16
16
|
} from "react-native";
|
|
17
|
+
import type {
|
|
18
|
+
PluginButtonRegistration,
|
|
19
|
+
PluginComposerPillContribution,
|
|
20
|
+
} from "@getpaseo/plugin/client";
|
|
17
21
|
|
|
18
22
|
/**
|
|
19
23
|
* Structural host types for Paseo client integration.
|
|
20
24
|
*
|
|
21
25
|
* These interfaces describe the shapes `paseo-plugin-helper/client` needs
|
|
22
|
-
* from the Paseo host app
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* bundle satisfies both the Paseo v0.7 SDK entry points and the Paseo
|
|
26
|
-
* v0.8 runtime-owned entry points.
|
|
26
|
+
* from the Paseo host app: the runtime dependencies the plugin injects once
|
|
27
|
+
* in its client entry — the host theme, the layout, the icon/Modal primitives,
|
|
28
|
+
* the agents feed — so they can be supplied from any entry point.
|
|
27
29
|
*
|
|
28
|
-
* The
|
|
29
|
-
*
|
|
30
|
-
*
|
|
30
|
+
* The composer pill is the exception: the host owns that contribution's shape,
|
|
31
|
+
* so it is imported from the Paseo SDK rather than restated here. A restated
|
|
32
|
+
* copy is what let a pre-0.9 `{Component, onPress}` pill through the type
|
|
33
|
+
* system (#666).
|
|
31
34
|
*/
|
|
32
35
|
|
|
33
36
|
export interface HostThemeColors {
|
|
@@ -216,57 +219,8 @@ export interface HostSurfaceProps {
|
|
|
216
219
|
};
|
|
217
220
|
}
|
|
218
221
|
|
|
219
|
-
export interface ComposerPillContribution {
|
|
220
|
-
id: string;
|
|
221
|
-
title: string;
|
|
222
|
-
workspaceId: string;
|
|
223
|
-
agentId: string;
|
|
224
|
-
Component: ComponentType<HostPillProps>;
|
|
225
|
-
onPress(): void | Promise<void>;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export type ComposerPillButtonIcon = string | ComponentType<any>;
|
|
229
|
-
|
|
230
|
-
export interface ComposerPillButtonDescriptor {
|
|
231
|
-
title: string;
|
|
232
|
-
icon: ComposerPillButtonIcon;
|
|
233
|
-
label?: string;
|
|
234
|
-
visible?: boolean;
|
|
235
|
-
disabled?: boolean;
|
|
236
|
-
behavior:
|
|
237
|
-
| { kind: "action"; onPress(): void | Promise<void> }
|
|
238
|
-
| { kind: "popover"; Content: ComponentType<any> };
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export interface ComposerPillButtonContribution {
|
|
242
|
-
id: string;
|
|
243
|
-
workspaceId: string;
|
|
244
|
-
agentId: string;
|
|
245
|
-
button: ComposerPillButtonDescriptor;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
export interface ComposerPillSdkContribution {
|
|
249
|
-
id: string;
|
|
250
|
-
workspaceId: string;
|
|
251
|
-
agentId: string;
|
|
252
|
-
button: Record<string, any>;
|
|
253
|
-
[key: string]: any;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
export interface ComposerPillRegistrationHandle {
|
|
257
|
-
update(patch: Record<string, any>): void;
|
|
258
|
-
remove(): void;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export type ComposerPillRegistration = PluginCleanup | ComposerPillRegistrationHandle;
|
|
262
|
-
|
|
263
222
|
export interface ComposerPillRegistrar {
|
|
264
|
-
addComposerPill(
|
|
265
|
-
contribution:
|
|
266
|
-
| ComposerPillContribution
|
|
267
|
-
| ComposerPillButtonContribution
|
|
268
|
-
| ComposerPillSdkContribution,
|
|
269
|
-
): ComposerPillRegistration;
|
|
223
|
+
addComposerPill(contribution: PluginComposerPillContribution): PluginButtonRegistration;
|
|
270
224
|
paseo: {
|
|
271
225
|
agents: HostAgentsApi;
|
|
272
226
|
};
|