1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Chore: Refactor: Mobile: Rename CustomButton to IconButton (#10473)

This commit is contained in:
Henry Heino 2024-05-25 06:41:27 -07:00 committed by GitHub
parent 3bf9438a59
commit b09d6e8568
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 148 additions and 151 deletions

View File

@ -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

2
.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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 }}
/>
</>
);

View File

@ -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}
/>
);
};

View File

@ -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',

View File

@ -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>
}
/>
);
};

View File

@ -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}
/>
);
}

View File

@ -108,3 +108,4 @@ libasound
libatk
ENOTFOUND
Scaleway
Ionicon