From b09d6e856886ff2a0bf092d713cb1335200e2a14 Mon Sep 17 00:00:00 2001 From: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com> Date: Sat, 25 May 2024 06:41:27 -0700 Subject: [PATCH] Chore: Refactor: Mobile: Rename CustomButton to IconButton (#10473) --- .eslintignore | 2 +- .gitignore | 2 +- packages/app-mobile/components/Icon.tsx | 8 +- .../{CustomButton.tsx => IconButton.tsx} | 138 +++++++++--------- .../MarkdownToolbar/ToggleOverflowButton.tsx | 2 +- .../MarkdownToolbar/ToggleSpaceButton.tsx | 17 +-- .../MarkdownToolbar/ToolbarButton.tsx | 15 +- .../buttons/useActionButtons.ts | 4 +- .../components/NoteEditor/SearchPanel.tsx | 26 ++-- .../components/ScreenHeader/index.tsx | 84 ++++++----- packages/tools/cspell/dictionary4.txt | 1 + 11 files changed, 148 insertions(+), 151 deletions(-) rename packages/app-mobile/components/{CustomButton.tsx => IconButton.tsx} (56%) diff --git a/.eslintignore b/.eslintignore index 3f9c753e3..3bfe95b0b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -510,13 +510,13 @@ packages/app-mobile/commands/util/goToNote.js packages/app-mobile/components/ActionButton.js packages/app-mobile/components/BackButtonDialogBox.js packages/app-mobile/components/CameraView.js -packages/app-mobile/components/CustomButton.js packages/app-mobile/components/DismissibleDialog.js packages/app-mobile/components/Dropdown.test.js packages/app-mobile/components/Dropdown.js packages/app-mobile/components/ExtendedWebView.js packages/app-mobile/components/FolderPicker.js packages/app-mobile/components/Icon.js +packages/app-mobile/components/IconButton.js packages/app-mobile/components/Modal.js packages/app-mobile/components/ModalDialog.js packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js diff --git a/.gitignore b/.gitignore index a173867be..187a395b4 100644 --- a/.gitignore +++ b/.gitignore @@ -489,13 +489,13 @@ packages/app-mobile/commands/util/goToNote.js packages/app-mobile/components/ActionButton.js packages/app-mobile/components/BackButtonDialogBox.js packages/app-mobile/components/CameraView.js -packages/app-mobile/components/CustomButton.js packages/app-mobile/components/DismissibleDialog.js packages/app-mobile/components/Dropdown.test.js packages/app-mobile/components/Dropdown.js packages/app-mobile/components/ExtendedWebView.js packages/app-mobile/components/FolderPicker.js packages/app-mobile/components/Icon.js +packages/app-mobile/components/IconButton.js packages/app-mobile/components/Modal.js packages/app-mobile/components/ModalDialog.js packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js diff --git a/packages/app-mobile/components/Icon.tsx b/packages/app-mobile/components/Icon.tsx index a074623e4..1f9ca927b 100644 --- a/packages/app-mobile/components/Icon.tsx +++ b/packages/app-mobile/components/Icon.tsx @@ -4,8 +4,8 @@ import { TextStyle, Text } from 'react-native'; const FontAwesomeIcon = require('react-native-vector-icons/FontAwesome5').default; const AntIcon = require('react-native-vector-icons/AntDesign').default; -const MaterialIcon = require('react-native-vector-icons/MaterialIcons').default; - +const MaterialCommunityIcon = require('react-native-vector-icons/MaterialCommunityIcons').default; +const Ionicon = require('react-native-vector-icons/Ionicons').default; interface Props { name: string; @@ -53,7 +53,9 @@ const Icon: React.FC<Props> = props => { } else if (namePrefix === 'ant') { return <AntIcon name={nameSuffix} {...sharedProps}/>; } else if (namePrefix === 'material') { - return <MaterialIcon name={nameSuffix} {...sharedProps}/>; + return <MaterialCommunityIcon name={nameSuffix} {...sharedProps}/>; + } else if (namePrefix === 'ionicon') { + return <Ionicon name={nameSuffix} {...sharedProps}/>; } else if (namePrefix === 'text') { return ( <Text diff --git a/packages/app-mobile/components/CustomButton.tsx b/packages/app-mobile/components/IconButton.tsx similarity index 56% rename from packages/app-mobile/components/CustomButton.tsx rename to packages/app-mobile/components/IconButton.tsx index 6f006e16f..93f7272d3 100644 --- a/packages/app-mobile/components/CustomButton.tsx +++ b/packages/app-mobile/components/IconButton.tsx @@ -2,28 +2,28 @@ // A button with a long-press action. Long-pressing the button displays a tooltip // -const React = require('react'); -import { ReactNode } from 'react'; +import * as React from 'react'; import { themeStyle } from '@joplin/lib/theme'; import { Theme } from '@joplin/lib/themes/type'; import { useState, useMemo, useCallback, useRef } from 'react'; -import { View, Text, Pressable, ViewStyle, PressableStateCallbackType, StyleProp, StyleSheet, LayoutChangeEvent, LayoutRectangle, Animated, AccessibilityState, AccessibilityRole } from 'react-native'; +import { View, Text, Pressable, ViewStyle, StyleSheet, LayoutChangeEvent, LayoutRectangle, Animated, AccessibilityState, AccessibilityRole, TextStyle } from 'react-native'; import { Menu, MenuOptions, MenuTrigger, renderers } from 'react-native-popup-menu'; +import Icon from './Icon'; type ButtonClickListener = ()=> void; interface ButtonProps { onPress: ButtonClickListener; // Accessibility label and text shown in a tooltip - description?: string; + description: string; - children: ReactNode; + iconName: string; + iconStyle: TextStyle; themeId: number; - style?: ViewStyle; - pressedStyle?: ViewStyle; - contentStyle?: ViewStyle; + containerStyle?: ViewStyle; + contentWrapperStyle?: ViewStyle; // Additional accessibility information. See View.accessibilityHint accessibilityHint?: string; @@ -35,7 +35,7 @@ interface ButtonProps { disabled?: boolean; } -const CustomButton = (props: ButtonProps) => { +const IconButton = (props: ButtonProps) => { const [tooltipVisible, setTooltipVisible] = useState(false); const [buttonLayout, setButtonLayout] = useState<LayoutRectangle|null>(null); const tooltipStyles = useTooltipStyles(props.themeId); @@ -67,19 +67,6 @@ const CustomButton = (props: ButtonProps) => { setTooltipVisible(true); }, []); - // Select different user-specified styles if selected/unselected. - const onStyleChange = useCallback((state: PressableStateCallbackType): StyleProp<ViewStyle> => { - let result = { ...props.style }; - - if (state.pressed) { - result = { - ...result, - ...props.pressedStyle, - }; - } - return result; - }, [props.pressedStyle, props.style]); - const onButtonLayout = useCallback((event: LayoutChangeEvent) => { const layoutEvt = event.nativeEvent.layout; @@ -87,7 +74,6 @@ const CustomButton = (props: ButtonProps) => { setButtonLayout({ ...layoutEvt }); }, []); - const button = ( <Pressable onPress={props.onPress} @@ -95,7 +81,7 @@ const CustomButton = (props: ButtonProps) => { onPressIn={onPressIn} onPressOut={onPressOut} - style={ onStyleChange } + style={ props.containerStyle } disabled={ props.disabled ?? false } onLayout={ onButtonLayout } @@ -107,62 +93,70 @@ const CustomButton = (props: ButtonProps) => { > <Animated.View style={{ opacity: fadeAnim, - ...props.contentStyle, + ...props.contentWrapperStyle, }}> - { props.children } + <Icon + name={props.iconName} + style={props.iconStyle} + accessibilityLabel={null} + /> </Animated.View> </Pressable> ); - const tooltip = ( - <View - // Any information given by the tooltip should also be provided via - // [accessibilityLabel]/[accessibilityHint]. As such, we can hide the tooltip - // from the screen reader. - // On Android: - importantForAccessibility='no-hide-descendants' - // On iOS: - accessibilityElementsHidden={true} + const renderTooltip = () => { + if (!props.description) return null; - // Position the menu beneath the button so the tooltip appears in the - // correct location. - style={{ - left: buttonLayout?.x, - top: buttonLayout?.y, - position: 'absolute', - zIndex: -1, - }} - > - <Menu - opened={tooltipVisible} - renderer={renderers.Popover} - rendererProps={{ - preferredPlacement: 'bottom', - anchorStyle: tooltipStyles.anchor, - }}> - <MenuTrigger - // Don't show/hide when pressed (let the Pressable handle opening/closing) - disabled={true} - style={{ - // Ensure that the trigger region has the same size as the button. - width: buttonLayout?.width ?? 0, - height: buttonLayout?.height ?? 0, - }} - /> - <MenuOptions - customStyles={{ optionsContainer: tooltipStyles.optionsContainer }} - > - <Text style={tooltipStyles.text}> - {props.description} - </Text> - </MenuOptions> - </Menu> - </View> - ); + return ( + <View + // Any information given by the tooltip should also be provided via + // [accessibilityLabel]/[accessibilityHint]. As such, we can hide the tooltip + // from the screen reader. + // On Android: + importantForAccessibility='no-hide-descendants' + // On iOS: + accessibilityElementsHidden={true} + + // Position the menu beneath the button so the tooltip appears in the + // correct location. + style={{ + left: buttonLayout?.x, + top: buttonLayout?.y, + position: 'absolute', + zIndex: -1, + }} + > + <Menu + opened={tooltipVisible} + renderer={renderers.Popover} + rendererProps={{ + preferredPlacement: 'bottom', + anchorStyle: tooltipStyles.anchor, + }}> + <MenuTrigger + // Don't show/hide when pressed (let the Pressable handle opening/closing) + disabled={true} + style={{ + // Ensure that the trigger region has the same size as the button. + width: buttonLayout?.width ?? 0, + height: buttonLayout?.height ?? 0, + }} + /> + <MenuOptions + customStyles={{ optionsContainer: tooltipStyles.optionsContainer }} + > + <Text style={tooltipStyles.text}> + {props.description} + </Text> + </MenuOptions> + </Menu> + </View> + ); + }; return ( <> - {props.description ? tooltip : null} + {renderTooltip()} {button} </> ); @@ -187,4 +181,4 @@ const useTooltipStyles = (themeId: number) => { }, [themeId]); }; -export default CustomButton; +export default IconButton; diff --git a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleOverflowButton.tsx b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleOverflowButton.tsx index b528dbb34..4f7b6afba 100644 --- a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleOverflowButton.tsx +++ b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleOverflowButton.tsx @@ -14,7 +14,7 @@ interface ToggleOverflowButtonProps { // Button that shows/hides the overflow menu. const ToggleOverflowButton: React.FC<ToggleOverflowButtonProps> = (props: ToggleOverflowButtonProps) => { const spec: ButtonSpec = { - icon: 'material more-horiz', + icon: 'material dots-horizontal', description: props.overflowVisible ? _('Hide more actions') : _('Show more actions'), active: props.overflowVisible, diff --git a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleSpaceButton.tsx b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleSpaceButton.tsx index e1f316fd4..7a486a241 100644 --- a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleSpaceButton.tsx +++ b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleSpaceButton.tsx @@ -14,9 +14,7 @@ import { Theme } from '@joplin/lib/themes/type'; import * as React from 'react'; import { ReactNode, useCallback, useState, useEffect } from 'react'; import { View, ViewStyle } from 'react-native'; -import CustomButton from '../../CustomButton'; - -const AntIcon = require('react-native-vector-icons/AntDesign').default; +import IconButton from '../../IconButton'; interface Props { children: ReactNode; @@ -54,10 +52,10 @@ const ToggleSpaceButton = (props: Props) => { height: additionalPositiveSpace, zIndex: -2, }} /> - <CustomButton + <IconButton themeId={props.themeId} description={'Move toolbar to bottom of screen'} - style={{ + containerStyle={{ height: additionalPositiveSpace, width: '100%', @@ -72,11 +70,10 @@ const ToggleSpaceButton = (props: Props) => { alignItems: 'center', }} onPress={onDecreaseSpace} - > - <AntIcon name='down' style={{ - color: theme.color, - }}/> - </CustomButton> + + iconName='material chevron-down' + iconStyle={{ color: theme.color }} + /> </> ); diff --git a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.tsx b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.tsx index 17272890b..3ec396d25 100644 --- a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.tsx +++ b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.tsx @@ -2,8 +2,7 @@ import * as React from 'react'; import { useCallback, useMemo } from 'react'; import { TextStyle, StyleSheet } from 'react-native'; import { ButtonSpec, StyleSheetData } from './types'; -import CustomButton from '../../CustomButton'; -import Icon from '../../Icon'; +import IconButton from '../../IconButton'; export const buttonSize = 54; @@ -58,16 +57,16 @@ const ToolbarButton = ({ styleSheet, spec, onActionComplete, style }: ToolbarBut }, [disabled, sourceOnPress, onActionComplete]); return ( - <CustomButton - style={styles.buttonStyle} + <IconButton + containerStyle={styles.buttonStyle} themeId={styleSheet.themeId} onPress={onPress} description={ spec.description } - accessibilityRole="button" disabled={ disabled } - > - <Icon name={spec.icon} style={styles.iconStyle} accessibilityLabel={null}/> - </CustomButton> + + iconName={spec.icon} + iconStyle={styles.iconStyle} + /> ); }; diff --git a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useActionButtons.ts b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useActionButtons.ts index 47d7afaa5..330c62434 100644 --- a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useActionButtons.ts +++ b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useActionButtons.ts @@ -51,7 +51,7 @@ const useActionButtons = (props: ActionButtonRowProps) => { }); actionButtons.push({ - icon: 'material search', + icon: 'material magnify', description: ( props.searchState.dialogVisible ? _('Close') : _('Find and replace') ), @@ -63,7 +63,7 @@ const useActionButtons = (props: ActionButtonRowProps) => { }); actionButtons.push({ - icon: 'material keyboard-hide', + icon: 'material keyboard-close', description: _('Hide keyboard'), disabled: !props.keyboardVisible, visible: props.hasSoftwareKeyboard && Platform.OS === 'ios', diff --git a/packages/app-mobile/components/NoteEditor/SearchPanel.tsx b/packages/app-mobile/components/NoteEditor/SearchPanel.tsx index ddbb9bf21..e6b4a22b9 100644 --- a/packages/app-mobile/components/NoteEditor/SearchPanel.tsx +++ b/packages/app-mobile/components/NoteEditor/SearchPanel.tsx @@ -2,13 +2,12 @@ const React = require('react'); const { useMemo, useState, useEffect } = require('react'); -const MaterialCommunityIcon = require('react-native-vector-icons/MaterialCommunityIcons').default; import { EditorSettings } from './types'; import { _ } from '@joplin/lib/locale'; import { BackHandler, TextInput, View, Text, StyleSheet, ViewStyle } from 'react-native'; import { Theme } from '@joplin/lib/themes/type'; -import CustomButton from '../CustomButton'; +import IconButton from '../IconButton'; import { SearchState } from '@joplin/editor/types'; import { SearchControl } from './types'; @@ -43,14 +42,14 @@ interface ActionButtonProps { const ActionButton = (props: ActionButtonProps) => { return ( - <CustomButton + <IconButton themeId={props.themeId} - style={props.styles.button} + containerStyle={props.styles.button} onPress={props.onPress} description={props.title} - > - <MaterialCommunityIcon name={props.iconName} style={props.styles.buttonText}/> - </CustomButton> + iconName={`material ${props.iconName}`} + iconStyle={props.styles.buttonText} + /> ); }; @@ -68,9 +67,9 @@ const ToggleButton = (props: ToggleButtonProps) => { const active = props.active; return ( - <CustomButton + <IconButton themeId={props.themeId} - style={{ + containerStyle={{ ...props.styles.toggleButton, ...(active ? props.styles.toggleButtonActive : {}), }} @@ -81,11 +80,12 @@ const ToggleButton = (props: ToggleButtonProps) => { }} description={props.title} accessibilityRole='switch' - > - <MaterialCommunityIcon name={props.iconName} style={ + + iconName={`material ${props.iconName}`} + iconStyle={ active ? props.styles.activeButtonText : props.styles.buttonText - }/> - </CustomButton> + } + /> ); }; diff --git a/packages/app-mobile/components/ScreenHeader/index.tsx b/packages/app-mobile/components/ScreenHeader/index.tsx index b6132d001..78ac67dc9 100644 --- a/packages/app-mobile/components/ScreenHeader/index.tsx +++ b/packages/app-mobile/components/ScreenHeader/index.tsx @@ -15,7 +15,7 @@ const { dialogs } = require('../../utils/dialogs.js'); const DialogBox = require('react-native-dialogbox').default; import { FolderEntity } from '@joplin/lib/services/database/types'; import { State } from '@joplin/lib/reducer'; -import CustomButton from '../CustomButton'; +import IconButton from '../IconButton'; import FolderPicker from '../FolderPicker'; import { itemIsInTrash } from '@joplin/lib/services/trash'; import restoreItems from '@joplin/lib/services/trash/restoreItems'; @@ -363,26 +363,25 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade const renderTopButton = (options: TopButtonOptions) => { if (!options.visible) return null; - const icon = <Icon name={options.iconName} style={this.styles().topIcon} />; const viewStyle = options.disabled ? this.styles().iconButtonDisabled : this.styles().iconButton; return ( - <CustomButton + <IconButton onPress={options.onPress} - style={{ padding: 0 }} + containerStyle={{ padding: 0 }} + contentWrapperStyle={viewStyle} themeId={themeId} disabled={!!options.disabled} description={options.description} - contentStyle={viewStyle} - > - {icon} - </CustomButton> + iconName={options.iconName} + iconStyle={this.styles().topIcon} + /> ); }; const renderUndoButton = () => { return renderTopButton({ - iconName: 'arrow-undo-circle-sharp', + iconName: 'ionicon arrow-undo-circle-sharp', description: _('Undo'), onPress: this.props.onUndoButtonPress, visible: this.props.showUndoButton, @@ -392,7 +391,7 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade const renderRedoButton = () => { return renderTopButton({ - iconName: 'arrow-redo-circle-sharp', + iconName: 'ionicon arrow-redo-circle-sharp', description: _('Redo'), onPress: this.props.onRedoButtonPress, visible: this.props.showRedoButton, @@ -402,30 +401,32 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied function selectAllButton(styles: any, onPress: OnPressCallback) { return ( - <CustomButton + <IconButton onPress={onPress} themeId={themeId} description={_('Select all')} - contentStyle={styles.iconButton} - > - <Icon name="checkmark-circle-outline" style={styles.topIcon} /> - </CustomButton> + contentWrapperStyle={styles.iconButton} + + iconName="ionicon checkmark-circle-outline" + iconStyle={styles.topIcon} + /> ); } // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied function searchButton(styles: any, onPress: OnPressCallback) { return ( - <CustomButton + <IconButton onPress={onPress} description={_('Search')} themeId={themeId} - contentStyle={styles.iconButton} - > - <Icon name="search" style={styles.topIcon} /> - </CustomButton> + contentWrapperStyle={styles.iconButton} + + iconName='ionicon search' + iconStyle={styles.topIcon} + /> ); } @@ -438,21 +439,22 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade if (allVisiblePanels.length === 0) return null; return ( - <CustomButton + <IconButton onPress={onPress} description={_('Plugin panels')} themeId={themeId} - contentStyle={styles.iconButton} - > - <Icon name="extension-puzzle" style={styles.topIcon} /> - </CustomButton> + contentWrapperStyle={styles.iconButton} + + iconName="ionicon extension-puzzle" + iconStyle={styles.topIcon} + /> ); }; // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied function deleteButton(styles: any, onPress: OnPressCallback, disabled: boolean) { return ( - <CustomButton + <IconButton onPress={onPress} disabled={disabled} @@ -461,17 +463,18 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade accessibilityHint={ disabled ? null : _('Delete selected notes') } - contentStyle={disabled ? styles.iconButtonDisabled : styles.iconButton} - > - <Icon name="trash" style={styles.topIcon} /> - </CustomButton> + contentWrapperStyle={disabled ? styles.iconButtonDisabled : styles.iconButton} + + iconName='ionicon trash' + iconStyle={styles.topIcon} + /> ); } // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied function restoreButton(styles: any, onPress: OnPressCallback, disabled: boolean) { return ( - <CustomButton + <IconButton onPress={onPress} disabled={disabled} @@ -480,17 +483,18 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade accessibilityHint={ disabled ? null : _('Restore') } - contentStyle={disabled ? styles.iconButtonDisabled : styles.iconButton} - > - <Icon name="reload-circle" style={styles.topIcon} /> - </CustomButton> + contentWrapperStyle={disabled ? styles.iconButtonDisabled : styles.iconButton} + + iconName='ionicon reload-circle' + iconStyle={styles.topIcon} + /> ); } // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied function duplicateButton(styles: any, onPress: OnPressCallback, disabled: boolean) { return ( - <CustomButton + <IconButton onPress={onPress} disabled={disabled} @@ -499,10 +503,10 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade accessibilityHint={ disabled ? null : _('Duplicate selected notes') } - contentStyle={disabled ? styles.iconButtonDisabled : styles.iconButton} - > - <Icon name="copy" style={styles.topIcon} /> - </CustomButton> + contentWrapperStyle={disabled ? styles.iconButtonDisabled : styles.iconButton} + iconName='ionicon copy' + iconStyle={styles.topIcon} + /> ); } diff --git a/packages/tools/cspell/dictionary4.txt b/packages/tools/cspell/dictionary4.txt index 33ee97b06..933b559e5 100644 --- a/packages/tools/cspell/dictionary4.txt +++ b/packages/tools/cspell/dictionary4.txt @@ -108,3 +108,4 @@ libasound libatk ENOTFOUND Scaleway +Ionicon