mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-01 00:07:01 +00:00
fix: give sheet close buttons, chips and small text buttons a full-size touch target
This commit is contained in:
@@ -64,7 +64,7 @@ export function IdentitySheet({
|
||||
</View>
|
||||
<View style={styles.sheetHeader}>
|
||||
<Text style={styles.sheetTitle}>{t('email_composer.from', 'From')}</Text>
|
||||
<Pressable onPress={onClose} hitSlop={8} style={styles.sheetClose}
|
||||
<Pressable onPress={onClose} hitSlop={10} style={styles.sheetClose}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
>
|
||||
|
||||
@@ -96,7 +96,7 @@ export function MoveSheet({
|
||||
</View>
|
||||
<View style={styles.sheetHeader}>
|
||||
<Text style={styles.sheetTitle}>{t('context_menu.move_to', 'Move to folder')}</Text>
|
||||
<Pressable onPress={onClose} hitSlop={8} style={styles.sheetClose}
|
||||
<Pressable onPress={onClose} hitSlop={10} style={styles.sheetClose}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
>
|
||||
|
||||
@@ -150,7 +150,7 @@ export function PushOnboardingPrompt(): React.ReactElement | null {
|
||||
</View>
|
||||
<Pressable
|
||||
onPress={handleDismiss}
|
||||
hitSlop={8}
|
||||
hitSlop={14}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('settings.notifications.push.dismiss_aria', 'Dismiss notification prompt')}
|
||||
style={styles.close}
|
||||
|
||||
@@ -87,7 +87,7 @@ export function TagSheet({ visible, onClose, keywords, selectedEmails, onToggle
|
||||
<Text style={styles.title}>
|
||||
{t('context_menu.items_selected', `${selectedEmails.length} emails selected`, { count: selectedEmails.length })}
|
||||
</Text>
|
||||
<Pressable onPress={onClose} hitSlop={8} style={styles.close}
|
||||
<Pressable onPress={onClose} hitSlop={10} style={styles.close}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
>
|
||||
|
||||
@@ -83,7 +83,7 @@ export function TemplateSheet({ visible, onClose, onPick }: TemplateSheetProps)
|
||||
</View>
|
||||
<View style={styles.sheetHeader}>
|
||||
<Text style={styles.sheetTitle}>{t('templates.picker_title', 'Choose a Template')}</Text>
|
||||
<Pressable onPress={onClose} hitSlop={8} style={styles.sheetClose}
|
||||
<Pressable onPress={onClose} hitSlop={10} style={styles.sheetClose}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
>
|
||||
|
||||
@@ -57,6 +57,7 @@ import {
|
||||
import { getEventEditability } from '../../lib/calendar-editability';
|
||||
import { useContactNameResolver } from '../../lib/contact-name-resolver';
|
||||
import { useCalendarLocale } from '../../lib/calendar-locale';
|
||||
import { formatCalendarDate } from '../../lib/date-format';
|
||||
import { useSheetDrag } from '../../lib/use-sheet-drag';
|
||||
import { recurrenceEndLabel, recurrenceIntervalLabel } from '../../lib/recurrence';
|
||||
import type { TranslateFn } from '../../stores/locale-store';
|
||||
@@ -95,9 +96,9 @@ function formatRange(event: CalendarEvent, timeFormat: TimeFormat | undefined, l
|
||||
start.getMonth() === end.getMonth() &&
|
||||
start.getDate() === end.getDate()
|
||||
) {
|
||||
return format(start, 'EEEE, MMM d, yyyy', opts);
|
||||
return formatCalendarDate(start, locale.code, 'longWeekdayMonthDayYear');
|
||||
}
|
||||
return `${format(start, 'MMM d', opts)} – ${format(end, 'MMM d, yyyy', opts)}`;
|
||||
return `${formatCalendarDate(start, locale.code, 'monthDay')} – ${formatCalendarDate(end, locale.code, 'monthDayYear')}`;
|
||||
}
|
||||
const tp = timePattern(timeFormat);
|
||||
const sameDay =
|
||||
@@ -105,9 +106,9 @@ function formatRange(event: CalendarEvent, timeFormat: TimeFormat | undefined, l
|
||||
start.getMonth() === end.getMonth() &&
|
||||
start.getDate() === end.getDate();
|
||||
if (sameDay) {
|
||||
return `${format(start, 'EEE, MMM d', opts)} · ${format(start, tp, opts)} – ${format(end, tp, opts)}`;
|
||||
return `${formatCalendarDate(start, locale.code, 'weekdayMonthDay')} · ${format(start, tp, opts)} – ${format(end, tp, opts)}`;
|
||||
}
|
||||
return `${format(start, `MMM d, ${tp}`, opts)} – ${format(end, `MMM d, ${tp}`, opts)}`;
|
||||
return `${formatCalendarDate(start, locale.code, 'monthDay')}, ${format(start, tp, opts)} – ${formatCalendarDate(end, locale.code, 'monthDay')}, ${format(end, tp, opts)}`;
|
||||
}
|
||||
|
||||
function recurrenceLabel(event: CalendarEvent, t: TranslateFn, locale: Locale): string | null {
|
||||
@@ -276,7 +277,7 @@ export function EventDetailSheet({
|
||||
<Pressable
|
||||
onPress={onClose}
|
||||
style={styles.closeBtn}
|
||||
hitSlop={8}
|
||||
hitSlop={10}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
>
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
} from '../../lib/task-editor';
|
||||
import { isWritableCalendar } from '../../lib/calendar-editability';
|
||||
import { useCalendarLocale } from '../../lib/calendar-locale';
|
||||
import { formatCalendarDate } from '../../lib/date-format';
|
||||
import { useSheetDrag } from '../../lib/use-sheet-drag';
|
||||
import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations';
|
||||
import { useSheetInsets } from '../../lib/use-layout';
|
||||
@@ -213,8 +214,8 @@ export function TasksSheet({
|
||||
if (!d || !task.due) return null;
|
||||
const hasTime = !task.showWithoutTime && !/^\d{4}-\d{2}-\d{2}$/.test(task.due);
|
||||
return hasTime
|
||||
? format(d, `EEE, MMM d · ${timePattern(timeFormat)}`, { locale })
|
||||
: format(d, 'EEE, MMM d', { locale });
|
||||
? `${formatCalendarDate(d, locale.code, 'weekdayMonthDay')} · ${format(d, timePattern(timeFormat), { locale })}`
|
||||
: formatCalendarDate(d, locale.code, 'weekdayMonthDay');
|
||||
};
|
||||
|
||||
const priorityColor = (level: PriorityLevel): string =>
|
||||
@@ -251,7 +252,7 @@ export function TasksSheet({
|
||||
<Pressable
|
||||
onPress={onClose}
|
||||
style={styles.closeBtn}
|
||||
hitSlop={8}
|
||||
hitSlop={10}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
>
|
||||
@@ -299,7 +300,7 @@ export function TasksSheet({
|
||||
>
|
||||
<CalendarIcon size={14} color={editor.due ? c.primary : c.textMuted} />
|
||||
<Text style={[styles.chipText, editor.due && styles.chipTextActive]}>
|
||||
{editor.due ? format(editor.due, 'MMM d', { locale }) : t('calendar.tasks.due_date', 'Due date')}
|
||||
{editor.due ? formatCalendarDate(editor.due, locale.code, 'monthDay') : t('calendar.tasks.due_date', 'Due date')}
|
||||
</Text>
|
||||
{editor.due && (
|
||||
<Pressable
|
||||
|
||||
@@ -92,7 +92,7 @@ export default function AddressBookPickerSheet({
|
||||
<Text style={styles.title}>{title ?? t('contacts.bulk.move_to_address_book', 'Move to address book')}</Text>
|
||||
<Pressable
|
||||
onPress={onClose}
|
||||
hitSlop={8}
|
||||
hitSlop={10}
|
||||
style={styles.close}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
|
||||
@@ -82,7 +82,7 @@ export default function TagAssignSheet({
|
||||
<Text style={styles.title}>{title ?? t('contacts.bulk.add_tag', 'Add tag')}</Text>
|
||||
<Pressable
|
||||
onPress={onClose}
|
||||
hitSlop={8}
|
||||
hitSlop={10}
|
||||
style={styles.close}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
|
||||
@@ -78,7 +78,7 @@ export function ActionSheet({ visible, title, subtitle, items, onClose, children
|
||||
<Text style={styles.title} numberOfLines={1}>{title}</Text>
|
||||
{subtitle ? <Text style={styles.subtitle} numberOfLines={1}>{subtitle}</Text> : null}
|
||||
</View>
|
||||
<Pressable onPress={onClose} hitSlop={8} style={styles.close}
|
||||
<Pressable onPress={onClose} hitSlop={10} style={styles.close}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
>
|
||||
|
||||
@@ -74,15 +74,15 @@ export function ThreadMessageCard({
|
||||
<MessageContent {...content} compact={false} onToggleStar={bare ? undefined : onToggleStar} />
|
||||
{!bare && (
|
||||
<View style={[styles.actions, !replyable && styles.actionsDisabled]}>
|
||||
<Pressable style={styles.actionBtn} onPress={reply('reply')} hitSlop={4}>
|
||||
<Pressable style={styles.actionBtn} onPress={reply('reply')} hitSlop={9}>
|
||||
<Reply size={16} color={c.textSecondary} />
|
||||
<Text style={styles.actionLabel}>{t('email_viewer.reply', 'Reply')}</Text>
|
||||
</Pressable>
|
||||
<Pressable style={styles.actionBtn} onPress={reply('replyAll')} hitSlop={4}>
|
||||
<Pressable style={styles.actionBtn} onPress={reply('replyAll')} hitSlop={9}>
|
||||
<ReplyAll size={16} color={c.textSecondary} />
|
||||
<Text style={styles.actionLabel}>{t('email_viewer.reply_all', 'Reply All')}</Text>
|
||||
</Pressable>
|
||||
<Pressable style={styles.actionBtn} onPress={reply('forward')} hitSlop={4}>
|
||||
<Pressable style={styles.actionBtn} onPress={reply('forward')} hitSlop={9}>
|
||||
<Forward size={16} color={c.textSecondary} />
|
||||
<Text style={styles.actionLabel}>{t('email_viewer.forward', 'Forward')}</Text>
|
||||
</Pressable>
|
||||
|
||||
@@ -137,7 +137,7 @@ export function DeviceSyncCollectionsSheet({ visible, onClose, registryId, autho
|
||||
</View>
|
||||
<Pressable
|
||||
onPress={close}
|
||||
hitSlop={8}
|
||||
hitSlop={10}
|
||||
style={styles.close}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
|
||||
@@ -2282,12 +2282,12 @@ export default function ComposeScreen({ route, navigation }: Props) {
|
||||
/>
|
||||
)}
|
||||
{field === 'to' && !ccVisible && (
|
||||
<Pressable onPress={() => setCcVisible(true)} style={styles.ccToggle}>
|
||||
<Pressable onPress={() => setCcVisible(true)} style={styles.ccToggle} hitSlop={10} accessibilityRole="button">
|
||||
<Text style={styles.ccToggleText}>{t('email_composer.cc', 'Cc')}</Text>
|
||||
</Pressable>
|
||||
)}
|
||||
{field === 'cc' && !bccVisible && (
|
||||
<Pressable onPress={() => setBccVisible(true)} style={styles.ccToggle}>
|
||||
<Pressable onPress={() => setBccVisible(true)} style={styles.ccToggle} hitSlop={10} accessibilityRole="button">
|
||||
<Text style={styles.ccToggleText}>{t('email_composer.bcc', 'Bcc')}</Text>
|
||||
</Pressable>
|
||||
)}
|
||||
|
||||
@@ -536,6 +536,7 @@ export default function ContactsScreen({ onOpenContact, activeContactId = null }
|
||||
key={chip.key}
|
||||
onPress={() => setSelectedCategory(chip.category)}
|
||||
style={[styles.chip, chip.active && styles.chipActive]}
|
||||
hitSlop={{ top: 6, bottom: 6 }}
|
||||
accessibilityRole="button"
|
||||
accessibilityState={{ selected: chip.active }}
|
||||
>
|
||||
@@ -718,15 +719,20 @@ function makeStyles(c: ThemePalette) {
|
||||
|
||||
// flexShrink 0: in a short (landscape) window the column squeezed the
|
||||
// horizontal scroller and cut the chips off at the bottom.
|
||||
chipBar: { flexGrow: 0, flexShrink: 0, marginBottom: spacing.sm },
|
||||
chipBar: { flexGrow: 0, flexShrink: 0, marginBottom: 2 },
|
||||
// The vertical padding leaves room for the chips' slop, which makes them
|
||||
// 48 dp targets (they were about 28 dp).
|
||||
chipBarContent: {
|
||||
paddingHorizontal: spacing.lg,
|
||||
paddingVertical: 6,
|
||||
gap: spacing.sm,
|
||||
alignItems: 'center',
|
||||
},
|
||||
chip: {
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.xs,
|
||||
minHeight: 36,
|
||||
justifyContent: 'center',
|
||||
borderRadius: radius.full,
|
||||
backgroundColor: c.surface,
|
||||
borderWidth: 1,
|
||||
|
||||
@@ -1305,7 +1305,7 @@ export default function EmailListScreen({
|
||||
</Pressable>
|
||||
<Pressable
|
||||
onPress={() => removeRecentSearch(q)}
|
||||
hitSlop={8}
|
||||
hitSlop={18}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('advanced_search.suggestions_remove_recent', 'Remove from recent searches')}
|
||||
>
|
||||
@@ -1600,7 +1600,7 @@ export default function EmailListScreen({
|
||||
<Pressable
|
||||
onPress={() => setFilterMenuOpen(false)}
|
||||
style={styles.filterMenuClose}
|
||||
hitSlop={6}
|
||||
hitSlop={10}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
>
|
||||
@@ -1702,7 +1702,7 @@ export default function EmailListScreen({
|
||||
{filters.dateAfter ? (
|
||||
<Pressable
|
||||
onPress={() => setFilterField('dateAfter', undefined)}
|
||||
hitSlop={6}
|
||||
hitSlop={12}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('advanced_search.clear', 'Clear')}
|
||||
>
|
||||
@@ -1724,7 +1724,7 @@ export default function EmailListScreen({
|
||||
{filters.dateBefore ? (
|
||||
<Pressable
|
||||
onPress={() => setFilterField('dateBefore', undefined)}
|
||||
hitSlop={6}
|
||||
hitSlop={12}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('advanced_search.clear', 'Clear')}
|
||||
>
|
||||
|
||||
@@ -1356,7 +1356,7 @@ function MoreMenuSheet({
|
||||
<Text style={styles.sheetTitle}>{t('email_viewer.more_actions', 'More actions')}</Text>
|
||||
<Pressable
|
||||
onPress={onClose}
|
||||
hitSlop={8}
|
||||
hitSlop={10}
|
||||
style={styles.sheetClose}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
@@ -1507,7 +1507,7 @@ function TagMenuSheet({ visible, onClose, keywords, activeKeywords, onToggle }:
|
||||
<Text style={styles.sheetTitle}>{t('email_viewer.tag', 'Tag')}</Text>
|
||||
<Pressable
|
||||
onPress={onClose}
|
||||
hitSlop={8}
|
||||
hitSlop={10}
|
||||
style={styles.sheetClose}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.close', 'Close')}
|
||||
|
||||
@@ -73,6 +73,7 @@ export default function EmailStep({
|
||||
key={address}
|
||||
onPress={() => onChange(address)}
|
||||
style={styles.suggestion}
|
||||
hitSlop={4}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={address}
|
||||
>
|
||||
@@ -108,7 +109,9 @@ function makeStyles(c: ThemePalette) {
|
||||
borderColor: c.border,
|
||||
borderRadius: 999,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.xs,
|
||||
// 40 dp plus the slop: the chips were about 26 dp tall.
|
||||
minHeight: 40,
|
||||
justifyContent: 'center',
|
||||
maxWidth: '100%',
|
||||
},
|
||||
suggestionText: { ...typography.caption, color: c.textSecondary },
|
||||
|
||||
Reference in New Issue
Block a user