refactor: menu items (#2057)

This commit is contained in:
Aman Harwara
2022-11-27 22:53:30 +05:30
committed by GitHub
parent 8145cdb8ac
commit 3c91b0cb17
24 changed files with 291 additions and 352 deletions

View File

@@ -9,7 +9,6 @@ import { AccountMenuPane } from './AccountMenuPane'
import Menu from '@/Components/Menu/Menu' import Menu from '@/Components/Menu/Menu'
import MenuItem from '@/Components/Menu/MenuItem' import MenuItem from '@/Components/Menu/MenuItem'
import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator' import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator'
import { MenuItemType } from '@/Components/Menu/MenuItemType'
import WorkspaceSwitcherOption from './WorkspaceSwitcher/WorkspaceSwitcherOption' import WorkspaceSwitcherOption from './WorkspaceSwitcher/WorkspaceSwitcherOption'
import { ApplicationGroup } from '@/Application/ApplicationGroup' import { ApplicationGroup } from '@/Application/ApplicationGroup'
import { formatLastSyncDate } from '@/Utils/DateUtils' import { formatLastSyncDate } from '@/Utils/DateUtils'
@@ -150,23 +149,23 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
/> />
<MenuItemSeparator /> <MenuItemSeparator />
{user ? ( {user ? (
<MenuItem type={MenuItemType.IconButton} onClick={openPreferences}> <MenuItem onClick={openPreferences}>
<Icon type="user" className={iconClassName} /> <Icon type="user" className={iconClassName} />
Account settings Account settings
</MenuItem> </MenuItem>
) : ( ) : (
<> <>
<MenuItem type={MenuItemType.IconButton} onClick={activateRegisterPane}> <MenuItem onClick={activateRegisterPane}>
<Icon type="user" className={iconClassName} /> <Icon type="user" className={iconClassName} />
Create free account Create free account
</MenuItem> </MenuItem>
<MenuItem type={MenuItemType.IconButton} onClick={activateSignInPane}> <MenuItem onClick={activateSignInPane}>
<Icon type="signIn" className={iconClassName} /> <Icon type="signIn" className={iconClassName} />
Sign in Sign in
</MenuItem> </MenuItem>
</> </>
)} )}
<MenuItem className="justify-between" type={MenuItemType.IconButton} onClick={openHelp}> <MenuItem className="justify-between" onClick={openHelp}>
<div className="flex items-center"> <div className="flex items-center">
<Icon type="help" className={iconClassName} /> <Icon type="help" className={iconClassName} />
Help &amp; feedback Help &amp; feedback
@@ -176,7 +175,7 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
{user ? ( {user ? (
<> <>
<MenuItemSeparator /> <MenuItemSeparator />
<MenuItem type={MenuItemType.IconButton} onClick={signOut}> <MenuItem onClick={signOut}>
<Icon type="signOut" className={iconClassName} /> <Icon type="signOut" className={iconClassName} />
Sign out workspace Sign out workspace
</MenuItem> </MenuItem>

View File

@@ -1,6 +1,4 @@
import Icon from '@/Components/Icon/Icon' import Icon from '@/Components/Icon/Icon'
import MenuItem from '@/Components/Menu/MenuItem'
import { MenuItemType } from '@/Components/Menu/MenuItemType'
import { KeyboardKey } from '@standardnotes/ui-services' import { KeyboardKey } from '@standardnotes/ui-services'
import { ApplicationDescriptor } from '@standardnotes/snjs' import { ApplicationDescriptor } from '@standardnotes/snjs'
import { import {
@@ -13,6 +11,7 @@ import {
useRef, useRef,
useState, useState,
} from 'react' } from 'react'
import MenuRadioButtonItem from '@/Components/Menu/MenuRadioButtonItem'
type Props = { type Props = {
descriptor: ApplicationDescriptor descriptor: ApplicationDescriptor
@@ -56,8 +55,7 @@ const WorkspaceMenuItem: FunctionComponent<Props> = ({
}, [inputValue, renameDescriptor]) }, [inputValue, renameDescriptor])
return ( return (
<MenuItem <MenuRadioButtonItem
type={MenuItemType.RadioButton}
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none" className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
onClick={onClick} onClick={onClick}
checked={descriptor.primary} checked={descriptor.primary}
@@ -100,7 +98,7 @@ const WorkspaceMenuItem: FunctionComponent<Props> = ({
</div> </div>
)} )}
</div> </div>
</MenuItem> </MenuRadioButtonItem>
) )
} }

View File

@@ -7,7 +7,6 @@ import Icon from '@/Components/Icon/Icon'
import Menu from '@/Components/Menu/Menu' import Menu from '@/Components/Menu/Menu'
import MenuItem from '@/Components/Menu/MenuItem' import MenuItem from '@/Components/Menu/MenuItem'
import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator' import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator'
import { MenuItemType } from '@/Components/Menu/MenuItemType'
import WorkspaceMenuItem from './WorkspaceMenuItem' import WorkspaceMenuItem from './WorkspaceMenuItem'
type Props = { type Props = {
@@ -85,13 +84,13 @@ const WorkspaceSwitcherMenu: FunctionComponent<Props> = ({
))} ))}
<MenuItemSeparator /> <MenuItemSeparator />
<MenuItem type={MenuItemType.IconButton} onClick={addAnotherWorkspace}> <MenuItem onClick={addAnotherWorkspace}>
<Icon type="user-add" className="mr-2 text-neutral" /> <Icon type="user-add" className="mr-2 text-neutral" />
Add another workspace Add another workspace
</MenuItem> </MenuItem>
{!hideWorkspaceOptions && ( {!hideWorkspaceOptions && (
<MenuItem type={MenuItemType.IconButton} onClick={signoutAll}> <MenuItem onClick={signoutAll}>
<Icon type="signOut" className="mr-2 text-neutral" /> <Icon type="signOut" className="mr-2 text-neutral" />
Sign out all workspaces Sign out all workspaces
</MenuItem> </MenuItem>

View File

@@ -6,7 +6,6 @@ import { FunctionComponent, useCallback, useRef, useState } from 'react'
import Icon from '@/Components/Icon/Icon' import Icon from '@/Components/Icon/Icon'
import WorkspaceSwitcherMenu from './WorkspaceSwitcherMenu' import WorkspaceSwitcherMenu from './WorkspaceSwitcherMenu'
import MenuItem from '@/Components/Menu/MenuItem' import MenuItem from '@/Components/Menu/MenuItem'
import { MenuItemType } from '@/Components/Menu/MenuItemType'
import Popover from '@/Components/Popover/Popover' import Popover from '@/Components/Popover/Popover'
import { MenuItemIconSize } from '@/Constants/TailwindClassNames' import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
@@ -25,13 +24,7 @@ const WorkspaceSwitcherOption: FunctionComponent<Props> = ({ mainApplicationGrou
return ( return (
<> <>
<MenuItem <MenuItem tabIndex={FOCUSABLE_BUT_NOT_TABBABLE} ref={buttonRef} onClick={toggleMenu} className="justify-between">
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
ref={buttonRef}
type={MenuItemType.IconButton}
onClick={toggleMenu}
className="justify-between"
>
<div className="flex items-center"> <div className="flex items-center">
<Icon type="user-switch" className={`mr-2 text-neutral ${MenuItemIconSize}`} /> <Icon type="user-switch" className={`mr-2 text-neutral ${MenuItemIconSize}`} />
Switch workspace Switch workspace

View File

@@ -1,7 +1,5 @@
import Icon from '@/Components/Icon/Icon' import Icon from '@/Components/Icon/Icon'
import Menu from '@/Components/Menu/Menu' import Menu from '@/Components/Menu/Menu'
import MenuItem from '@/Components/Menu/MenuItem'
import { MenuItemType } from '@/Components/Menu/MenuItemType'
import { usePremiumModal } from '@/Hooks/usePremiumModal' import { usePremiumModal } from '@/Hooks/usePremiumModal'
import { STRING_EDIT_LOCKED_ATTEMPT } from '@/Constants/Strings' import { STRING_EDIT_LOCKED_ATTEMPT } from '@/Constants/Strings'
import { WebApplication } from '@/Application/Application' import { WebApplication } from '@/Application/Application'
@@ -13,6 +11,7 @@ import { createEditorMenuGroups } from '../../Utils/createEditorMenuGroups'
import { reloadFont } from '../NoteView/FontFunctions' import { reloadFont } from '../NoteView/FontFunctions'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon' import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
import { SuperNoteImporter } from '../NoteView/SuperEditor/SuperNoteImporter' import { SuperNoteImporter } from '../NoteView/SuperEditor/SuperNoteImporter'
import MenuRadioButtonItem from '../Menu/MenuRadioButtonItem'
type ChangeEditorMenuProps = { type ChangeEditorMenuProps = {
application: WebApplication application: WebApplication
@@ -190,12 +189,11 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
selectItem(item).catch(console.error) selectItem(item).catch(console.error)
} }
return ( return (
<MenuItem <MenuRadioButtonItem
key={item.name} key={item.name}
type={MenuItemType.RadioButton}
onClick={onClickEditorItem} onClick={onClickEditorItem}
className={'flex-row-reverse py-2'} className={'flex-row-reverse py-2'}
checked={item.isEntitled ? isSelected(item) : undefined} checked={item.isEntitled ? isSelected(item) : false}
> >
<div className="flex flex-grow items-center justify-between"> <div className="flex flex-grow items-center justify-between">
<div className={`flex items-center ${group.featured ? 'font-bold' : ''}`}> <div className={`flex items-center ${group.featured ? 'font-bold' : ''}`}>
@@ -206,7 +204,7 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
<Icon type={PremiumFeatureIconName} className={PremiumFeatureIconClass} /> <Icon type={PremiumFeatureIconName} className={PremiumFeatureIconClass} />
)} )}
</div> </div>
</MenuItem> </MenuRadioButtonItem>
) )
})} })}
</div> </div>

