mirror of https://github.com/laurent22/joplin.git
Mobile: Toolbar: Show only half of last button to indicate scroll (#11772)
parent
8312196faa
commit
cc09f92d3b
|
@ -619,6 +619,7 @@ packages/app-mobile/components/EditorToolbar/utils/allToolbarCommandNamesFromSta
|
|||
packages/app-mobile/components/EditorToolbar/utils/isSelected.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/selectedCommandNamesFromState.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/toolbarButtonsFromState.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/useButtonSize.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.jest.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.web.js
|
||||
|
|
|
@ -594,6 +594,7 @@ packages/app-mobile/components/EditorToolbar/utils/allToolbarCommandNamesFromSta
|
|||
packages/app-mobile/components/EditorToolbar/utils/isSelected.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/selectedCommandNamesFromState.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/toolbarButtonsFromState.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/useButtonSize.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.jest.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.web.js
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import { AppState } from '../../utils/types';
|
||||
import { connect } from 'react-redux';
|
||||
import { ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { LayoutChangeEvent, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { ToolbarButtonInfo, ToolbarItem } from '@joplin/lib/services/commands/ToolbarButtonUtils';
|
||||
import toolbarButtonsFromState from './utils/toolbarButtonsFromState';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
|
@ -12,6 +12,7 @@ import { EditorState } from './types';
|
|||
import ToolbarButton from './ToolbarButton';
|
||||
import isSelected from './utils/isSelected';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import useButtonSize from './utils/useButtonSize';
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
|
@ -19,6 +20,33 @@ interface Props {
|
|||
editorState: EditorState;
|
||||
}
|
||||
|
||||
const useButtonPadding = (buttonCount: number) => {
|
||||
const { buttonSize } = useButtonSize();
|
||||
const [containerWidth, setContainerWidth] = useState(0);
|
||||
|
||||
const onContainerLayout = useCallback((event: LayoutChangeEvent) => {
|
||||
setContainerWidth(event.nativeEvent.layout.width);
|
||||
}, []);
|
||||
|
||||
const buttonPadding = useMemo(() => {
|
||||
if (buttonCount <= 1) return 0;
|
||||
// Number of offscreen buttons -- round up because we can't have negative padding
|
||||
const overflowButtonCount = Math.max(0, Math.ceil((buttonCount * buttonSize - containerWidth) / buttonSize));
|
||||
const visibleButtonCount = buttonCount - overflowButtonCount;
|
||||
const allVisible = visibleButtonCount === buttonCount;
|
||||
if (allVisible) return 0;
|
||||
|
||||
const targetContentWidth = containerWidth + buttonSize / 2;
|
||||
const actualContentWidth = visibleButtonCount * buttonSize;
|
||||
const widthDifference = targetContentWidth - actualContentWidth;
|
||||
// Only half of the gap for the rightmost visible button is on the screen
|
||||
const gapCount = visibleButtonCount - 1 / 2;
|
||||
return widthDifference / gapCount;
|
||||
}, [containerWidth, buttonCount, buttonSize]);
|
||||
|
||||
return { onContainerLayout, buttonPadding };
|
||||
};
|
||||
|
||||
const useStyles = (themeId: number) => {
|
||||
return useMemo(() => {
|
||||
const theme = themeStyle(themeId);
|
||||
|
@ -53,7 +81,6 @@ const useSettingButtonInfo = (setSettingsVisible: SetSettingsVisible) => {
|
|||
};
|
||||
|
||||
const EditorToolbar: React.FC<Props> = props => {
|
||||
const styles = useStyles(props.themeId);
|
||||
|
||||
const buttonInfos: ToolbarButtonInfo[] = [];
|
||||
|
||||
|
@ -63,11 +90,17 @@ const EditorToolbar: React.FC<Props> = props => {
|
|||
}
|
||||
}
|
||||
|
||||
// Include setting button in the count
|
||||
const buttonCount = buttonInfos.length + 1;
|
||||
const { buttonPadding, onContainerLayout } = useButtonPadding(buttonCount);
|
||||
const styles = useStyles(props.themeId);
|
||||
|
||||
const renderButton = (info: ToolbarButtonInfo) => {
|
||||
return <ToolbarButton
|
||||
key={`command-${info.name}`}
|
||||
buttonInfo={info}
|
||||
themeId={props.themeId}
|
||||
extraPadding={buttonPadding}
|
||||
selected={isSelected(info.name, props.editorState)}
|
||||
/>;
|
||||
};
|
||||
|
@ -89,6 +122,7 @@ const EditorToolbar: React.FC<Props> = props => {
|
|||
const settingsButtonInfo = useSettingButtonInfo(setSettingsVisible);
|
||||
const settingsButton = <ToolbarButton
|
||||
buttonInfo={settingsButtonInfo}
|
||||
extraPadding={buttonPadding}
|
||||
themeId={props.themeId}
|
||||
/>;
|
||||
|
||||
|
@ -99,6 +133,7 @@ const EditorToolbar: React.FC<Props> = props => {
|
|||
horizontal={true}
|
||||
style={styles.content}
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
onLayout={onContainerLayout}
|
||||
>
|
||||
{buttonInfos.map(renderButton)}
|
||||
<View style={styles.spacer}/>
|
||||
|
|
|
@ -2,41 +2,41 @@ import * as React from 'react';
|
|||
import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
|
||||
import IconButton from '../IconButton';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { StyleSheet, useWindowDimensions } from 'react-native';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { themeStyle } from '../global-style';
|
||||
import useButtonSize from './utils/useButtonSize';
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
extraPadding: number;
|
||||
buttonInfo: ToolbarButtonInfo;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
const useStyles = (themeId: number, selected: boolean, enabled: boolean) => {
|
||||
const { fontScale } = useWindowDimensions();
|
||||
const useStyles = (themeId: number, selected: boolean, enabled: boolean, extraPadding: number) => {
|
||||
const { buttonSize, iconSize } = useButtonSize();
|
||||
|
||||
return useMemo(() => {
|
||||
const theme = themeStyle(themeId);
|
||||
return StyleSheet.create({
|
||||
icon: {
|
||||
color: theme.color,
|
||||
fontSize: 22 * fontScale,
|
||||
fontSize: iconSize,
|
||||
},
|
||||
button: {
|
||||
// Scaling the button width/height by the device font scale causes the button to scale
|
||||
// with the user's device font size.
|
||||
width: 48 * fontScale,
|
||||
height: 48 * fontScale,
|
||||
width: buttonSize + extraPadding,
|
||||
height: buttonSize,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: selected ? theme.backgroundColorHover3 : theme.backgroundColor3,
|
||||
opacity: enabled ? 1 : theme.disabledOpacity,
|
||||
},
|
||||
});
|
||||
}, [themeId, selected, enabled, fontScale]);
|
||||
}, [themeId, selected, enabled, buttonSize, iconSize, extraPadding]);
|
||||
};
|
||||
|
||||
const ToolbarButton: React.FC<Props> = memo(({ themeId, buttonInfo, selected }) => {
|
||||
const styles = useStyles(themeId, selected, buttonInfo.enabled);
|
||||
const ToolbarButton: React.FC<Props> = memo(({ themeId, buttonInfo, selected, extraPadding }) => {
|
||||
const styles = useStyles(themeId, selected, buttonInfo.enabled, extraPadding);
|
||||
const isToggleButton = selected !== undefined;
|
||||
|
||||
return <IconButton
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { useMemo } from 'react';
|
||||
import { useWindowDimensions } from 'react-native';
|
||||
|
||||
const useButtonSize = () => {
|
||||
const { fontScale } = useWindowDimensions();
|
||||
|
||||
return useMemo(() => {
|
||||
return {
|
||||
// Scaling the button width/height by the device font scale causes the button to scale
|
||||
// with the user's device font size.
|
||||
buttonSize: 48 * fontScale,
|
||||
iconSize: 22 * fontScale,
|
||||
};
|
||||
}, [fontScale]);
|
||||
};
|
||||
|
||||
export default useButtonSize;
|
Loading…
Reference in New Issue