Skip to content

Commit 028eed3

Browse files
committed
AttachmentSources: adopt RichMenu
1 parent 1ef8806 commit 028eed3

1 file changed

Lines changed: 18 additions & 181 deletions

File tree

src/common/attachment-drafts/attachment-sources/AttachmentSources.tsx

Lines changed: 18 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import * as React from 'react';
2-
import { keyframes } from '@emotion/react';
32
import type { FileWithHandle } from 'browser-fs-access';
43

54
import type { SxProps } from '@mui/joy/styles/types';
6-
import { Box, Button, Checkbox, ColorPaletteProp, Dropdown, IconButton, ListDivider, ListItem, ListItemDecorator, Menu, MenuButton, MenuItem } from '@mui/joy';
5+
import { Box, Button, Checkbox, ColorPaletteProp, Dropdown, IconButton, ListDivider, ListItem, ListItemDecorator, MenuButton } from '@mui/joy';
76
import AddRoundedIcon from '@mui/icons-material/AddRounded';
87
import AddToDriveRoundedIcon from '@mui/icons-material/AddToDriveRounded';
98
import AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';
@@ -16,10 +15,10 @@ import ScreenshotMonitorIcon from '@mui/icons-material/ScreenshotMonitor';
1615
import { useBrowseStore } from '~/modules/browse/store-module-browsing';
1716

1817
import { ButtonAttachFilesMemo, openFileForAttaching } from '~/common/components/ButtonAttachFiles';
18+
import { RichMenu, RichMenuButton, RichMenuItem, richMenuItemSx } from '~/common/components/RichMenu';
1919
import { TooltipOutlined } from '~/common/components/TooltipOutlined';
2020
import { supportsClipboardRead } from '~/common/util/clipboardUtils';
2121
import { takeScreenCapture } from '~/common/util/screenCaptureUtils';
22-
import { themeZIndexOverMobileDrawer } from '~/common/app.theme';
2322

2423
import { ButtonAttachCameraMemo } from './ButtonAttachCamera';
2524
import { ButtonAttachClipboardMemo } from './ButtonAttachClipboard';
@@ -29,43 +28,7 @@ import { ButtonAttachWebMemo } from './ButtonAttachWeb';
2928
import { hasGoogleDriveCapability } from './useGoogleDrivePicker';
3029

3130