View File

@@ -13,15 +13,15 @@ import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback, useEffect, useState } from 'react' import { FunctionComponent, useCallback, useEffect, useState } from 'react'
import Icon from '@/Components/Icon/Icon' import Icon from '@/Components/Icon/Icon'
import Menu from '@/Components/Menu/Menu' import Menu from '@/Components/Menu/Menu'
import MenuItem from '@/Components/Menu/MenuItem'
import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator' import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator'
import { MenuItemType } from '@/Components/Menu/MenuItemType'
import { DisplayOptionsMenuProps } from './DisplayOptionsMenuProps' import { DisplayOptionsMenuProps } from './DisplayOptionsMenuProps'
import { PrefDefaults } from '@/Constants/PrefDefaults' import { PrefDefaults } from '@/Constants/PrefDefaults'
import NewNotePreferences from './NewNotePreferences' import NewNotePreferences from './NewNotePreferences'
import { PreferenceMode } from './PreferenceMode' import { PreferenceMode } from './PreferenceMode'
import { classNames } from '@standardnotes/utils' import { classNames } from '@standardnotes/utils'
import NoSubscriptionBanner from '@/Components/NoSubscriptionBanner/NoSubscriptionBanner' import NoSubscriptionBanner from '@/Components/NoSubscriptionBanner/NoSubscriptionBanner'
import MenuRadioButtonItem from '@/Components/Menu/MenuRadioButtonItem'
import MenuSwitchButtonItem from '@/Components/Menu/MenuSwitchButtonItem'
const DailyEntryModeEnabled = true const DailyEntryModeEnabled = true
@@ -230,10 +230,9 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
<MenuItemSeparator /> <MenuItemSeparator />
<div className="my-1 px-3 text-base font-semibold uppercase text-text lg:text-xs">Sort by</div> <div className="my-1 px-3 text-base font-semibold uppercase text-text lg:text-xs">Sort by</div>
<MenuItem <MenuRadioButtonItem
disabled={controlsDisabled || isDailyEntry} disabled={controlsDisabled || isDailyEntry}
className="py-2" className="py-2"
type={MenuItemType.RadioButton}
onClick={toggleSortByDateModified} onClick={toggleSortByDateModified}
checked={preferences.sortBy === CollectionSort.UpdatedAt} checked={preferences.sortBy === CollectionSort.UpdatedAt}
> >
@@ -247,11 +246,10 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
) )
) : null} ) : null}
</div> </div>
</MenuItem> </MenuRadioButtonItem>
<MenuItem <MenuRadioButtonItem
disabled={controlsDisabled || isDailyEntry} disabled={controlsDisabled || isDailyEntry}
className="py-2" className="py-2"
type={MenuItemType.RadioButton}
onClick={toggleSortByCreationDate} onClick={toggleSortByCreationDate}
checked={preferences.sortBy === CollectionSort.CreatedAt} checked={preferences.sortBy === CollectionSort.CreatedAt}
> >
@@ -265,11 +263,10 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
) )
) : null} ) : null}
</div> </div>
</MenuItem> </MenuRadioButtonItem>
<MenuItem <MenuRadioButtonItem
disabled={controlsDisabled || isDailyEntry} disabled={controlsDisabled || isDailyEntry}
className="py-2" className="py-2"
type={MenuItemType.RadioButton}
onClick={toggleSortByTitle} onClick={toggleSortByTitle}
checked={preferences.sortBy === CollectionSort.Title} checked={preferences.sortBy === CollectionSort.Title}
> >
@@ -283,92 +280,83 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
) )
) : null} ) : null}
</div> </div>
</MenuItem> </MenuRadioButtonItem>
<MenuItemSeparator /> <MenuItemSeparator />
<div className="px-3 py-1 text-base font-semibold uppercase text-text lg:text-xs">View</div> <div className="px-3 py-1 text-base font-semibold uppercase text-text lg:text-xs">View</div>
{!isFilesSmartView && ( {!isFilesSmartView && (
<MenuItem <MenuSwitchButtonItem
disabled={controlsDisabled} disabled={controlsDisabled}
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop" className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={!preferences.hideNotePreview} checked={!preferences.hideNotePreview}
onChange={toggleHidePreview} onChange={toggleHidePreview}
> >
<div className="max-w-3/4 flex flex-col">Show note preview</div> <div className="max-w-3/4 flex flex-col">Show note preview</div>
</MenuItem> </MenuSwitchButtonItem>
)} )}
<MenuItem <MenuSwitchButtonItem
disabled={controlsDisabled} disabled={controlsDisabled}
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop" className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={!preferences.hideDate} checked={!preferences.hideDate}
onChange={toggleHideDate} onChange={toggleHideDate}
> >
Show date Show date
</MenuItem> </MenuSwitchButtonItem>
<MenuItem <MenuSwitchButtonItem
disabled={controlsDisabled} disabled={controlsDisabled}
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop" className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={!preferences.hideTags} checked={!preferences.hideTags}
onChange={toggleHideTags} onChange={toggleHideTags}
> >
Show tags Show tags
</MenuItem> </MenuSwitchButtonItem>
<MenuItem <MenuSwitchButtonItem
disabled={controlsDisabled} disabled={controlsDisabled}
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop" className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={!preferences.hideEditorIcon} checked={!preferences.hideEditorIcon}
onChange={toggleEditorIcon} onChange={toggleEditorIcon}
> >
Show icon Show icon
</MenuItem> </MenuSwitchButtonItem>
<MenuItemSeparator /> <MenuItemSeparator />
<div className="px-3 py-1 text-base font-semibold uppercase text-text lg:text-xs">Other</div> <div className="px-3 py-1 text-base font-semibold uppercase text-text lg:text-xs">Other</div>
<MenuItem <MenuSwitchButtonItem
disabled={controlsDisabled} disabled={controlsDisabled}
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop" className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={!preferences.hidePinned} checked={!preferences.hidePinned}
onChange={toggleHidePinned} onChange={toggleHidePinned}
> >
Show pinned Show pinned
</MenuItem> </MenuSwitchButtonItem>
<MenuItem <MenuSwitchButtonItem
disabled={controlsDisabled} disabled={controlsDisabled}
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop" className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={!preferences.hideProtected} checked={!preferences.hideProtected}
onChange={toggleHideProtected} onChange={toggleHideProtected}
> >
Show protected Show protected
</MenuItem> </MenuSwitchButtonItem>
<MenuItem <MenuSwitchButtonItem
disabled={controlsDisabled} disabled={controlsDisabled}
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop" className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={preferences.showArchived} checked={Boolean(preferences.showArchived)}
onChange={toggleShowArchived} onChange={toggleShowArchived}
> >
Show archived Show archived
</MenuItem> </MenuSwitchButtonItem>
<MenuItem <MenuSwitchButtonItem
disabled={controlsDisabled} disabled={controlsDisabled}
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop" className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={preferences.showTrashed} checked={Boolean(preferences.showTrashed)}
onChange={toggleShowTrashed} onChange={toggleShowTrashed}
> >
Show trashed Show trashed
</MenuItem> </MenuSwitchButtonItem>
{currentMode === 'tag' && DailyEntryModeEnabled && ( {currentMode === 'tag' && DailyEntryModeEnabled && (
<> <>
<MenuItemSeparator /> <MenuItemSeparator />
<MenuItem <MenuSwitchButtonItem
disabled={controlsDisabled} disabled={controlsDisabled}
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop" className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={isDailyEntry} checked={isDailyEntry}
onChange={toggleEntryMode} onChange={toggleEntryMode}
@@ -382,7 +370,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
</div> </div>
<div className="mt-1">Capture new notes daily with a calendar-based layout</div> <div className="mt-1">Capture new notes daily with a calendar-based layout</div>
</div> </div>
</MenuItem> </MenuSwitchButtonItem>
</> </>
)} )}

