fix(app): align compact menu sheet content
Route composer attachments through the shared compact menu sheet so headers and rows use the same alignment rails. Base the final-row padding on the device safe area to keep actions clear of system navigation.
This commit is contained in:
@@ -4,6 +4,7 @@ import { expectComposerVisible } from "../support/helpers/composer";
|
|||||||
import { expectAgentIdle } from "../support/helpers/agent-stream";
|
import { expectAgentIdle } from "../support/helpers/agent-stream";
|
||||||
import {
|
import {
|
||||||
openAttachmentMenu,
|
openAttachmentMenu,
|
||||||
|
expectAttachmentSheetRowsOnTitleRail,
|
||||||
openGithubPickerFromMenu,
|
openGithubPickerFromMenu,
|
||||||
attachImageFromMenu,
|
attachImageFromMenu,
|
||||||
expectAttachmentPill,
|
expectAttachmentPill,
|
||||||
@@ -63,6 +64,22 @@ test.describe("Composer attachments", () => {
|
|||||||
await expect(page.getByTestId("message-input-attachment-menu-item-github")).toBeVisible();
|
await expect(page.getByTestId("message-input-attachment-menu-item-github")).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("compact Plus menu aligns attachment rows with its sheet title", async ({
|
||||||
|
page,
|
||||||
|
withWorkspace,
|
||||||
|
}) => {
|
||||||
|
test.setTimeout(60_000);
|
||||||
|
const workspace = await withWorkspace({ prefix: "attach-sheet-rails-" });
|
||||||
|
await workspace.navigateTo();
|
||||||
|
await clickNewChat(page);
|
||||||
|
await expectComposerVisible(page);
|
||||||
|
await page.setViewportSize({ width: 390, height: 844 });
|
||||||
|
|
||||||
|
await openAttachmentMenu(page);
|
||||||
|
|
||||||
|
await expectAttachmentSheetRowsOnTitleRail(page);
|
||||||
|
});
|
||||||
|
|
||||||
test("GitHub combobox does not render until the picker is opened", async ({
|
test("GitHub combobox does not render until the picker is opened", async ({
|
||||||
page,
|
page,
|
||||||
withWorkspace,
|
withWorkspace,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
connectNewWorkspaceDaemonClient,
|
connectNewWorkspaceDaemonClient,
|
||||||
expectNewWorkspaceControlsEnabled,
|
expectNewWorkspaceControlsEnabled,
|
||||||
expectNewWorkspaceProjectSelected,
|
expectNewWorkspaceProjectSelected,
|
||||||
|
expectNewWorkspaceTriggerLabelsAligned,
|
||||||
openGlobalNewWorkspaceComposer,
|
openGlobalNewWorkspaceComposer,
|
||||||
openMissingProjectNewWorkspaceComposer,
|
openMissingProjectNewWorkspaceComposer,
|
||||||
openNewWorkspaceComposer,
|
openNewWorkspaceComposer,
|
||||||
@@ -77,6 +78,10 @@ test.describe("New workspace entry points", () => {
|
|||||||
timeout: 30_000,
|
timeout: 30_000,
|
||||||
});
|
});
|
||||||
await expect(page.getByTestId("host-picker-trigger")).toBeVisible({ timeout: 30_000 });
|
await expect(page.getByTestId("host-picker-trigger")).toBeVisible({ timeout: 30_000 });
|
||||||
|
await expectNewWorkspaceTriggerLabelsAligned(page, {
|
||||||
|
projectLabel: seeded.projectDisplayName,
|
||||||
|
hostLabel: "localhost",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
await seeded.cleanup();
|
await seeded.cleanup();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { connectWorkspaceSetupClient } from "./workspace-setup";
|
|||||||
import { selectWorkspaceInSidebar } from "./sidebar";
|
import { selectWorkspaceInSidebar } from "./sidebar";
|
||||||
import { getServerId } from "./server-id";
|
import { getServerId } from "./server-id";
|
||||||
import { waitForTabBar } from "./launcher";
|
import { waitForTabBar } from "./launcher";
|
||||||
|
import { waitForSettledPosition } from "./sheet-layout";
|
||||||
|
|
||||||
function composerInput(page: Page) {
|
function composerInput(page: Page) {
|
||||||
return page.getByRole("textbox", { name: "Message agent..." }).first();
|
return page.getByRole("textbox", { name: "Message agent..." }).first();
|
||||||
@@ -71,7 +72,28 @@ export async function pressInterruptShortcut(page: Page): Promise<void> {
|
|||||||
|
|
||||||
export async function openAttachmentMenu(page: Page): Promise<void> {
|
export async function openAttachmentMenu(page: Page): Promise<void> {
|
||||||
await page.getByTestId("message-input-attach-button").filter({ visible: true }).first().click();
|
await page.getByTestId("message-input-attach-button").filter({ visible: true }).first().click();
|
||||||
await expect(page.getByTestId("message-input-attachment-menu")).toBeVisible({ timeout: 5_000 });
|
await expect(
|
||||||
|
page
|
||||||
|
.getByTestId("message-input-attachment-menu")
|
||||||
|
.or(page.getByTestId("message-input-attachment-menu-content")),
|
||||||
|
).toBeVisible({ timeout: 5_000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function expectAttachmentSheetRowsOnTitleRail(page: Page): Promise<void> {
|
||||||
|
const title = page.getByText("Add attachment", { exact: true });
|
||||||
|
const firstItemGlyph = page
|
||||||
|
.getByRole("button", { name: "Add image", exact: true })
|
||||||
|
.locator("svg")
|
||||||
|
.first();
|
||||||
|
await waitForSettledPosition(title);
|
||||||
|
const [titleBox, glyphBox] = await Promise.all([
|
||||||
|
title.boundingBox(),
|
||||||
|
firstItemGlyph.boundingBox(),
|
||||||
|
]);
|
||||||
|
if (!titleBox || !glyphBox) {
|
||||||
|
throw new Error("Attachment sheet geometry could not be measured");
|
||||||
|
}
|
||||||
|
expect(Math.abs(glyphBox.x - titleBox.x)).toBeLessThanOrEqual(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function expectAttachButtonDisabled(page: Page): Promise<void> {
|
export async function expectAttachButtonDisabled(page: Page): Promise<void> {
|
||||||
|
|||||||
@@ -235,6 +235,27 @@ export async function expectNewWorkspaceProjectSelected(
|
|||||||
await expect(projectPicker).toContainText(projectDisplayName);
|
await expect(projectPicker).toContainText(projectDisplayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function expectNewWorkspaceTriggerLabelsAligned(
|
||||||
|
page: Page,
|
||||||
|
input: { projectLabel: string; hostLabel: string },
|
||||||
|
): Promise<void> {
|
||||||
|
const projectTrigger = page.getByRole("button", { name: "Workspace project" });
|
||||||
|
const hostTrigger = page.getByRole("button", { name: "Host", exact: true });
|
||||||
|
const [projectTriggerBox, projectLabelBox, hostTriggerBox, hostLabelBox] = await Promise.all([
|
||||||
|
projectTrigger.boundingBox(),
|
||||||
|
projectTrigger.getByText(input.projectLabel, { exact: true }).boundingBox(),
|
||||||
|
hostTrigger.boundingBox(),
|
||||||
|
hostTrigger.getByText(input.hostLabel, { exact: true }).boundingBox(),
|
||||||
|
]);
|
||||||
|
if (!projectTriggerBox || !projectLabelBox || !hostTriggerBox || !hostLabelBox) {
|
||||||
|
throw new Error("New Workspace trigger geometry could not be measured");
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectLabelInset = projectLabelBox.x - projectTriggerBox.x;
|
||||||
|
const hostLabelInset = hostLabelBox.x - hostTriggerBox.x;
|
||||||
|
expect(hostLabelInset).toBeCloseTo(projectLabelInset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
export async function fillNewWorkspaceDraft(page: Page, draft: string): Promise<void> {
|
export async function fillNewWorkspaceDraft(page: Page, draft: string): Promise<void> {
|
||||||
const composer = page.getByRole("textbox", { name: "Message agent..." });
|
const composer = page.getByRole("textbox", { name: "Message agent..." });
|
||||||
await expect(composer).toBeVisible({ timeout: 30_000 });
|
await expect(composer).toBeVisible({ timeout: 30_000 });
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { getMenuSheetBottomPadding } from "./menu-sheet-layout";
|
||||||
|
|
||||||
|
describe("getMenuSheetBottomPadding", () => {
|
||||||
|
it("keeps breathing room below the last row when there is no system inset", () => {
|
||||||
|
expect(
|
||||||
|
getMenuSheetBottomPadding({
|
||||||
|
safeAreaBottom: 0,
|
||||||
|
breathingRoom: 4,
|
||||||
|
}),
|
||||||
|
).toBe(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds breathing room after the system navigation inset", () => {
|
||||||
|
expect(
|
||||||
|
getMenuSheetBottomPadding({
|
||||||
|
safeAreaBottom: 48,
|
||||||
|
breathingRoom: 4,
|
||||||
|
}),
|
||||||
|
).toBe(52);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export function getMenuSheetBottomPadding(input: {
|
||||||
|
safeAreaBottom: number;
|
||||||
|
breathingRoom: number;
|
||||||
|
}): number {
|
||||||
|
return input.safeAreaBottom + input.breathingRoom;
|
||||||
|
}
|
||||||
@@ -14,16 +14,18 @@ import { Pressable, Text, View } from "react-native";
|
|||||||
import { StyleSheet, withUnistyles } from "react-native-unistyles";
|
import { StyleSheet, withUnistyles } from "react-native-unistyles";
|
||||||
import { BottomSheetBackdrop, BottomSheetScrollView } from "@gorhom/bottom-sheet";
|
import { BottomSheetBackdrop, BottomSheetScrollView } from "@gorhom/bottom-sheet";
|
||||||
import { ChevronLeft } from "lucide-react-native";
|
import { ChevronLeft } from "lucide-react-native";
|
||||||
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import {
|
import {
|
||||||
IsolatedBottomSheetModal,
|
IsolatedBottomSheetModal,
|
||||||
useIsolatedBottomSheetVisibility,
|
useIsolatedBottomSheetVisibility,
|
||||||
type ContextBridge,
|
type ContextBridge,
|
||||||
} from "@/components/ui/isolated-bottom-sheet-modal";
|
} from "@/components/ui/isolated-bottom-sheet-modal";
|
||||||
import type { Theme } from "@/styles/theme";
|
import { SPACING, type Theme } from "@/styles/theme";
|
||||||
import { useMenuContext, MenuContextProvider } from "./menu-context";
|
import { useMenuContext, MenuContextProvider } from "./menu-context";
|
||||||
import { MenuPage } from "./menu-item";
|
import { MenuPage } from "./menu-item";
|
||||||
import { currentPageId, isSubPageOpen } from "./menu-navigation";
|
import { currentPageId, isSubPageOpen } from "./menu-navigation";
|
||||||
import { AnchoredSurface, MenuOverlay } from "./menu-overlay";
|
import { AnchoredSurface, MenuOverlay } from "./menu-overlay";
|
||||||
|
import { getMenuSheetBottomPadding } from "./menu-sheet-layout";
|
||||||
import type { Alignment, Placement } from "./menu-anchor";
|
import type { Alignment, Placement } from "./menu-anchor";
|
||||||
|
|
||||||
const ThemedChevronLeft = withUnistyles(ChevronLeft);
|
const ThemedChevronLeft = withUnistyles(ChevronLeft);
|
||||||
@@ -305,6 +307,17 @@ function MenuSheetSurface({
|
|||||||
}: MenuSurfaceProps): ReactElement | null {
|
}: MenuSurfaceProps): ReactElement | null {
|
||||||
const menu = useMenuContext("MenuSurface");
|
const menu = useMenuContext("MenuSurface");
|
||||||
const { value: surfaceValue } = useSubAnchors();
|
const { value: surfaceValue } = useSubAnchors();
|
||||||
|
const safeAreaInsets = useSafeAreaInsets();
|
||||||
|
|
||||||
|
const sheetScrollContentStyle = useMemo(
|
||||||
|
() => ({
|
||||||
|
paddingBottom: getMenuSheetBottomPadding({
|
||||||
|
safeAreaBottom: safeAreaInsets.bottom,
|
||||||
|
breathingRoom: SPACING[1],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
[safeAreaInsets.bottom],
|
||||||
|
);
|
||||||
|
|
||||||
const handleClose = useCallback(() => menu.setOpen(false), [menu]);
|
const handleClose = useCallback(() => menu.setOpen(false), [menu]);
|
||||||
const { sheetRef, handleSheetChange, handleSheetDismiss } = useIsolatedBottomSheetVisibility({
|
const { sheetRef, handleSheetChange, handleSheetDismiss } = useIsolatedBottomSheetVisibility({
|
||||||
@@ -356,7 +369,7 @@ function MenuSheetSurface({
|
|||||||
keyboardBlurBehavior="restore"
|
keyboardBlurBehavior="restore"
|
||||||
>
|
>
|
||||||
<BottomSheetScrollView
|
<BottomSheetScrollView
|
||||||
contentContainerStyle={styles.sheetScrollContent}
|
contentContainerStyle={sheetScrollContentStyle}
|
||||||
keyboardShouldPersistTaps="handled"
|
keyboardShouldPersistTaps="handled"
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
testID={testID ? `${testID}-content` : undefined}
|
testID={testID ? `${testID}-content` : undefined}
|
||||||
@@ -407,9 +420,6 @@ function MenuSheetHeader({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create((theme) => ({
|
const styles = StyleSheet.create((theme) => ({
|
||||||
sheetScrollContent: {
|
|
||||||
paddingBottom: theme.spacing[6],
|
|
||||||
},
|
|
||||||
sheetHeader: {
|
sheetHeader: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import {
|
|||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Pressable,
|
|
||||||
Platform,
|
|
||||||
useWindowDimensions,
|
useWindowDimensions,
|
||||||
NativeSyntheticEvent,
|
NativeSyntheticEvent,
|
||||||
TextInputContentSizeChangeEventData,
|
TextInputContentSizeChangeEventData,
|
||||||
@@ -48,8 +46,6 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { AdaptiveModalSheet, type SheetHeader } from "@/components/adaptive-modal-sheet";
|
|
||||||
import { useDismissKeyboardOnOpen } from "@/components/ui/keyboard-dismiss";
|
|
||||||
import { useShortcutKeys } from "@/hooks/use-shortcut-keys";
|
import { useShortcutKeys } from "@/hooks/use-shortcut-keys";
|
||||||
import { useIosHardwareKeyboardSubmit } from "@/hooks/use-ios-hardware-keyboard-submit";
|
import { useIosHardwareKeyboardSubmit } from "@/hooks/use-ios-hardware-keyboard-submit";
|
||||||
import { formatShortcut, type ShortcutKey } from "@/utils/format-shortcut";
|
import { formatShortcut, type ShortcutKey } from "@/utils/format-shortcut";
|
||||||
@@ -164,8 +160,6 @@ const MIN_INPUT_HEIGHT_DESKTOP = 46;
|
|||||||
const DEFAULT_MAX_INPUT_HEIGHT = 160;
|
const DEFAULT_MAX_INPUT_HEIGHT = 160;
|
||||||
const MAX_INPUT_VIEWPORT_RATIO = 0.5;
|
const MAX_INPUT_VIEWPORT_RATIO = 0.5;
|
||||||
const MIN_INPUT_HEIGHT = isWeb ? MIN_INPUT_HEIGHT_DESKTOP : MIN_INPUT_HEIGHT_MOBILE;
|
const MIN_INPUT_HEIGHT = isWeb ? MIN_INPUT_HEIGHT_DESKTOP : MIN_INPUT_HEIGHT_MOBILE;
|
||||||
const ATTACHMENT_SHEET_SNAP_POINTS = ["34%", "45%"];
|
|
||||||
|
|
||||||
type WebTextInputKeyPressEvent = NativeSyntheticEvent<
|
type WebTextInputKeyPressEvent = NativeSyntheticEvent<
|
||||||
TextInputKeyPressEventData & {
|
TextInputKeyPressEventData & {
|
||||||
metaKey?: boolean;
|
metaKey?: boolean;
|
||||||
@@ -225,55 +219,6 @@ function AttachmentMenuList({ items }: { items: AttachmentMenuItem[] }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AttachmentSheetItem({
|
|
||||||
item,
|
|
||||||
onSelect,
|
|
||||||
}: {
|
|
||||||
item: AttachmentMenuItem;
|
|
||||||
onSelect: (item: AttachmentMenuItem) => void;
|
|
||||||
}) {
|
|
||||||
const handlePress = useCallback(() => {
|
|
||||||
onSelect(item);
|
|
||||||
}, [item, onSelect]);
|
|
||||||
const pressableStyle = useCallback(
|
|
||||||
({ pressed }: { pressed: boolean }) => [
|
|
||||||
styles.attachmentSheetItem,
|
|
||||||
pressed && styles.attachmentSheetItemPressed,
|
|
||||||
item.disabled && styles.buttonDisabled,
|
|
||||||
],
|
|
||||||
[item.disabled],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Pressable
|
|
||||||
testID={`message-input-attachment-menu-item-${item.id}`}
|
|
||||||
accessibilityRole="button"
|
|
||||||
disabled={item.disabled}
|
|
||||||
onPress={handlePress}
|
|
||||||
style={pressableStyle}
|
|
||||||
>
|
|
||||||
{item.icon ? <View style={styles.attachmentSheetItemIcon}>{item.icon}</View> : null}
|
|
||||||
<Text style={styles.attachmentSheetItemText}>{item.label}</Text>
|
|
||||||
</Pressable>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function AttachmentSheetList({
|
|
||||||
items,
|
|
||||||
onSelect,
|
|
||||||
}: {
|
|
||||||
items: AttachmentMenuItem[];
|
|
||||||
onSelect: (item: AttachmentMenuItem) => void;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<View style={styles.attachmentSheetList}>
|
|
||||||
{items.map((item) => (
|
|
||||||
<AttachmentSheetItem key={item.id} item={item} onSelect={onSelect} />
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function AttachmentDropdown({
|
function AttachmentDropdown({
|
||||||
isConnected,
|
isConnected,
|
||||||
disabled,
|
disabled,
|
||||||
@@ -289,73 +234,9 @@ function AttachmentDropdown({
|
|||||||
attachmentMenuItems: AttachmentMenuItem[];
|
attachmentMenuItems: AttachmentMenuItem[];
|
||||||
addAttachmentLabel: string;
|
addAttachmentLabel: string;
|
||||||
}) {
|
}) {
|
||||||
const isCompact = useIsCompactFormFactor();
|
|
||||||
const [isSheetOpen, setIsSheetOpen] = useState(false);
|
|
||||||
useDismissKeyboardOnOpen(isSheetOpen, isCompact);
|
|
||||||
|
|
||||||
const isButtonDisabled = !isConnected || disabled;
|
const isButtonDisabled = !isConnected || disabled;
|
||||||
const attachmentSheetHeader = useMemo<SheetHeader>(
|
|
||||||
() => ({ title: addAttachmentLabel }),
|
|
||||||
[addAttachmentLabel],
|
|
||||||
);
|
|
||||||
const handleOpenSheet = useCallback(() => {
|
|
||||||
if (isButtonDisabled) return;
|
|
||||||
setIsSheetOpen(true);
|
|
||||||
}, [isButtonDisabled]);
|
|
||||||
const handleCloseSheet = useCallback(() => {
|
|
||||||
setIsSheetOpen(false);
|
|
||||||
}, []);
|
|
||||||
const handleSheetItemSelect = useCallback((item: AttachmentMenuItem) => {
|
|
||||||
if (item.disabled) return;
|
|
||||||
setIsSheetOpen(false);
|
|
||||||
if (Platform.OS === "ios") {
|
|
||||||
setTimeout(item.onSelect, 250);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
item.onSelect();
|
|
||||||
}, []);
|
|
||||||
const mobileAttachButtonStyle = useCallback(
|
|
||||||
(state: { pressed: boolean; hovered?: boolean }) => {
|
|
||||||
if (typeof attachButtonStyle === "function") {
|
|
||||||
return attachButtonStyle({ ...state, hovered: Boolean(state.hovered), open: isSheetOpen });
|
|
||||||
}
|
|
||||||
return attachButtonStyle;
|
|
||||||
},
|
|
||||||
[attachButtonStyle, isSheetOpen],
|
|
||||||
);
|
|
||||||
const renderMobileAttachButtonIcon = useCallback(
|
|
||||||
({ hovered }: { hovered?: boolean }) => renderAttachButtonIcon({ hovered }),
|
|
||||||
[renderAttachButtonIcon],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isCompact) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Pressable
|
|
||||||
disabled={isButtonDisabled}
|
|
||||||
accessibilityLabel={addAttachmentLabel}
|
|
||||||
accessibilityRole="button"
|
|
||||||
testID="message-input-attach-button"
|
|
||||||
onPress={handleOpenSheet}
|
|
||||||
style={mobileAttachButtonStyle}
|
|
||||||
>
|
|
||||||
{renderMobileAttachButtonIcon}
|
|
||||||
</Pressable>
|
|
||||||
<AdaptiveModalSheet
|
|
||||||
header={attachmentSheetHeader}
|
|
||||||
visible={isSheetOpen}
|
|
||||||
onClose={handleCloseSheet}
|
|
||||||
snapPoints={ATTACHMENT_SHEET_SNAP_POINTS}
|
|
||||||
testID="message-input-attachment-menu"
|
|
||||||
>
|
|
||||||
<AttachmentSheetList items={attachmentMenuItems} onSelect={handleSheetItemSelect} />
|
|
||||||
</AdaptiveModalSheet>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu compactMode="sheet">
|
||||||
<Tooltip delayDuration={0} enabledOnDesktop enabledOnMobile={false}>
|
<Tooltip delayDuration={0} enabledOnDesktop enabledOnMobile={false}>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<DropdownMenuTrigger
|
<DropdownMenuTrigger
|
||||||
@@ -378,6 +259,7 @@ function AttachmentDropdown({
|
|||||||
offset={8}
|
offset={8}
|
||||||
minWidth={220}
|
minWidth={220}
|
||||||
testID="message-input-attachment-menu"
|
testID="message-input-attachment-menu"
|
||||||
|
sheetTitle={addAttachmentLabel}
|
||||||
>
|
>
|
||||||
<AttachmentMenuList items={attachmentMenuItems} />
|
<AttachmentMenuList items={attachmentMenuItems} />
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
@@ -1941,31 +1823,6 @@ const styles = StyleSheet.create((theme: Theme) => ({
|
|||||||
buttonDisabled: {
|
buttonDisabled: {
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
},
|
},
|
||||||
attachmentSheetList: {
|
|
||||||
gap: theme.spacing[1],
|
|
||||||
},
|
|
||||||
attachmentSheetItem: {
|
|
||||||
minHeight: 44,
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: theme.spacing[3],
|
|
||||||
paddingHorizontal: theme.spacing[3],
|
|
||||||
paddingVertical: theme.spacing[2],
|
|
||||||
borderRadius: theme.borderRadius.xl,
|
|
||||||
},
|
|
||||||
attachmentSheetItemPressed: {
|
|
||||||
backgroundColor: theme.colors.surface2,
|
|
||||||
},
|
|
||||||
attachmentSheetItemIcon: {
|
|
||||||
width: 20,
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
},
|
|
||||||
attachmentSheetItemText: {
|
|
||||||
color: theme.colors.foreground,
|
|
||||||
fontSize: theme.fontSize.base,
|
|
||||||
fontWeight: theme.fontWeight.normal,
|
|
||||||
},
|
|
||||||
overlayContainer: {
|
overlayContainer: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
|||||||
@@ -1359,7 +1359,9 @@ function useNewWorkspaceFormStack(input: NewWorkspaceFormStackInput): ReactEleme
|
|||||||
style={badgePressableStyle}
|
style={badgePressableStyle}
|
||||||
testID="host-picker-trigger"
|
testID="host-picker-trigger"
|
||||||
>
|
>
|
||||||
<HostStatusDot serverId={host.selectedServerId} />
|
<View style={styles.badgeIconBox}>
|
||||||
|
<HostStatusDot serverId={host.selectedServerId} />
|
||||||
|
</View>
|
||||||
<Text style={styles.badgeText} numberOfLines={1}>
|
<Text style={styles.badgeText} numberOfLines={1}>
|
||||||
{selectedHostLabel}
|
{selectedHostLabel}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
Reference in New Issue
Block a user