Skip to content

Commit d6c7f75

Browse files
authored
Merge branch 'v0.2' into fix/android-pin-button-animation
2 parents 6114381 + 0e7bc35 commit d6c7f75

5 files changed

Lines changed: 69 additions & 46 deletions

File tree

src/components/SlotText/SlotText.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export function SlotText({
4949
colorFadeDurationMs,
5050
containerStyle,
5151
durationMs,
52+
ellipsizeMode,
5253
exitOffsetMs,
5354
maxFontSizeMultiplier,
5455
maxGraphemes,
@@ -59,6 +60,7 @@ export function SlotText({
5960
textClassName,
6061
textStyle,
6162
}: SlotTextProps) {
63+
const resolvedEllipsizeMode = ellipsizeMode ?? 'clip';
6264
const reducedMotion = useReducedMotion();
6365
const resolved = resolveSlotTextOptions({
6466
bounce,
@@ -174,6 +176,7 @@ export function SlotText({
174176
accessible={false}
175177
allowFontScaling={allowFontScaling}
176178
className={textClassName}
179+
ellipsizeMode={resolvedEllipsizeMode}
177180
maxFontSizeMultiplier={maxFontSizeMultiplier}
178181
numberOfLines={1}
179182
style={textStyle}
@@ -230,6 +233,7 @@ export function SlotText({
230233
allowFontScaling={allowFontScaling}
231234
currentSegment={currentSegment}
232235
currentWidth={currentMetrics?.width ?? 0}
236+
ellipsizeMode={resolvedEllipsizeMode}
233237
entryPhaseStartMs={transitionPhaseTiming.entryPhaseStartMs}
234238
exitPhaseDurationMs={transitionPhaseTiming.exitPhaseDurationMs}
235239
height={height}
@@ -252,6 +256,7 @@ export function SlotText({
252256
accessible={false}
253257
allowFontScaling={allowFontScaling}
254258
className={textClassName}
259+
ellipsizeMode={resolvedEllipsizeMode}
255260
maxFontSizeMultiplier={maxFontSizeMultiplier}
256261
numberOfLines={1}
257262
style={textStyle}
@@ -272,6 +277,7 @@ export function SlotText({
272277
accessible={false}
273278
allowFontScaling={allowFontScaling}
274279
className={textClassName}
280+
ellipsizeMode={resolvedEllipsizeMode}
275281
key={segment}
276282
maxFontSizeMultiplier={maxFontSizeMultiplier}
277283
numberOfLines={1}

src/components/SlotText/SlotText.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export type SlotTextProps = {
1515
/** Keep a grapheme static only when it is unchanged and its slot offset will not move. */
1616
skipUnchanged?: boolean;
1717
staggerMs?: number;
18+
/** How a grapheme is truncated when its slot is narrower than the glyph. Defaults to 'clip'. */
19+
ellipsizeMode?: 'head' | 'middle' | 'tail' | 'clip';
1820
testID?: string;
1921
text: string;
2022
textClassName?: string;

src/components/SlotText/components/SlotGlyph.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type SlotGlyphProps = {
3535
allowFontScaling?: boolean;
3636
currentSegment: string;
3737
currentWidth: number;
38+
ellipsizeMode?: 'head' | 'middle' | 'tail' | 'clip';
3839
entryPhaseStartMs: number;
3940
exitPhaseDurationMs: number;
4041
height: number;
@@ -53,7 +54,7 @@ type SlotGlyphProps = {
5354

5455
type GlyphTextProps = Pick<
5556
SlotGlyphProps,
56-
'allowFontScaling' | 'maxFontSizeMultiplier' | 'textClassName' | 'textStyle'
57+
'allowFontScaling' | 'ellipsizeMode' | 'maxFontSizeMultiplier' | 'textClassName' | 'textStyle'
5758
> & {
5859
color?: string;
5960
segment: string;
@@ -63,6 +64,7 @@ export function SlotGlyph({
6364
allowFontScaling,
6465
currentSegment,
6566
currentWidth,
67+
ellipsizeMode,
6668
entryPhaseStartMs,
6769
exitPhaseDurationMs,
6870
height,
@@ -222,6 +224,7 @@ export function SlotGlyph({
222224
<Animated.View style={[faceStyle, outgoingStyle]}>
223225
<GlyphText
224226
allowFontScaling={allowFontScaling}
227+
ellipsizeMode={ellipsizeMode}
225228
maxFontSizeMultiplier={maxFontSizeMultiplier}
226229
segment={currentSegment}
227230
textClassName={textClassName}
@@ -234,6 +237,7 @@ export function SlotGlyph({
234237
<Animated.View style={[faceStyle, incomingStyle]}>
235238
<GlyphText
236239
allowFontScaling={allowFontScaling}
240+
ellipsizeMode={ellipsizeMode}
237241
maxFontSizeMultiplier={maxFontSizeMultiplier}
238242
segment={targetSegment}
239243
textClassName={textClassName}
@@ -247,6 +251,7 @@ export function SlotGlyph({
247251
<GlyphText
248252
allowFontScaling={allowFontScaling}
249253
color={highlightColor}
254+
ellipsizeMode={ellipsizeMode}
250255
maxFontSizeMultiplier={maxFontSizeMultiplier}
251256
segment={targetSegment}
252257
textClassName={textClassName}
@@ -261,6 +266,7 @@ export function SlotGlyph({
261266
<View style={faceStyle}>
262267
<GlyphText
263268
allowFontScaling={allowFontScaling}
269+
ellipsizeMode={ellipsizeMode}
264270
maxFontSizeMultiplier={maxFontSizeMultiplier}
265271
segment={targetSegment}
266272
textClassName={textClassName}
@@ -308,6 +314,7 @@ function createFaceAnimationStyle(
308314
function GlyphText({
309315
allowFontScaling,
310316
color,
317+
ellipsizeMode,
311318
maxFontSizeMultiplier,
312319
segment,
313320
textClassName,
@@ -318,6 +325,7 @@ function GlyphText({
318325
accessible={false}
319326
allowFontScaling={allowFontScaling}
320327
className={textClassName}
328+
ellipsizeMode={ellipsizeMode}
321329
maxFontSizeMultiplier={maxFontSizeMultiplier}
322330
numberOfLines={1}
323331
style={[textStyle, color ? { color } : undefined]}

src/components/headers/MainHeader/MainHeader.android.tsx

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
import { type MenuAction, MenuView, type NativeActionEvent } from '@expo/ui/community/menu';
21
import { Stack, useRouter } from 'expo-router';
32
import { useThemeColor } from 'heroui-native/hooks';
4-
import { ChevronLeftIcon, EllipsisIcon, SquarePenIcon } from 'lucide-uniwind/png';
5-
import { useCallback, useMemo } from 'react';
3+
import { Menu } from 'heroui-native/menu';
4+
import {
5+
ChevronLeftIcon,
6+
EllipsisIcon,
7+
PencilIcon,
8+
PinIcon,
9+
SquarePenIcon,
10+
} from 'lucide-uniwind/png';
11+
import { useCallback } from 'react';
612
import { useTranslation } from 'react-i18next';
7-
import { StyleSheet, View } from 'react-native';
13+
import { Pressable, StyleSheet, View } from 'react-native';
814
import { useSafeAreaInsets } from 'react-native-safe-area-context';
15+
916
import { HeaderIconButton } from '../components/HeaderIconButton';
1017
import { useMainHeaderTopicActions } from './useMainHeaderTopicActions';
1118

@@ -22,38 +29,6 @@ export function MainHeader() {
2229
const goBack = useCallback(() => {
2330
router.back();
2431
}, [router]);
25-
const menuActions = useMemo<MenuAction[]>(
26-
() => [
27-
{
28-
id: 'rename-topic',
29-
image: 'pencil',
30-
title: t('topic.actions.rename'),
31-
},
32-
{
33-
attributes: { disabled: topicActions.isPinActionDisabled },
34-
id: 'toggle-pin-topic',
35-
image: topicActions.isTopicPinned ? 'pin.slash' : 'pin',
36-
state: topicActions.isTopicPinned ? 'on' : 'off',
37-
title: t(topicActions.isTopicPinned ? 'topic.actions.unpin' : 'topic.actions.pin'),
38-
},
39-
],
40-
[t, topicActions.isPinActionDisabled, topicActions.isTopicPinned],
41-
);
42-
const handleMenuAction = useCallback(
43-
(event: NativeActionEvent) => {
44-
const actionId = event.nativeEvent.event;
45-
46-
if (actionId === 'rename-topic') {
47-
topicActions.openRenameTopic();
48-
return;
49-
}
50-
51-
if (actionId === 'toggle-pin-topic') {
52-
void topicActions.toggleTopicPin();
53-
}
54-
},
55-
[topicActions],
56-
);
5732

5833
return (
5934
<>
@@ -72,15 +47,46 @@ export function MainHeader() {
7247
<SquarePenIcon className="size-6 text-foreground" strokeWidth={2} />
7348
</HeaderIconButton>
7449
{topicActions.isTopicActionsVisible ? (
75-
<MenuView actions={menuActions} onPressAction={handleMenuAction}>
76-
<View
77-
accessibilityLabel={t('topic.actions.more')}
78-
accessibilityRole="button"
79-
className="size-9 items-center justify-center"
80-
>
81-
<EllipsisIcon className="size-6 text-foreground" strokeWidth={2} />
82-
</View>
83-
</MenuView>
50+
<Menu presentation="popover">
51+
<Menu.Trigger asChild>
52+
<Pressable
53+
accessibilityLabel={t('topic.actions.more')}
54+
accessibilityRole="button"
55+
className="size-9 items-center justify-center active:opacity-60"
56+
hitSlop={8}
57+
testID="topic-actions-menu"
58+
>
59+
<EllipsisIcon className="size-6 text-foreground" strokeWidth={2} />
60+
</Pressable>
61+
</Menu.Trigger>
62+
<Menu.Portal>
63+
<Menu.Overlay />
64+
<Menu.Content align="end" placement="bottom" presentation="popover" width={210}>
65+
<Menu.Item
66+
className="flex-row items-center gap-3"
67+
onPress={topicActions.openRenameTopic}
68+
testID="topic-actions-rename"
69+
>
70+
<PencilIcon className="size-5 text-foreground" strokeWidth={2} />
71+
<Menu.ItemTitle>{t('topic.actions.rename')}</Menu.ItemTitle>
72+
</Menu.Item>
73+
<Menu.Item
74+
className="flex-row items-center gap-3"
75+
isDisabled={topicActions.isPinActionDisabled}
76+
isSelected={topicActions.isTopicPinned}
77+
onPress={() => void topicActions.toggleTopicPin()}
78+
testID="topic-actions-toggle-pin"
79+
>
80+
<PinIcon className="size-5 text-foreground" strokeWidth={2} />
81+
<Menu.ItemTitle>
82+
{t(
83+
topicActions.isTopicPinned ? 'topic.actions.unpin' : 'topic.actions.pin',
84+
)}
85+
</Menu.ItemTitle>
86+
</Menu.Item>
87+
</Menu.Content>
88+
</Menu.Portal>
89+
</Menu>
8490
) : null}
8591
</View>
8692
</View>

src/screens/ChatScreen/input/components/ChatInputReasoningSection.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export function ChatInputReasoningSection({
7070
<View className="flex-row items-center gap-1.5">
7171
<Text className="font-medium text-base text-foreground">{t('chat.reasoning.title')}</Text>
7272
<SlotText
73+
ellipsizeMode="clip"
7374
text={currentOption ? t(currentOption.labelKey) : ''}
7475
textClassName="font-semibold text-base text-foreground"
7576
textStyle={

0 commit comments

Comments
 (0)