fix(mobile): increase font sizes and other mobile-centric improvements (#1907)
This commit is contained in:
@@ -6,14 +6,23 @@ import { NotesController } from '@/Controllers/NotesController'
|
||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||
import Popover from '../Popover/Popover'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { IconType } from '@standardnotes/snjs'
|
||||
|
||||
type Props = {
|
||||
navigationController: NavigationController
|
||||
notesController: NotesController
|
||||
linkingController: LinkingController
|
||||
className: string
|
||||
iconClassName: string
|
||||
}
|
||||
|
||||
const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesController, linkingController }) => {
|
||||
const AddTagOption: FunctionComponent<Props> = ({
|
||||
navigationController,
|
||||
notesController,
|
||||
linkingController,
|
||||
className,
|
||||
iconClassName,
|
||||
}) => {
|
||||
const menuContainerRef = useRef<HTMLDivElement>(null)
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
@@ -33,10 +42,10 @@ const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesCon
|
||||
}
|
||||
}}
|
||||
ref={buttonRef}
|
||||
className="flex w-full cursor-pointer items-center justify-between 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"
|
||||
className={className}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<Icon type="hashtag" className="mr-2 text-neutral" />
|
||||
<Icon type="hashtag" className={`${iconClassName} mr-2 text-neutral`} />
|
||||
Add tag
|
||||
</div>
|
||||
<Icon type="chevron-right" className="text-neutral" />
|
||||
@@ -52,13 +61,20 @@ const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesCon
|
||||
{navigationController.tags.map((tag) => (
|
||||
<button
|
||||
key={tag.uuid}
|
||||
className="max-w-80 flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 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"
|
||||
className={`max-w-80 ${className.replace('justify-between', 'justify-start')}`}
|
||||
onClick={() => {
|
||||
notesController.isTagInSelectedNotes(tag)
|
||||
? notesController.removeTagFromSelectedNotes(tag).catch(console.error)
|
||||
: notesController.addTagToSelectedNotes(tag).catch(console.error)
|
||||
}}
|
||||
>
|
||||
{tag.iconString && (
|
||||
<Icon
|
||||
type={tag.iconString as IconType}
|
||||
size={'custom'}
|
||||
className={'ml-0.5 mr-1.5 h-7 w-7 text-2xl text-neutral lg:h-6 lg:w-6 lg:text-lg'}
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
className={`overflow-hidden overflow-ellipsis whitespace-nowrap
|
||||
${notesController.isTagInSelectedNotes(tag) ? 'font-bold' : ''}`}
|
||||
|
||||
@@ -9,9 +9,16 @@ import Popover from '../Popover/Popover'
|
||||
type ChangeEditorOptionProps = {
|
||||
application: WebApplication
|
||||
note: SNNote
|
||||
className: string
|
||||
iconClassName: string
|
||||
}
|
||||
|
||||
const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({ application, note }) => {
|
||||
const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({
|
||||
application,
|
||||
note,
|
||||
className,
|
||||
iconClassName,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const menuContainerRef = useRef<HTMLDivElement>(null)
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
@@ -30,10 +37,10 @@ const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({ applic
|
||||
}
|
||||
}}
|
||||
ref={buttonRef}
|
||||
className="flex w-full cursor-pointer items-center justify-between 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"
|
||||
className={className}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<Icon type="dashboard" className="mr-2 text-neutral" />
|
||||
<Icon type="dashboard" className={`${iconClassName} mr-2 text-neutral`} />
|
||||
Change note type
|
||||
</div>
|
||||
<Icon type="chevron-right" className="text-neutral" />
|
||||
|
||||
@@ -9,9 +9,11 @@ import Popover from '../Popover/Popover'
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
note: SNNote
|
||||
className: string
|
||||
iconClassName: string
|
||||
}
|
||||
|
||||
const ListedActionsOption: FunctionComponent<Props> = ({ application, note }) => {
|
||||
const ListedActionsOption: FunctionComponent<Props> = ({ application, note, className, iconClassName }) => {
|
||||
const menuContainerRef = useRef<HTMLDivElement>(null)
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
@@ -37,10 +39,10 @@ const ListedActionsOption: FunctionComponent<Props> = ({ application, note }) =>
|
||||
}
|
||||
}}
|
||||
ref={buttonRef}
|
||||
className="flex w-full cursor-pointer items-center justify-between 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"
|
||||
className={className}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<Icon type="listed" className="mr-2 text-neutral" />
|
||||
<Icon type="listed" className={`mr-2 text-neutral ${iconClassName}`} />
|
||||
Listed actions
|
||||
</div>
|
||||
<Icon type="chevron-right" className="text-neutral" />
|
||||
|
||||
@@ -41,17 +41,19 @@ const ListedMenuItem: FunctionComponent<ListedMenuItemProps> = ({
|
||||
onClick={handleClick}
|
||||
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"
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<div className="font-semibold">{action.label}</div>
|
||||
{action.access_type && (
|
||||
<div className="mt-0.5 text-xs text-passive-0">
|
||||
{'Uses '}
|
||||
<strong>{action.access_type}</strong>
|
||||
{' access to this note.'}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex w-full flex-row items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<div className="font-semibold">{action.label}</div>
|
||||
{action.access_type && (
|
||||
<div className="mt-0.5 text-xs text-passive-0">
|
||||
{'Uses '}
|
||||
<strong>{action.access_type}</strong>
|
||||
{' access to this note.'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isRunning && <Spinner className="h-3 w-3" />}
|
||||
</div>
|
||||
{isRunning && <Spinner className="h-3 w-3" />}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import { getNoteBlob, getNoteFileName } from '@/Utils/NoteExportUtils'
|
||||
import { shareSelectedNotes } from '@/NativeMobileWeb/ShareSelectedNotes'
|
||||
import { downloadSelectedNotesOnAndroid } from '@/NativeMobileWeb/DownloadSelectedNotesOnAndroid'
|
||||
import ProtectedUnauthorizedLabel from '../ProtectedItemOverlay/ProtectedUnauthorizedLabel'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
|
||||
|
||||
type DeletePermanentlyButtonProps = {
|
||||
onClick: () => void
|
||||
@@ -34,10 +36,11 @@ const DeletePermanentlyButton = ({ onClick }: DeletePermanentlyButtonProps) => (
|
||||
</button>
|
||||
)
|
||||
|
||||
const iconClass = 'text-neutral mr-2'
|
||||
const iconClassDanger = 'text-danger mr-2'
|
||||
const iconClassWarning = 'text-warning mr-2'
|
||||
const iconClassSuccess = 'text-success mr-2'
|
||||
const iconSize = MenuItemIconSize
|
||||
const iconClass = `text-neutral mr-2 ${iconSize}`
|
||||
const iconClassDanger = `text-danger mr-2 ${iconSize}`
|
||||
const iconClassWarning = `text-warning mr-2 ${iconSize}`
|
||||
const iconClassSuccess = `text-success mr-2 ${iconSize}`
|
||||
|
||||
const getWordCount = (text: string) => {
|
||||
if (text.trim().length === 0) {
|
||||
@@ -99,7 +102,7 @@ const NoteAttributes: FunctionComponent<{
|
||||
const format = editor?.package_info?.file_type || 'txt'
|
||||
|
||||
return (
|
||||
<div className="select-text px-3 py-1.5 text-xs font-medium text-neutral">
|
||||
<div className="select-text px-3 py-1.5 text-sm font-medium text-neutral lg:text-xs">
|
||||
{typeof words === 'number' && (format === 'txt' || format === 'md') ? (
|
||||
<>
|
||||
<div className="mb-1">
|
||||
@@ -127,7 +130,8 @@ const SpellcheckOptions: FunctionComponent<{
|
||||
editorForNote: SNComponent | undefined
|
||||
notesController: NotesController
|
||||
note: SNNote
|
||||
}> = ({ editorForNote, notesController, note }) => {
|
||||
className: string
|
||||
}> = ({ editorForNote, notesController, note, className }) => {
|
||||
const spellcheckControllable = Boolean(!editorForNote || editorForNote.package_info.spellcheckControl)
|
||||
const noteSpellcheck = !spellcheckControllable
|
||||
? true
|
||||
@@ -138,7 +142,7 @@ const SpellcheckOptions: FunctionComponent<{
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<button
|
||||
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1 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"
|
||||
className={className}
|
||||
onClick={() => {
|
||||
notesController.toggleGlobalSpellcheckForNote(note).catch(console.error)
|
||||
}}
|
||||
@@ -273,14 +277,20 @@ const NotesOptions = ({
|
||||
return <ProtectedUnauthorizedLabel />
|
||||
}
|
||||
|
||||
const textClassNames = 'text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item'
|
||||
|
||||
const defaultClassNames = classNames(
|
||||
textClassNames,
|
||||
'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',
|
||||
)
|
||||
|
||||
const switchClassNames = classNames(textClassNames, defaultClassNames, 'justify-between')
|
||||
|
||||
return (
|
||||
<>
|
||||
{notes.length === 1 && (
|
||||
<>
|
||||
<button
|
||||
className="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={openRevisionHistoryModal}
|
||||
>
|
||||
<button className={defaultClassNames} onClick={openRevisionHistoryModal}>
|
||||
<Icon type="history" className={iconClass} />
|
||||
Note history
|
||||
</button>
|
||||
@@ -288,7 +298,7 @@ const NotesOptions = ({
|
||||
</>
|
||||
)}
|
||||
<button
|
||||
className="flex w-full cursor-pointer items-center justify-between 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"
|
||||
className={switchClassNames}
|
||||
onClick={() => {
|
||||
notesController.setLockSelectedNotes(!locked)
|
||||
}}
|
||||
@@ -300,7 +310,7 @@ const NotesOptions = ({
|
||||
<Switch className="px-0" checked={locked} />
|
||||
</button>
|
||||
<button
|
||||
className="flex w-full cursor-pointer items-center justify-between 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"
|
||||
className={switchClassNames}
|
||||
onClick={() => {
|
||||
notesController.setHideSelectedNotePreviews(!hidePreviews)
|
||||
}}
|
||||
@@ -312,7 +322,7 @@ const NotesOptions = ({
|
||||
<Switch className="px-0" checked={!hidePreviews} />
|
||||
</button>
|
||||
<button
|
||||
className="flex w-full cursor-pointer items-center justify-between 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"
|
||||
className={switchClassNames}
|
||||
onClick={() => {
|
||||
notesController.setProtectSelectedNotes(!protect).catch(console.error)
|
||||
}}
|
||||
@@ -326,12 +336,19 @@ const NotesOptions = ({
|
||||
{notes.length === 1 && (
|
||||
<>
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
<ChangeEditorOption application={application} note={notes[0]} />
|
||||
<ChangeEditorOption
|
||||
iconClassName={iconClass}
|
||||
className={switchClassNames}
|
||||
application={application}
|
||||
note={notes[0]}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
{navigationController.tagsCount > 0 && (
|
||||
<AddTagOption
|
||||
iconClassName={iconClass}
|
||||
className={switchClassNames}
|
||||
navigationController={navigationController}
|
||||
notesController={notesController}
|
||||
linkingController={linkingController}
|
||||
@@ -339,7 +356,7 @@ const NotesOptions = ({
|
||||
)}
|
||||
|
||||
<button
|
||||
className="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"
|
||||
className={defaultClassNames}
|
||||
onClick={() => {
|
||||
notesController.setStarSelectedNotes(!starred)
|
||||
}}
|
||||
@@ -350,7 +367,7 @@ const NotesOptions = ({
|
||||
|
||||
{unpinned && (
|
||||
<button
|
||||
className="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"
|
||||
className={defaultClassNames}
|
||||
onClick={() => {
|
||||
notesController.setPinSelectedNotes(true)
|
||||
}}
|
||||
@@ -361,7 +378,7 @@ const NotesOptions = ({
|
||||
)}
|
||||
{pinned && (
|
||||
<button
|
||||
className="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"
|
||||
className={defaultClassNames}
|
||||
onClick={() => {
|
||||
notesController.setPinSelectedNotes(false)
|
||||
}}
|
||||
@@ -371,7 +388,7 @@ const NotesOptions = ({
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="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"
|
||||
className={defaultClassNames}
|
||||
onClick={() => {
|
||||
application.isNativeMobileWeb() ? shareSelectedNotes(application, notes) : downloadSelectedItems()
|
||||
}}
|
||||
@@ -380,24 +397,18 @@ const NotesOptions = ({
|
||||
{application.platform === Platform.Android ? 'Share' : 'Export'}
|
||||
</button>
|
||||
{application.platform === Platform.Android && (
|
||||
<button
|
||||
className="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={() => downloadSelectedNotesOnAndroid(application, notes)}
|
||||
>
|
||||
<button className={defaultClassNames} onClick={() => downloadSelectedNotesOnAndroid(application, notes)}>
|
||||
<Icon type="download" className={iconClass} />
|
||||
Export
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="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={duplicateSelectedItems}
|
||||
>
|
||||
<button className={defaultClassNames} onClick={duplicateSelectedItems}>
|
||||
<Icon type="copy" className={iconClass} />
|
||||
Duplicate
|
||||
</button>
|
||||
{unarchived && (
|
||||
<button
|
||||
className="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"
|
||||
className={defaultClassNames}
|
||||
onClick={async () => {
|
||||
await notesController.setArchiveSelectedNotes(true).catch(console.error)
|
||||
closeMenuAndToggleNotesList()
|
||||
@@ -409,7 +420,7 @@ const NotesOptions = ({
|
||||
)}
|
||||
{archived && (
|
||||
<button
|
||||
className="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"
|
||||
className={defaultClassNames}
|
||||
onClick={async () => {
|
||||
await notesController.setArchiveSelectedNotes(false).catch(console.error)
|
||||
closeMenuAndToggleNotesList()
|
||||
@@ -429,7 +440,7 @@ const NotesOptions = ({
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
className="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"
|
||||
className={defaultClassNames}
|
||||
onClick={async () => {
|
||||
await notesController.setTrashSelectedNotes(true)
|
||||
closeMenuAndToggleNotesList()
|
||||
@@ -442,7 +453,7 @@ const NotesOptions = ({
|
||||
{trashed && (
|
||||
<>
|
||||
<button
|
||||
className="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"
|
||||
className={defaultClassNames}
|
||||
onClick={async () => {
|
||||
await notesController.setTrashSelectedNotes(false)
|
||||
closeMenuAndToggleNotesList()
|
||||
@@ -458,7 +469,7 @@ const NotesOptions = ({
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
className="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"
|
||||
className={defaultClassNames}
|
||||
onClick={async () => {
|
||||
await notesController.emptyTrash()
|
||||
closeMenuAndToggleNotesList()
|
||||
@@ -474,14 +485,31 @@ const NotesOptions = ({
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{notes.length === 1 ? (
|
||||
<>
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
<ListedActionsOption application={application} note={notes[0]} />
|
||||
|
||||
<ListedActionsOption
|
||||
iconClassName={iconClass}
|
||||
className={switchClassNames}
|
||||
application={application}
|
||||
note={notes[0]}
|
||||
/>
|
||||
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
<SpellcheckOptions editorForNote={editorForNote} notesController={notesController} note={notes[0]} />
|
||||
|
||||
<SpellcheckOptions
|
||||
className={switchClassNames}
|
||||
editorForNote={editorForNote}
|
||||
notesController={notesController}
|
||||
note={notes[0]}
|
||||
/>
|
||||
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
|
||||
<NoteAttributes application={application} note={notes[0]} />
|
||||
|
||||
<NoteSizeWarning note={notes[0]} />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
@@ -40,7 +40,7 @@ const NotesOptionsPanel = ({
|
||||
return (
|
||||
<>
|
||||
<RoundIconButton label="Note options menu" onClick={toggleMenu} ref={buttonRef} icon="more" />
|
||||
<Popover togglePopover={toggleMenu} anchorElement={buttonRef.current} open={isOpen} className="select-none py-2">
|
||||
<Popover togglePopover={toggleMenu} anchorElement={buttonRef.current} open={isOpen} className="select-none">
|
||||
<NotesOptions
|
||||
application={application}
|
||||
navigationController={navigationController}
|
||||
|
||||
Reference in New Issue
Block a user