32-
// configuration
33-
export const ATTACH_BUTTON_RADIUS = '18px'; // for the rich (non-compact) menu button
34-
35-
36-
// animations for the rich (non-compact) menu
37-
const animationMenu = keyframes` from {opacity: 0;} to {opacity: 1;}`;
38-
const animationMenuItem = keyframes` from {opacity: 0;transform: translateY(-6px);} to {opacity: 1;transform: translateY(0);}`;
39-
4031
const _style = {
41-
menuItem: {
42-
// pl: 3,
43-
// pr: 2,
44-
py: 0.5, // was 1
45-
minHeight: 60,
46-
// minHeight: '3.25rem', // now 52, was 60
47-
},
48-
menuItemContent: {
49-
display: 'flex',
50-
flexDirection: 'column',
51-
gap: 0.125,
52-
},
53-
menuItemContentDisabled: {
54-
display: 'flex',
55-
flexDirection: 'column',
56-
gap: 0.125,
57-
opacity: 0.5,
58-
},
59-
menuItemName: {
60-
typography: 'title-sm',
61-
fontWeight: 600,
62-
// fontSize: '15px',
63-
},
64-
menuItemDescription: {
65-
fontSize: 'xs',
66-
color: 'text.tertiary',
67-
// fontWeight: 400,
68-
},
6932
liveFeedButton: {
7033
ml: 1,
7134
// outline: '1px solid transparent',
@@ -98,50 +61,8 @@ function LiveFeedButton(props: { isActive: boolean, tooltip: string, onClick: ()
9861
}
9962

10063

101-
// Rich menu item (used in menu-rich mode)
102-
function RichMenuItem(props: {
103-
name: React.ReactNode;
104-
description: React.ReactNode;
105-
Icon: React.ComponentType;
106-
onClick: () => void;
107-
delay?: number;
108-
disabled?: boolean;
109-
color?: ColorPaletteProp;
110-
endAction?: React.ReactNode;
111-
}) {
112-
return (
113-
<MenuItem
114-
onClick={props.onClick}
115-
disabled={props.disabled}
116-
color={props.color}
117-
sx={!props.delay ? _style.menuItem : {
118-
..._style.menuItem,
119-
animation: `${animationMenuItem} 0.12s cubic-bezier(0.25, 0.46, 0.45, 0.94) ${props.delay}s both`,
120-
}}
121-
>
122-
<ListItemDecorator>
123-
<props.Icon />
124-
</ListItemDecorator>
125-
<Box sx={props.disabled ? _style.menuItemContentDisabled : _style.menuItemContent}>
126-
<Box sx={_style.menuItemName}>
127-
{props.name}
128-
</Box>
129-
<Box sx={_style.menuItemDescription}>
130-
{props.description}
131-
</Box>
132-
</Box>
133-
{props.endAction && (
134-
<Box sx={{ ml: 'auto', display: 'flex', alignItems: 'center' }}>
135-
{props.endAction}
136-
</Box>
137-
)}
138-
</MenuItem>
139-
);
140-
}
141-
142-
14364
// Auto-download toggle (shown when browsing capability exists)
144-
function AutoDownloadToggle(props: { delay?: number }) {
65+
function AutoDownloadToggle() {
14566

14667
// external state
14768
const enableComposerAttach = useBrowseStore(s => s.enableComposerAttach);
@@ -155,17 +76,7 @@ function AutoDownloadToggle(props: { delay?: number }) {
15576

15677
<ListDivider inset='gutter' sx={{ my: 1 }} />
15778

158-
<ListItem
159-
sx={{
160-
..._style.menuItem,
161-
animation: `${animationMenuItem} 0.12s cubic-bezier(0.25, 0.46, 0.45, 0.94) ${props.delay}s both`,
162-
}}
163-
// onClick={(event) => {
164-
// event.preventDefault();
165-
// event.stopPropagation();
166-
// setEnableComposerAttach(!enableComposerAttach);
167-
// }}
168-
>
79+
<ListItem sx={richMenuItemSx.item}>
16980
<ListItemDecorator>
17081
<Checkbox
17182
size='sm'
@@ -176,11 +87,11 @@ function AutoDownloadToggle(props: { delay?: number }) {
17687
sx={{ ml: 0.375 }}
17788
/>
17889
</ListItemDecorator>
179-
<Box sx={_style.menuItemContent}>
90+
<Box sx={richMenuItemSx.content}>
18091
<Box sx={{ typography: 'title-sm' }}>
18192
Attach pasted URLs
18293
</Box>
183-
<Box sx={_style.menuItemDescription}>
94+
<Box sx={richMenuItemSx.description}>
18495
Download and attach pasted web links
18596
</Box>
18697
</Box>
@@ -293,9 +204,7 @@ function AttachmentSources(props: {
293204

294205
<Dropdown>
295206
{props.menuButton ? props.menuButton : !isMessage ? (
296-
<MenuButton slots={{ root: IconButton }}>
297-
<AddRoundedIcon />
298-
</MenuButton>
207+
<RichMenuButton icon={<AddRoundedIcon />} />
299208
) : (
300209
<MenuButton slots={{ root: Button }} slotProps={{
301210
root: {
@@ -309,48 +218,28 @@ function AttachmentSources(props: {
309218
Attach
310219
</MenuButton>
311220
)}
312-
<Menu sx={{ '--List-padding': '0.5rem', zIndex: themeZIndexOverMobileDrawer /* menu-compact or menu-message: above dialogs */ }}>
221+
<RichMenu compact /* menu-compact or menu-message: above dialogs (default zIndex) */>
313222

314223
{/* Files */}
315-
{/*<MenuItem onClick={handleAttachFilePicker}>*/}
316-
{/* <ListItemDecorator><AttachFileRoundedIcon /></ListItemDecorator>*/}
317-
{/* {props.onlyImages ? 'Images' : 'File'}*/}
318-
{/*</MenuItem>*/}
319224
<RichMenuItem name={props.onlyImages ? 'Images' : 'Files'} description='PDF, DOCX, images, code' color={props.color} Icon={AttachFileRoundedIcon} onClick={handleAttachFilePicker} />
320225

321226
{/* Web */}
322227
{!props.onlyImages && /*props.canBrowse &&*/ (
323-
// <MenuItem onClick={props.onOpenWebInput} disabled={!props.canBrowse}>
324-
// <ListItemDecorator><LanguageRoundedIcon /></ListItemDecorator>
325-
// Web
326-
// </MenuItem>
327228
<RichMenuItem name='Web' description='Import from web pages' color={props.color} Icon={LanguageRoundedIcon} onClick={props.onOpenWebInput} disabled={!props.canBrowse} />
328229
)}
329230

330231
{/* Google Drive */}
331232
{!props.onlyImages && hasGoogleDriveCapability && !!props.onOpenGoogleDrivePicker && (
332-
// <MenuItem onClick={props.onOpenGoogleDrivePicker}>
333-
// <ListItemDecorator><AddToDriveRoundedIcon /></ListItemDecorator>
334-
// Drive
335-
// </MenuItem>
336233
<RichMenuItem name='Drive' description='Attach Google Drive files' color={props.color} Icon={AddToDriveRoundedIcon} onClick={props.onOpenGoogleDrivePicker} />
337234
)}
338235

339236
{/* Clipboard */}
340237
{!props.onlyImages && supportsClipboardRead() && (
341-
// <MenuItem onClick={props.onAttachClipboard}>
342-
// <ListItemDecorator><ContentPasteGoIcon /></ListItemDecorator>
343-
// Paste
344-
// </MenuItem>
345238
<RichMenuItem name='Clipboard' description='Auto-convert to the best format' color={props.color} Icon={ContentPasteGoIcon} onClick={props.onAttachClipboard} />
346239
)}
347240

348241
{/* Screen Capture */}
349242
{props.hasScreenCapture && (
350-
// <MenuItem onClick={handleTakeScreenCapture} disabled={capturingScreen}>
351-
// <ListItemDecorator><ScreenshotMonitorIcon /></ListItemDecorator>
352-
// Screen
353-
// </MenuItem>
354243
<RichMenuItem
355244
name='Screen'
356245
color={screenCaptureError ? 'danger' : props.color}
@@ -364,10 +253,6 @@ function AttachmentSources(props: {
364253

365254
{/* Camera */}
366255
{props.hasCamera && isMessage && (
367-
// <MenuItem onClick={props.onOpenCamera}>
368-
// <ListItemDecorator><CameraAltOutlinedIcon /></ListItemDecorator>
369-
// Camera
370-
// </MenuItem>
371256
<RichMenuItem
372257
name='Camera'
373258
color={props.color}
@@ -378,7 +263,7 @@ function AttachmentSources(props: {
378263
/>
379264
)}
380265

381-
</Menu>
266+
</RichMenu>
382267
</Dropdown>
383268

384269
{/* [mobile] Responsive Camera OCR button */}
@@ -391,65 +276,22 @@ function AttachmentSources(props: {
391276
// menu-rich mode (desktop) - labeled button trigger with animated, descriptive menu items
392277
return (
393278
<Dropdown>
394-
<MenuButton
395-
slots={{ root: Button }}
396-
slotProps={{
397-
root: {
398-
// size: 'sm',
399-
variant: 'plain',
400-
color: props.color,
401-
startDecorator: <AddRoundedIcon />,
402-
fullWidth: true, // to match other buttons in the col
403-
sx: {
404-
minWidth: 100,
405-
justifyContent: 'flex-start',
406-
borderRadius: ATTACH_BUTTON_RADIUS,
407-
textWrap: 'nowrap',
408-
...(props.richButtonStandOut && {
409-
backgroundColor: 'background.popup',
410-
border: '1px solid',
411-
borderColor: `${props.color || 'neutral'}.outlinedBorder`,
412-
}),
413-
// when aria-expanded is true (menu open), remove top border radius
414-
'&[aria-expanded="true"]': {
415-
borderTopRightRadius: 0,
416-
borderTopLeftRadius: 0,
417-
backgroundColor: `${props.color || 'neutral'}.softHoverBg`,
418-
},
419-
},
420-
},
421-
}}
422-
>
423-
Attach
424-
</MenuButton>
425279

426-
<Menu
427-
// variant='soft'
280+
<RichMenuButton
281+
icon={<AddRoundedIcon />}
282+
label='Attach'
428283
color={props.color}
429-
placement='top-start'
430-
popperOptions={{ modifiers: [{ name: 'offset', options: { offset: [-10 /* 62 */, -2] } }] }}
431-
sx={{
432-
minWidth: 280,
433-
'--List-padding': '0.5rem',
434-
zIndex: themeZIndexOverMobileDrawer,
435-
animation: `${animationMenu} 0.12s cubic-bezier(0.25, 0.46, 0.45, 0.94)`,
436-
// boxShadow: '0 16px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
437-
boxShadow: 'md',
438-
borderRadius: ATTACH_BUTTON_RADIUS,
439-
border: '1px solid',
440-
borderColor: `${props.color || 'neutral'}.outlinedBorder`,
441-
backgroundColor: 'background.popup',
442-
overflow: 'hidden',
443-
}}
444-
>
284+
standOut={props.richButtonStandOut}
285+
/>
286+
287+
<RichMenu stagger color={props.color}>
445288

446289
{/* File Attachment */}
447290
<RichMenuItem
448291
name={props.onlyImages ? 'Images' : 'Files'}
449292
Icon={AttachFileRoundedIcon}
450293
description={props.onlyImages ? 'PNG, JPG, WEBP images to edit' : 'PDF, DOCX, images, code'}
451294
onClick={handleAttachFilePicker}
452-
delay={0}
453295
/>
454296

455297
{/* Web/URL Attachment */}
@@ -460,7 +302,6 @@ function AttachmentSources(props: {
460302
description='Import web pages, including screenshots'
461303
onClick={props.onOpenWebInput}
462304
disabled={!props.canBrowse}
463-
delay={0.02}
464305
/>
465306
)}
466307

@@ -471,7 +312,6 @@ function AttachmentSources(props: {
471312
Icon={AddToDriveRoundedIcon}
472313
description='Attach Google Drive files'
473314
onClick={props.onOpenGoogleDrivePicker}
474-
delay={0.04}
475315
/>
476316
)}
477317

@@ -483,7 +323,6 @@ function AttachmentSources(props: {
483323
// description='Auto-converts images and text to the best format'
484324
description='Auto-adapts images and text'
485325
onClick={props.onAttachClipboard}
486-
delay={0.06}
487326
/>
488327
)}
489328

@@ -523,7 +362,6 @@ function AttachmentSources(props: {
523362
onClick={handleTakeScreenCapture}
524363
disabled={capturingScreen}
525364
color={screenCaptureError ? 'danger' : undefined}
526-
delay={0.08}
527365
endAction={props.onStartLiveScreenFeed && <LiveFeedButton isActive={!!props.hasActiveScreenFeed} tooltip='Live Screen chat' onClick={props.onStartLiveScreenFeed} />}
528366
/>
529367
)}
@@ -535,17 +373,16 @@ function AttachmentSources(props: {
535373
Icon={CameraAltOutlinedIcon}
536374
description='Capture photos with optional OCR'
537375
onClick={props.onOpenCamera}
538-
delay={0.1}
539376
endAction={props.onStartLiveCameraFeed && <LiveFeedButton isActive={!!props.hasActiveCameraFeed} tooltip='Live Camera chat' onClick={props.onStartLiveCameraFeed} />}
540377
/>
541378
)}
542379

543380
{/* URL Auto-Download Toggle - only show when browse capability exists */}
544381
{!props.onlyImages && props.canBrowse && (
545-
<AutoDownloadToggle delay={0.12} />
382+
<AutoDownloadToggle />
546383
)}
547384

548-
</Menu>
385+
</RichMenu>
549386
</Dropdown>
550387
);
551388
}

0 commit comments

Comments
 (0)