View File

@@ -9,8 +9,8 @@ import { formatSizeToReadableString } from '@standardnotes/filepicker'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider' import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata' import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import MenuItem from '../Menu/MenuItem' import MenuItem from '../Menu/MenuItem'
import { MenuItemType } from '../Menu/MenuItemType'
import { FileContextMenuBackupOption } from './FileContextMenuBackupOption' import { FileContextMenuBackupOption } from './FileContextMenuBackupOption'
import MenuSwitchButtonItem from '../Menu/MenuSwitchButtonItem'
type Props = { type Props = {
closeMenu: () => void closeMenu: () => void
@@ -83,8 +83,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
) : null} ) : null}
</> </>
)} )}
<MenuItem <MenuSwitchButtonItem
type={MenuItemType.SwitchButton}
checked={hasProtectedFiles} checked={hasProtectedFiles}
onChange={(hasProtectedFiles) => { onChange={(hasProtectedFiles) => {
void filesController.setProtectionForFiles(hasProtectedFiles, selectionController.selectedFiles) void filesController.setProtectionForFiles(hasProtectedFiles, selectionController.selectedFiles)
@@ -92,7 +91,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
> >
<Icon type="lock" className="mr-2 text-neutral" /> <Icon type="lock" className="mr-2 text-neutral" />
Password protect Password protect
</MenuItem> </MenuSwitchButtonItem>
<HorizontalSeparator classes="my-1" /> <HorizontalSeparator classes="my-1" />
<MenuItem <MenuItem
onClick={() => { onClick={() => {

View File

@@ -12,7 +12,6 @@ import { useApplication } from '../ApplicationView/ApplicationProvider'
import { PopoverFileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction' import { PopoverFileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction'
import Icon from '../Icon/Icon' import Icon from '../Icon/Icon'
import MenuItem from '../Menu/MenuItem' import MenuItem from '../Menu/MenuItem'
import { MenuItemType } from '../Menu/MenuItemType'
import Popover from '../Popover/Popover' import Popover from '../Popover/Popover'
import HorizontalSeparator from '../Shared/HorizontalSeparator' import HorizontalSeparator from '../Shared/HorizontalSeparator'
import LinkedFileMenuOptions from './LinkedFileMenuOptions' import LinkedFileMenuOptions from './LinkedFileMenuOptions'
@@ -107,7 +106,6 @@ export const LinkedItemsSectionItem = ({
className="py-2" className="py-2"
> >
<MenuItem <MenuItem
type={MenuItemType.IconButton}
onClick={() => { onClick={() => {
unlinkItem() unlinkItem()
toggleMenu() toggleMenu()

View File

@@ -1,79 +1,34 @@
import { forwardRef, MouseEventHandler, ReactNode, Ref } from 'react' import { ComponentPropsWithoutRef, forwardRef, MouseEventHandler, ReactNode, Ref } from 'react'
import Icon from '@/Components/Icon/Icon' import Icon from '@/Components/Icon/Icon'
import Switch from '@/Components/Switch/Switch'
import { SwitchProps } from '@/Components/Switch/SwitchProps'
import { IconType } from '@standardnotes/snjs' import { IconType } from '@standardnotes/snjs'
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants' import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
import { MenuItemType } from './MenuItemType'
import RadioIndicator from '../Radio/RadioIndicator'
import { classNames } from '@standardnotes/utils' import { classNames } from '@standardnotes/utils'
import { PlatformedKeyboardShortcut } from '@standardnotes/ui-services' import { PlatformedKeyboardShortcut } from '@standardnotes/ui-services'
import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator' import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator'
import MenuListItem from './MenuListItem'
type MenuItemProps = { type MenuItemProps = {
children: ReactNode children: ReactNode
type?: MenuItemType
onClick?: MouseEventHandler<HTMLButtonElement> onClick?: MouseEventHandler<HTMLButtonElement>
onChange?: SwitchProps['onChange']
onBlur?: (event: { relatedTarget: EventTarget | null }) => void onBlur?: (event: { relatedTarget: EventTarget | null }) => void
className?: string className?: string
checked?: boolean
icon?: IconType icon?: IconType
iconClassName?: string iconClassName?: string
tabIndex?: number tabIndex?: number
disabled?: boolean disabled?: boolean
shortcut?: PlatformedKeyboardShortcut shortcut?: PlatformedKeyboardShortcut
} } & ComponentPropsWithoutRef<'button'>
const MenuItem = forwardRef( const MenuItem = forwardRef(
( (
{ { children, className = '', icon, iconClassName, tabIndex, shortcut, ...props }: MenuItemProps,
children,
onClick,
onChange,
onBlur,
className = '',
type = MenuItemType.IconButton,
checked,
icon,
iconClassName,
tabIndex,
disabled,
shortcut,
}: MenuItemProps,
ref: Ref<HTMLButtonElement>, ref: Ref<HTMLButtonElement>,
) => { ) => {
return type === MenuItemType.SwitchButton && typeof onChange === 'function' ? ( return (
<li className="list-none" role="none"> <MenuListItem>
<button <button
disabled={disabled}
ref={ref} ref={ref}
className={classNames( role="menuitem"
'flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-2 md:py-1.5',
'text-left text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none',
'text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item',
)}
onClick={() => {
onChange(!checked)
}}
onBlur={onBlur}
tabIndex={typeof tabIndex === 'number' ? tabIndex : FOCUSABLE_BUT_NOT_TABBABLE}
role="menuitemcheckbox"
aria-checked={checked}
>
<span className="flex flex-grow items-center">{children}</span>
<div className="flex">
{shortcut && <KeyboardShortcutIndicator className="mr-2" shortcut={shortcut} />}
<Switch disabled={disabled} className="px-0" checked={checked} />
</div>
</button>
</li>
) : (
<li className="list-none" role="none">
<button
disabled={disabled}
ref={ref}
role={type === MenuItemType.RadioButton ? 'menuitemradio' : 'menuitem'}
tabIndex={typeof tabIndex === 'number' ? tabIndex : FOCUSABLE_BUT_NOT_TABBABLE} tabIndex={typeof tabIndex === 'number' ? tabIndex : FOCUSABLE_BUT_NOT_TABBABLE}
className={classNames( className={classNames(
'flex w-full cursor-pointer border-0 bg-transparent px-3 py-2 text-left md:py-1.5', 'flex w-full cursor-pointer border-0 bg-transparent px-3 py-2 text-left md:py-1.5',
@@ -82,20 +37,13 @@ const MenuItem = forwardRef(
className, className,
className.includes('items-') ? '' : 'items-center', className.includes('items-') ? '' : 'items-center',
)} )}
onClick={onClick} {...props}
onBlur={onBlur}
{...(type === MenuItemType.RadioButton ? { 'aria-checked': checked } : {})}
> >
{shortcut && <KeyboardShortcutIndicator className="mr-2" shortcut={shortcut} />} {shortcut && <KeyboardShortcutIndicator className="mr-2" shortcut={shortcut} />}
{type === MenuItemType.IconButton && icon ? ( {icon ? <Icon type={icon} className={classNames('flex-shrink-0', iconClassName)} /> : null}
<Icon type={icon} className={`${iconClassName} flex-shrink-0`} />
) : null}
{type === MenuItemType.RadioButton && typeof checked === 'boolean' ? (
<RadioIndicator disabled={disabled} checked={checked} className="flex-shrink-0" />
) : null}
{children} {children}
</button> </button>
</li> </MenuListItem>
) )
}, },
) )

View File

@@ -0,0 +1,11 @@
import { ReactNode } from 'react'
const MenuListItem = ({ children }: { children: ReactNode }) => {
return (
<li className="list-none" role="none">
{children}
</li>
)
}
export default MenuListItem

View File

@@ -0,0 +1,46 @@
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
import { classNames } from '@standardnotes/snjs'
import { PlatformedKeyboardShortcut } from '@standardnotes/ui-services'
import { ComponentPropsWithoutRef, ForwardedRef, forwardRef, ReactNode } from 'react'
import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator'
import RadioIndicator from '../Radio/RadioIndicator'
import MenuListItem from './MenuListItem'
type Props = {
checked: boolean
children: ReactNode
shortcut?: PlatformedKeyboardShortcut
} & ComponentPropsWithoutRef<'button'>
const MenuRadioButtonItem = forwardRef(
(
{ checked, disabled, tabIndex, children, shortcut, className, ...props }: Props,
ref: ForwardedRef<HTMLButtonElement>,
) => {
return (
<MenuListItem>
<button
ref={ref}
role="menuitemradio"
tabIndex={typeof tabIndex === 'number' ? tabIndex : FOCUSABLE_BUT_NOT_TABBABLE}
className={classNames(
'flex w-full cursor-pointer border-0 bg-transparent px-3 py-2 text-left md:py-1.5',
'text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground',
'focus:bg-info-backdrop focus:shadow-none md:text-tablet-menu-item lg:text-menu-item',
className,
className?.includes('items-') ? '' : 'items-center',
)}
aria-checked={checked}
disabled={disabled}
{...props}
>
{shortcut && <KeyboardShortcutIndicator className="mr-2" shortcut={shortcut} />}
<RadioIndicator disabled={disabled} checked={checked} className="flex-shrink-0" />
{children}
</button>
</MenuListItem>
)
},
)
export default MenuRadioButtonItem

View File

@@ -0,0 +1,53 @@
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
import { classNames } from '@standardnotes/snjs'
import { PlatformedKeyboardShortcut } from '@standardnotes/ui-services'
import { ComponentPropsWithoutRef, ForwardedRef, forwardRef, ReactNode } from 'react'
import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator'
import Switch from '../Switch/Switch'
import { SwitchProps } from '../Switch/SwitchProps'
import MenuListItem from './MenuListItem'
type Props = {
checked: boolean
children: ReactNode
onChange: NonNullable<SwitchProps['onChange']>
shortcut?: PlatformedKeyboardShortcut
} & Omit<ComponentPropsWithoutRef<'button'>, 'onChange'>
const MenuSwitchButtonItem = forwardRef(
(
{ checked, onChange, disabled, onBlur, tabIndex, children, shortcut, className, ...props }: Props,
ref: ForwardedRef<HTMLButtonElement>,
) => {
return (
<MenuListItem>
<button
disabled={disabled}
ref={ref}
className={classNames(
'flex w-full cursor-pointer border-0 bg-transparent px-3 py-2 md:py-1.5',
'text-left text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none',
'text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item',
className,
)}
onClick={() => {
onChange(!checked)
}}
onBlur={onBlur}
tabIndex={typeof tabIndex === 'number' ? tabIndex : FOCUSABLE_BUT_NOT_TABBABLE}
role="menuitemcheckbox"
aria-checked={checked}
{...props}
>
<span className="flex flex-grow items-center">{children}</span>
<div className="flex">
{shortcut && <KeyboardShortcutIndicator className="mr-2" shortcut={shortcut} />}
<Switch disabled={disabled} className="px-0" checked={checked} />
</div>
</button>
</MenuListItem>
)
},
)
export default MenuSwitchButtonItem

View File

@@ -1,12 +1,13 @@
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
import NotesOptions from '@/Components/NotesOptions/NotesOptions' import NotesOptions from '@/Components/NotesOptions/NotesOptions'
import { useCallback, useRef, useState } from 'react' import { useCallback, useState } from 'react'
import { WebApplication } from '@/Application/Application' import { WebApplication } from '@/Application/Application'
import { NotesController } from '@/Controllers/NotesController/NotesController' import { NotesController } from '@/Controllers/NotesController/NotesController'
import { NavigationController } from '@/Controllers/Navigation/NavigationController' import { NavigationController } from '@/Controllers/Navigation/NavigationController'
import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalController' import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalController'
import Popover from '../Popover/Popover' import Popover from '../Popover/Popover'
import { LinkingController } from '@/Controllers/LinkingController' import { LinkingController } from '@/Controllers/LinkingController'
import Menu from '../Menu/Menu'
type Props = { type Props = {
application: WebApplication application: WebApplication
@@ -25,8 +26,6 @@ const NotesContextMenu = ({
}: Props) => { }: Props) => {
const { contextMenuOpen, contextMenuClickLocation, setContextMenuOpen } = notesController const { contextMenuOpen, contextMenuClickLocation, setContextMenuOpen } = notesController
const contextMenuRef = useRef<HTMLDivElement>(null)
const closeMenu = () => setContextMenuOpen(!contextMenuOpen) const closeMenu = () => setContextMenuOpen(!contextMenuOpen)
const [disableClickOutside, setDisableClickOutside] = useState(false) const [disableClickOutside, setDisableClickOutside] = useState(false)
@@ -46,7 +45,7 @@ const NotesContextMenu = ({
open={contextMenuOpen} open={contextMenuOpen}
togglePopover={closeMenu} togglePopover={closeMenu}
> >
<div className="select-none" ref={contextMenuRef}> <Menu className="select-none" a11yLabel="Note context menu" isOpen={contextMenuOpen}>
<NotesOptions <NotesOptions
application={application} application={application}
navigationController={navigationController} navigationController={navigationController}
@@ -56,7 +55,7 @@ const NotesContextMenu = ({
requestDisableClickOutside={handleDisableClickOutsideRequest} requestDisableClickOutside={handleDisableClickOutsideRequest}
closeMenu={closeMenu} closeMenu={closeMenu}
/> />
</div> </Menu>
</Popover> </Popover>
) )
} }

View File

@@ -8,20 +8,15 @@ import Popover from '../Popover/Popover'
import { IconType } from '@standardnotes/snjs' import { IconType } from '@standardnotes/snjs'
import { getTitleForLinkedTag } from '@/Utils/Items/Display/getTitleForLinkedTag' import { getTitleForLinkedTag } from '@/Utils/Items/Display/getTitleForLinkedTag'
import { useApplication } from '../ApplicationView/ApplicationProvider' import { useApplication } from '../ApplicationView/ApplicationProvider'
import MenuItem from '../Menu/MenuItem'
type Props = { type Props = {
navigationController: NavigationController navigationController: NavigationController
notesController: NotesController notesController: NotesController
className: string
iconClassName: string iconClassName: string
} }
const AddTagOption: FunctionComponent<Props> = ({ const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesController, iconClassName }) => {
navigationController,
notesController,
className,
iconClassName,
}) => {
const application = useApplication() const application = useApplication()
const menuContainerRef = useRef<HTMLDivElement>(null) const menuContainerRef = useRef<HTMLDivElement>(null)
const buttonRef = useRef<HTMLButtonElement>(null) const buttonRef = useRef<HTMLButtonElement>(null)
@@ -34,7 +29,8 @@ const AddTagOption: FunctionComponent<Props> = ({
return ( return (
<div ref={menuContainerRef}> <div ref={menuContainerRef}>
<button <MenuItem
className="justify-between"
onClick={toggleMenu} onClick={toggleMenu}
onKeyDown={(event) => { onKeyDown={(event) => {
if (event.key === KeyboardKey.Escape) { if (event.key === KeyboardKey.Escape) {
@@ -42,14 +38,13 @@ const AddTagOption: FunctionComponent<Props> = ({
} }
}} }}
ref={buttonRef} ref={buttonRef}
className={className}
> >
<div className="flex items-center"> <div className="flex items-center">
<Icon type="hashtag" className={`${iconClassName} mr-2 text-neutral`} /> <Icon type="hashtag" className={`${iconClassName} mr-2 text-neutral`} />
Add tag Add tag
</div> </div>
<Icon type="chevron-right" className="text-neutral" /> <Icon type="chevron-right" className="text-neutral" />
</button> </MenuItem>
<Popover <Popover
togglePopover={toggleMenu} togglePopover={toggleMenu}
anchorElement={buttonRef.current} anchorElement={buttonRef.current}
@@ -59,9 +54,8 @@ const AddTagOption: FunctionComponent<Props> = ({
className="py-2" className="py-2"
> >
{navigationController.tags.map((tag) => ( {navigationController.tags.map((tag) => (
<button <MenuItem
key={tag.uuid} key={tag.uuid}
className={`max-w-80 ${className.replace('justify-between', 'justify-start')}`}
onClick={() => { onClick={() => {
notesController.isTagInSelectedNotes(tag) notesController.isTagInSelectedNotes(tag)
? notesController.removeTagFromSelectedNotes(tag).catch(console.error) ? notesController.removeTagFromSelectedNotes(tag).catch(console.error)
@@ -81,7 +75,7 @@ const AddTagOption: FunctionComponent<Props> = ({
> >
{getTitleForLinkedTag(tag, application)?.longTitle} {getTitleForLinkedTag(tag, application)?.longTitle}
</span> </span>
</button> </MenuItem>
))} ))}
</Popover> </Popover>
</div> </div>

View File

@@ -6,20 +6,15 @@ import Icon from '@/Components/Icon/Icon'
import ChangeEditorMenu from '@/Components/ChangeEditor/ChangeEditorMenu' import ChangeEditorMenu from '@/Components/ChangeEditor/ChangeEditorMenu'
import Popover from '../Popover/Popover' import Popover from '../Popover/Popover'
import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator' import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator'
import MenuItem from '../Menu/MenuItem'
type ChangeEditorOptionProps = { type ChangeEditorOptionProps = {
application: WebApplication application: WebApplication
note: SNNote note: SNNote
className: string
iconClassName: string iconClassName: string
} }
const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({ const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({ application, note, iconClassName }) => {
application,
note,
className,
iconClassName,
}) => {
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const menuContainerRef = useRef<HTMLDivElement>(null) const menuContainerRef = useRef<HTMLDivElement>(null)
const buttonRef = useRef<HTMLButtonElement>(null) const buttonRef = useRef<HTMLButtonElement>(null)
@@ -35,7 +30,8 @@ const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({
return ( return (
<div ref={menuContainerRef}> <div ref={menuContainerRef}>
<button <MenuItem
className="justify-between"
onClick={toggleMenu} onClick={toggleMenu}
onKeyDown={(event) => { onKeyDown={(event) => {
if (event.key === KeyboardKey.Escape) { if (event.key === KeyboardKey.Escape) {
@@ -43,7 +39,6 @@ const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({
} }
}} }}
ref={buttonRef} ref={buttonRef}
className={className}
> >
<div className="flex items-center"> <div className="flex items-center">
<Icon type="dashboard" className={`${iconClassName} mr-2 text-neutral`} /> <Icon type="dashboard" className={`${iconClassName} mr-2 text-neutral`} />
@@ -53,7 +48,7 @@ const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({
{shortcut && <KeyboardShortcutIndicator className={'mr-2'} shortcut={shortcut} />} {shortcut && <KeyboardShortcutIndicator className={'mr-2'} shortcut={shortcut} />}
<Icon type="chevron-right" className="text-neutral" /> <Icon type="chevron-right" className="text-neutral" />
</div> </div>
</button> </MenuItem>
<Popover <Popover
align="start" align="start"
anchorElement={buttonRef.current} anchorElement={buttonRef.current}

View File

@@ -1,13 +1,3 @@
import { MenuItemIconSize } from '@/Constants/TailwindClassNames' import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
import { classNames } from '@standardnotes/utils'
export const menuItemTextClassNames = 'text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item'
export const menuItemClassNames = classNames(
menuItemTextClassNames,
'flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none',
)
export const menuItemSwitchClassNames = classNames(menuItemTextClassNames, menuItemClassNames, 'justify-between')
export const iconClass = `text-neutral mr-2 ${MenuItemIconSize}` export const iconClass = `text-neutral mr-2 ${MenuItemIconSize}`

View File

@@ -1,19 +1,13 @@
import Icon from '@/Components/Icon/Icon' import Icon from '@/Components/Icon/Icon'
import { classNames } from '@standardnotes/utils' import MenuItem from '../Menu/MenuItem'
type DeletePermanentlyButtonProps = { type DeletePermanentlyButtonProps = {
onClick: () => void onClick: () => void
} }
export const DeletePermanentlyButton = ({ onClick }: DeletePermanentlyButtonProps) => ( export const DeletePermanentlyButton = ({ onClick }: DeletePermanentlyButtonProps) => (
<button <MenuItem onClick={onClick}>
className={classNames(
'flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item',
'text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item',
)}
onClick={onClick}
>
<Icon type="close" className="mr-2 text-danger" /> <Icon type="close" className="mr-2 text-danger" />
<span className="text-danger">Delete permanently</span> <span className="text-danger">Delete permanently</span>
</button> </MenuItem>
) )

View File

@@ -5,15 +5,15 @@ import Icon from '@/Components/Icon/Icon'
import ListedActionsMenu from './ListedActionsMenu' import ListedActionsMenu from './ListedActionsMenu'
import { KeyboardKey } from '@standardnotes/ui-services' import { KeyboardKey } from '@standardnotes/ui-services'
import Popover from '../../Popover/Popover' import Popover from '../../Popover/Popover'
import MenuItem from '@/Components/Menu/MenuItem'
type Props = { type Props = {
application: WebApplication application: WebApplication
note: SNNote note: SNNote
className: string
iconClassName: string iconClassName: string
} }
const ListedActionsOption: FunctionComponent<Props> = ({ application, note, className, iconClassName }) => { const ListedActionsOption: FunctionComponent<Props> = ({ application, note, iconClassName }) => {
const menuContainerRef = useRef<HTMLDivElement>(null) const menuContainerRef = useRef<HTMLDivElement>(null)
const buttonRef = useRef<HTMLButtonElement>(null) const buttonRef = useRef<HTMLButtonElement>(null)
@@ -31,7 +31,8 @@ const ListedActionsOption: FunctionComponent<Props> = ({ application, note, clas
return ( return (
<div ref={menuContainerRef}> <div ref={menuContainerRef}>
<button <MenuItem
className="justify-between"
onClick={toggleMenu} onClick={toggleMenu}
onKeyDown={(event) => { onKeyDown={(event) => {
if (event.key === KeyboardKey.Escape) { if (event.key === KeyboardKey.Escape) {
@@ -39,14 +40,13 @@ const ListedActionsOption: FunctionComponent<Props> = ({ application, note, clas
} }
}} }}
ref={buttonRef} ref={buttonRef}
className={className}
> >
<div className="flex items-center"> <div className="flex items-center">
<Icon type="listed" className={`mr-2 text-neutral ${iconClassName}`} /> <Icon type="listed" className={`mr-2 text-neutral ${iconClassName}`} />
Listed actions Listed actions
</div> </div>
<Icon type="chevron-right" className="text-neutral" /> <Icon type="chevron-right" className="text-neutral" />
</button> </MenuItem>
<Popover <Popover
togglePopover={toggleMenu} togglePopover={toggleMenu}
anchorElement={buttonRef.current} anchorElement={buttonRef.current}

View File

@@ -1,5 +1,4 @@
import Icon from '@/Components/Icon/Icon' import Icon from '@/Components/Icon/Icon'
import Switch from '@/Components/Switch/Switch'
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
import { useState, useEffect, useMemo, useCallback } from 'react' import { useState, useEffect, useMemo, useCallback } from 'react'
import { NoteType, Platform, SNNote } from '@standardnotes/snjs' import { NoteType, Platform, SNNote } from '@standardnotes/snjs'
@@ -22,7 +21,6 @@ import { getNoteBlob, getNoteFileName } from '@/Utils/NoteExportUtils'
import { shareSelectedNotes } from '@/NativeMobileWeb/ShareSelectedNotes' import { shareSelectedNotes } from '@/NativeMobileWeb/ShareSelectedNotes'
import { downloadSelectedNotesOnAndroid } from '@/NativeMobileWeb/DownloadSelectedNotesOnAndroid' import { downloadSelectedNotesOnAndroid } from '@/NativeMobileWeb/DownloadSelectedNotesOnAndroid'
import ProtectedUnauthorizedLabel from '../ProtectedItemOverlay/ProtectedUnauthorizedLabel' import ProtectedUnauthorizedLabel from '../ProtectedItemOverlay/ProtectedUnauthorizedLabel'
import { classNames } from '@standardnotes/utils'
import { MenuItemIconSize } from '@/Constants/TailwindClassNames' import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator' import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator'
import { NoteAttributes } from './NoteAttributes' import { NoteAttributes } from './NoteAttributes'
@@ -30,8 +28,10 @@ import { SpellcheckOptions } from './SpellcheckOptions'
import { NoteSizeWarning } from './NoteSizeWarning' import { NoteSizeWarning } from './NoteSizeWarning'
import { DeletePermanentlyButton } from './DeletePermanentlyButton' import { DeletePermanentlyButton } from './DeletePermanentlyButton'
import { useCommandService } from '../ApplicationView/CommandProvider' import { useCommandService } from '../ApplicationView/CommandProvider'
import { menuItemClassNames, menuItemSwitchClassNames, iconClass } from './ClassNames' import { iconClass } from './ClassNames'
import SuperNoteOptions from './SuperNoteOptions' import SuperNoteOptions from './SuperNoteOptions'
import MenuSwitchButtonItem from '../Menu/MenuSwitchButtonItem'
import MenuItem from '../Menu/MenuItem'
const iconSize = MenuItemIconSize const iconSize = MenuItemIconSize
const iconClassDanger = `text-danger mr-2 ${iconSize}` const iconClassDanger = `text-danger mr-2 ${iconSize}`
@@ -160,132 +160,94 @@ const NotesOptions = ({
return <ProtectedUnauthorizedLabel /> return <ProtectedUnauthorizedLabel />
} }
const firstItemClass = 'pt-4'
return ( return (
<> <>
{notes.length === 1 && ( {notes.length === 1 && (
<> <>
<button className={classNames(menuItemClassNames, firstItemClass)} onClick={openRevisionHistoryModal}> <MenuItem onClick={openRevisionHistoryModal}>
<div className="flex w-full items-center justify-between"> <Icon type="history" className={iconClass} />
<span className="flex"> Note history
<Icon type="history" className={iconClass} /> {historyShortcut && <KeyboardShortcutIndicator className="ml-auto" shortcut={historyShortcut} />}
Note history </MenuItem>
</span>
{historyShortcut && <KeyboardShortcutIndicator className={''} shortcut={historyShortcut} />}
</div>
</button>
<HorizontalSeparator classes="my-2" /> <HorizontalSeparator classes="my-2" />
</> </>
)} )}
<button <MenuSwitchButtonItem
className={menuItemSwitchClassNames} checked={locked}
onClick={() => { onChange={(locked) => {
notesController.setLockSelectedNotes(!locked) notesController.setLockSelectedNotes(locked)
}} }}
> >
<span className="flex items-center"> <Icon type="pencil-off" className={iconClass} />
<Icon type="pencil-off" className={iconClass} /> Prevent editing
Prevent editing </MenuSwitchButtonItem>
</span> <MenuSwitchButtonItem
<Switch className="px-0" checked={locked} /> checked={!hidePreviews}
</button> onChange={(hidePreviews) => {
<button
className={menuItemSwitchClassNames}
onClick={() => {
notesController.setHideSelectedNotePreviews(!hidePreviews) notesController.setHideSelectedNotePreviews(!hidePreviews)
}} }}
> >
<span className="flex items-center"> <Icon type="rich-text" className={iconClass} />
<Icon type="rich-text" className={iconClass} /> Show preview
Show preview </MenuSwitchButtonItem>
</span> <MenuSwitchButtonItem
<Switch className="px-0" checked={!hidePreviews} /> checked={protect}
</button> onChange={(protect) => {
<button notesController.setProtectSelectedNotes(protect).catch(console.error)
className={menuItemSwitchClassNames}
onClick={() => {
notesController.setProtectSelectedNotes(!protect).catch(console.error)
}} }}
> >
<span className="flex items-center"> <Icon type="lock" className={iconClass} />
<Icon type="lock" className={iconClass} /> Password protect
Password protect </MenuSwitchButtonItem>
</span>
<Switch className="px-0" checked={protect} />
</button>
{notes.length === 1 && ( {notes.length === 1 && (
<> <>
<HorizontalSeparator classes="my-2" /> <HorizontalSeparator classes="my-2" />
<ChangeEditorOption <ChangeEditorOption iconClassName={iconClass} application={application} note={notes[0]} />
iconClassName={iconClass}
className={menuItemSwitchClassNames}
application={application}
note={notes[0]}
/>
</> </>
)} )}
<HorizontalSeparator classes="my-2" /> <HorizontalSeparator classes="my-2" />
{navigationController.tagsCount > 0 && ( {navigationController.tagsCount > 0 && (
<AddTagOption <AddTagOption
iconClassName={iconClass} iconClassName={iconClass}
className={menuItemSwitchClassNames}
navigationController={navigationController} navigationController={navigationController}
notesController={notesController} notesController={notesController}
/> />
)} )}
<MenuItem
<button
className={menuItemClassNames}
onClick={() => { onClick={() => {
notesController.setStarSelectedNotes(!starred) notesController.setStarSelectedNotes(!starred)
}} }}
> >
<div className="flex w-full items-center justify-between"> <Icon type="star" className={iconClass} />
<span className="flex"> {starred ? 'Unstar' : 'Star'}
<Icon type="star" className={iconClass} /> {starShortcut && <KeyboardShortcutIndicator className="ml-auto" shortcut={starShortcut} />}
{starred ? 'Unstar' : 'Star'} </MenuItem>
</span>
{starShortcut && <KeyboardShortcutIndicator className={''} shortcut={starShortcut} />}
</div>
</button>
{unpinned && ( {unpinned && (
<button <MenuItem
className={menuItemClassNames}
onClick={() => { onClick={() => {
notesController.setPinSelectedNotes(true) notesController.setPinSelectedNotes(true)
}} }}
> >
<div className="flex w-full items-center justify-between"> <Icon type="pin" className={iconClass} />
<span className="flex"> Pin to top
<Icon type="pin" className={iconClass} /> {pinShortcut && <KeyboardShortcutIndicator className="ml-auto" shortcut={pinShortcut} />}
Pin to top </MenuItem>
</span>
{pinShortcut && <KeyboardShortcutIndicator className={''} shortcut={pinShortcut} />}
</div>
</button>
)} )}
{pinned && ( {pinned && (
<button <MenuItem
className={menuItemClassNames}
onClick={() => { onClick={() => {
notesController.setPinSelectedNotes(false) notesController.setPinSelectedNotes(false)
}} }}
> >
<div className="flex w-full items-center justify-between"> <Icon type="unpin" className={iconClass} />
<span className="flex"> Unpin
<Icon type="unpin" className={iconClass} /> {pinShortcut && <KeyboardShortcutIndicator className="ml-auto" shortcut={pinShortcut} />}
Unpin </MenuItem>
</span>
{pinShortcut && <KeyboardShortcutIndicator className={''} shortcut={pinShortcut} />}
</div>
</button>
)} )}
{notes[0].noteType !== NoteType.Super && ( {notes[0].noteType !== NoteType.Super && (
<> <>
<button <MenuItem
className={menuItemClassNames}
onClick={() => { onClick={() => {
application.isNativeMobileWeb() application.isNativeMobileWeb()
? void shareSelectedNotes(application, notes) ? void shareSelectedNotes(application, notes)
@@ -294,22 +256,21 @@ const NotesOptions = ({
> >
<Icon type={application.platform === Platform.Android ? 'share' : 'download'} className={iconClass} /> <Icon type={application.platform === Platform.Android ? 'share' : 'download'} className={iconClass} />
{application.platform === Platform.Android ? 'Share' : 'Export'} {application.platform === Platform.Android ? 'Share' : 'Export'}
</button> </MenuItem>
{application.platform === Platform.Android && ( {application.platform === Platform.Android && (
<button className={menuItemClassNames} onClick={() => downloadSelectedNotesOnAndroid(application, notes)}> <MenuItem onClick={() => downloadSelectedNotesOnAndroid(application, notes)}>
<Icon type="download" className={iconClass} /> <Icon type="download" className={iconClass} />
Export Export
</button> </MenuItem>
)} )}
</> </>
)} )}
<button className={menuItemClassNames} onClick={duplicateSelectedItems}> <MenuItem onClick={duplicateSelectedItems}>
<Icon type="copy" className={iconClass} /> <Icon type="copy" className={iconClass} />
Duplicate Duplicate
</button> </MenuItem>
{unarchived && ( {unarchived && (
<button <MenuItem
className={menuItemClassNames}
onClick={async () => { onClick={async () => {
await notesController.setArchiveSelectedNotes(true).catch(console.error) await notesController.setArchiveSelectedNotes(true).catch(console.error)
closeMenuAndToggleNotesList() closeMenuAndToggleNotesList()
@@ -317,11 +278,10 @@ const NotesOptions = ({
> >
<Icon type="archive" className={iconClassWarning} /> <Icon type="archive" className={iconClassWarning} />
<span className="text-warning">Archive</span> <span className="text-warning">Archive</span>
</button> </MenuItem>
)} )}
{archived && ( {archived && (
<button <MenuItem
className={menuItemClassNames}
onClick={async () => { onClick={async () => {
await notesController.setArchiveSelectedNotes(false).catch(console.error) await notesController.setArchiveSelectedNotes(false).catch(console.error)
closeMenuAndToggleNotesList() closeMenuAndToggleNotesList()
@@ -329,7 +289,7 @@ const NotesOptions = ({
> >
<Icon type="unarchive" className={iconClassWarning} /> <Icon type="unarchive" className={iconClassWarning} />
<span className="text-warning">Unarchive</span> <span className="text-warning">Unarchive</span>
</button> </MenuItem>
)} )}
{notTrashed && {notTrashed &&
(altKeyDown ? ( (altKeyDown ? (
@@ -340,8 +300,7 @@ const NotesOptions = ({
}} }}
/> />
) : ( ) : (
<button <MenuItem
className={menuItemClassNames}
onClick={async () => { onClick={async () => {
await notesController.setTrashSelectedNotes(true) await notesController.setTrashSelectedNotes(true)
closeMenuAndToggleNotesList() closeMenuAndToggleNotesList()
@@ -349,12 +308,11 @@ const NotesOptions = ({
> >
<Icon type="trash" className={iconClassDanger} /> <Icon type="trash" className={iconClassDanger} />
<span className="text-danger">Move to trash</span> <span className="text-danger">Move to trash</span>
</button> </MenuItem>
))} ))}
{trashed && ( {trashed && (
<> <>
<button <MenuItem
className={menuItemClassNames}
onClick={async () => { onClick={async () => {
await notesController.setTrashSelectedNotes(false) await notesController.setTrashSelectedNotes(false)
closeMenuAndToggleNotesList() closeMenuAndToggleNotesList()
@@ -362,15 +320,14 @@ const NotesOptions = ({
> >
<Icon type="restore" className={iconClassSuccess} /> <Icon type="restore" className={iconClassSuccess} />
<span className="text-success">Restore</span> <span className="text-success">Restore</span>
</button> </MenuItem>
<DeletePermanentlyButton <DeletePermanentlyButton
onClick={async () => { onClick={async () => {
await notesController.deleteNotesPermanently() await notesController.deleteNotesPermanently()
closeMenuAndToggleNotesList() closeMenuAndToggleNotesList()
}} }}
/> />
<button <MenuItem
className={menuItemClassNames}
onClick={async () => { onClick={async () => {
await notesController.emptyTrash() await notesController.emptyTrash()
closeMenuAndToggleNotesList() closeMenuAndToggleNotesList()
@@ -383,7 +340,7 @@ const NotesOptions = ({
<div className="text-xs">{notesController.trashedNotesCount} notes in Trash</div> <div className="text-xs">{notesController.trashedNotesCount} notes in Trash</div>
</div> </div>
</div> </div>
</button> </MenuItem>
</> </>
)} )}
@@ -398,21 +355,11 @@ const NotesOptions = ({
)} )}
<HorizontalSeparator classes="my-2" /> <HorizontalSeparator classes="my-2" />
<ListedActionsOption <ListedActionsOption iconClassName={iconClass} application={application} note={notes[0]} />
iconClassName={iconClass}
className={menuItemSwitchClassNames}
application={application}
note={notes[0]}
/>
<HorizontalSeparator classes="my-2" /> <HorizontalSeparator classes="my-2" />
<SpellcheckOptions <SpellcheckOptions editorForNote={editorForNote} notesController={notesController} note={notes[0]} />
className={menuItemSwitchClassNames}
editorForNote={editorForNote}
notesController={notesController}
note={notes[0]}
/>
<HorizontalSeparator classes="my-2" /> <HorizontalSeparator classes="my-2" />

View File

@@ -8,6 +8,7 @@ import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalCo
import Popover from '../Popover/Popover' import Popover from '../Popover/Popover'
import { LinkingController } from '@/Controllers/LinkingController' import { LinkingController } from '@/Controllers/LinkingController'
import RoundIconButton from '../Button/RoundIconButton' import RoundIconButton from '../Button/RoundIconButton'
import Menu from '../Menu/Menu'
type Props = { type Props = {
application: WebApplication application: WebApplication
@@ -50,17 +51,19 @@ const NotesOptionsPanel = ({
togglePopover={toggleMenu} togglePopover={toggleMenu}
anchorElement={buttonRef.current} anchorElement={buttonRef.current}
open={isOpen} open={isOpen}
className="select-none" className="select-none md:pt-2"
> >
<NotesOptions <Menu a11yLabel="Note options menu" isOpen={isOpen}>
application={application} <NotesOptions
navigationController={navigationController} application={application}
notesController={notesController} navigationController={navigationController}
linkingController={linkingController} notesController={notesController}
historyModalController={historyModalController} linkingController={linkingController}
requestDisableClickOutside={handleDisableClickOutsideRequest} historyModalController={historyModalController}
closeMenu={toggleMenu} requestDisableClickOutside={handleDisableClickOutsideRequest}
/> closeMenu={toggleMenu}
/>
</Menu>
</Popover> </Popover>
</> </>
) )

View File

@@ -1,16 +1,15 @@
import Icon from '@/Components/Icon/Icon' import Icon from '@/Components/Icon/Icon'
import Switch from '@/Components/Switch/Switch'
import { FunctionComponent } from 'react' import { FunctionComponent } from 'react'
import { SNComponent, SNNote } from '@standardnotes/snjs' import { SNComponent, SNNote } from '@standardnotes/snjs'
import { NotesController } from '@/Controllers/NotesController/NotesController' import { NotesController } from '@/Controllers/NotesController/NotesController'
import { iconClass } from './ClassNames' import { iconClass } from './ClassNames'
import MenuSwitchButtonItem from '../Menu/MenuSwitchButtonItem'
export const SpellcheckOptions: FunctionComponent<{ export const SpellcheckOptions: FunctionComponent<{
editorForNote: SNComponent | undefined editorForNote: SNComponent | undefined
notesController: NotesController notesController: NotesController
note: SNNote note: SNNote
className: string }> = ({ editorForNote, notesController, note }) => {
}> = ({ editorForNote, notesController, note, className }) => {
const spellcheckControllable = Boolean(!editorForNote || editorForNote.package_info.spellcheckControl) const spellcheckControllable = Boolean(!editorForNote || editorForNote.package_info.spellcheckControl)
const noteSpellcheck = !spellcheckControllable const noteSpellcheck = !spellcheckControllable
? true ? true
@@ -20,19 +19,16 @@ export const SpellcheckOptions: FunctionComponent<{
return ( return (
<div className="flex flex-col"> <div className="flex flex-col">
<button <MenuSwitchButtonItem
className={className} checked={Boolean(noteSpellcheck)}
onClick={() => { onChange={() => {
notesController.toggleGlobalSpellcheckForNote(note).catch(console.error) notesController.toggleGlobalSpellcheckForNote(note).catch(console.error)
}} }}
disabled={!spellcheckControllable} disabled={!spellcheckControllable}
> >
<span className="flex items-center"> <Icon type="notes" className={iconClass} />
<Icon type="notes" className={iconClass} /> Spellcheck
Spellcheck </MenuSwitchButtonItem>
</span>
<Switch className="px-0" checked={noteSpellcheck} disabled={!spellcheckControllable} />
</button>
{!spellcheckControllable && ( {!spellcheckControllable && (
<p className="px-3 py-1.5 text-xs">Spellcheck cannot be controlled for this editor.</p> <p className="px-3 py-1.5 text-xs">Spellcheck cannot be controlled for this editor.</p>
)} )}

View File

@@ -13,7 +13,7 @@ import Menu from '../Menu/Menu'
import MenuItem from '../Menu/MenuItem' import MenuItem from '../Menu/MenuItem'
import Popover from '../Popover/Popover' import Popover from '../Popover/Popover'
import HorizontalSeparator from '../Shared/HorizontalSeparator' import HorizontalSeparator from '../Shared/HorizontalSeparator'
import { iconClass, menuItemClassNames, menuItemSwitchClassNames } from './ClassNames' import { iconClass } from './ClassNames'
type Props = { type Props = {
note: SNNote note: SNNote
@@ -33,19 +33,14 @@ const SuperNoteOptions = ({ note, markdownShortcut, enableSuperMarkdownPreview }
<div className="my-1 px-3 text-base font-semibold uppercase text-text lg:text-xs">Super</div> <div className="my-1 px-3 text-base font-semibold uppercase text-text lg:text-xs">Super</div>
<button className={menuItemClassNames} onClick={enableSuperMarkdownPreview}> <MenuItem onClick={enableSuperMarkdownPreview}>
<div className="flex w-full items-center justify-between"> <Icon type="markdown" className={iconClass} />
<span className="flex"> Show Markdown
<Icon type="markdown" className={iconClass} /> {markdownShortcut && <KeyboardShortcutIndicator className="ml-auto" shortcut={markdownShortcut} />}
Show Markdown </MenuItem>
</span>
{markdownShortcut && <KeyboardShortcutIndicator shortcut={markdownShortcut} />}
</div>
</button>
<button <MenuItem
ref={exportButtonRef} ref={exportButtonRef}
className={menuItemSwitchClassNames}
onClick={() => { onClick={() => {
setIsExportMenuOpen((open) => !open) setIsExportMenuOpen((open) => !open)
}} }}
@@ -54,8 +49,8 @@ const SuperNoteOptions = ({ note, markdownShortcut, enableSuperMarkdownPreview }
<Icon type="download" className={iconClass} /> <Icon type="download" className={iconClass} />
Export Export
</div> </div>
<Icon type="chevron-right" className="text-neutral" /> <Icon type="chevron-right" className="ml-auto text-neutral" />
</button> </MenuItem>
<Popover <Popover
side="left" side="left"
align="start" align="start"

View File

@@ -1,10 +1,9 @@
import { TOGGLE_LIST_PANE_KEYBOARD_COMMAND, TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND } from '@standardnotes/ui-services' import { TOGGLE_LIST_PANE_KEYBOARD_COMMAND, TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND } from '@standardnotes/ui-services'
import { useMemo } from 'react' import { useMemo } from 'react'
import MenuItem from '../Menu/MenuItem'
import { MenuItemType } from '../Menu/MenuItemType'
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider' import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { useCommandService } from '../ApplicationView/CommandProvider' import { useCommandService } from '../ApplicationView/CommandProvider'
import MenuSwitchButtonItem from '../Menu/MenuSwitchButtonItem'
const PanelSettingsSection = () => { const PanelSettingsSection = () => {
const { isListPaneCollapsed, isNavigationPaneCollapsed, toggleListPane, toggleNavigationPane } = const { isListPaneCollapsed, isNavigationPaneCollapsed, toggleListPane, toggleNavigationPane } =
@@ -24,24 +23,22 @@ const PanelSettingsSection = () => {
return ( return (
<div className="hidden md:block pointer-coarse:md-only:hidden pointer-coarse:lg-only:hidden"> <div className="hidden md:block pointer-coarse:md-only:hidden pointer-coarse:lg-only:hidden">
<MenuItem <MenuSwitchButtonItem
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop" className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={isNavigationPaneCollapsed} checked={isNavigationPaneCollapsed}
onChange={toggleNavigationPane} onChange={toggleNavigationPane}
shortcut={navigationShortcut} shortcut={navigationShortcut}
> >
Show Tags Panel Show Tags Panel
</MenuItem> </MenuSwitchButtonItem>
<MenuItem <MenuSwitchButtonItem
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop" className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={isListPaneCollapsed} checked={isListPaneCollapsed}
onChange={toggleListPane} onChange={toggleListPane}
shortcut={listShortcut} shortcut={listShortcut}
> >
Show Notes Panel Show Notes Panel
</MenuItem> </MenuSwitchButtonItem>
</div> </div>
) )
} }

View File

@@ -3,7 +3,6 @@ import { useCallback, useRef, useMemo } from 'react'
import Icon from '@/Components/Icon/Icon' import Icon from '@/Components/Icon/Icon'
import Menu from '@/Components/Menu/Menu' import Menu from '@/Components/Menu/Menu'
import MenuItem from '@/Components/Menu/MenuItem' import MenuItem from '@/Components/Menu/MenuItem'
import { MenuItemType } from '@/Components/Menu/MenuItemType'
import { usePremiumModal } from '@/Hooks/usePremiumModal' import { usePremiumModal } from '@/Hooks/usePremiumModal'
import { SNTag, VectorIconNameOrEmoji, DefaultTagIconName } from '@standardnotes/snjs' import { SNTag, VectorIconNameOrEmoji, DefaultTagIconName } from '@standardnotes/snjs'
import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside' import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside'
@@ -82,24 +81,24 @@ const TagContextMenu = ({ navigationController, isEntitledToFolders, selectedTag
iconGridClassName="max-h-30" iconGridClassName="max-h-30"
/> />
<HorizontalSeparator classes="my-2" /> <HorizontalSeparator classes="my-2" />
<MenuItem type={MenuItemType.IconButton} className={'justify-between py-1.5'} onClick={onClickStar}> <MenuItem className={'justify-between py-1.5'} onClick={onClickStar}>
<div className="flex items-center"> <div className="flex items-center">
<Icon type="star" className="mr-2 text-neutral" /> <Icon type="star" className="mr-2 text-neutral" />
{selectedTag.starred ? 'Unfavorite' : 'Favorite'} {selectedTag.starred ? 'Unfavorite' : 'Favorite'}
</div> </div>
</MenuItem> </MenuItem>
<MenuItem type={MenuItemType.IconButton} className={'justify-between py-1.5'} onClick={onClickAddSubtag}> <MenuItem className={'justify-between py-1.5'} onClick={onClickAddSubtag}>
<div className="flex items-center"> <div className="flex items-center">
<Icon type="add" className="mr-2 text-neutral" /> <Icon type="add" className="mr-2 text-neutral" />
Add subtag Add subtag
</div> </div>
{!isEntitledToFolders && <Icon type={PremiumFeatureIconName} className={PremiumFeatureIconClass} />} {!isEntitledToFolders && <Icon type={PremiumFeatureIconName} className={PremiumFeatureIconClass} />}
</MenuItem> </MenuItem>
<MenuItem type={MenuItemType.IconButton} className={'py-1.5'} onClick={onClickRename}> <MenuItem className={'py-1.5'} onClick={onClickRename}>
<Icon type="pencil-filled" className="mr-2 text-neutral" /> <Icon type="pencil-filled" className="mr-2 text-neutral" />
Rename Rename
</MenuItem> </MenuItem>
<MenuItem type={MenuItemType.IconButton} className={'py-1.5'} onClick={onClickDelete}> <MenuItem className={'py-1.5'} onClick={onClickDelete}>
<Icon type="trash" className="mr-2 text-danger" /> <Icon type="trash" className="mr-2 text-danger" />
<span className="text-danger">Delete</span> <span className="text-danger">Delete</span>
</MenuItem> </MenuItem>