1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00
joplin/packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.tsx

122 lines
3.5 KiB
TypeScript
Raw Normal View History

import * as React from 'react';
2022-08-29 15:19:04 +02:00
import { ReactElement, useCallback, useMemo, useState } from 'react';
import { LayoutChangeEvent, ScrollView, View, ViewStyle } from 'react-native';
2022-08-29 15:19:04 +02:00
import ToggleOverflowButton from './ToggleOverflowButton';
import ToolbarButton, { buttonSize } from './ToolbarButton';
import ToolbarOverflowRows from './ToolbarOverflowRows';
import { ButtonGroup, ButtonSpec, StyleSheetData } from './types';
interface ToolbarProps {
buttons: ButtonGroup[];
styleSheet: StyleSheetData;
style?: ViewStyle;
2022-08-29 15:19:04 +02:00
}
// Displays a list of buttons with an overflow menu.
const Toolbar: React.FC<ToolbarProps> = (props: ToolbarProps) => {
2022-08-29 15:19:04 +02:00
const [overflowButtonsVisible, setOverflowPopupVisible] = useState(false);
const [maxButtonsEachSide, setMaxButtonsEachSide] = useState(0);
const allButtonSpecs = useMemo(() => {
const buttons = props.buttons.reduce((accumulator: ButtonSpec[], current: ButtonGroup) => {
const newItems: ButtonSpec[] = [];
for (const item of current.items) {
if (item.visible ?? true) {
newItems.push(item);
}
2022-08-29 15:19:04 +02:00
}
return accumulator.concat(...newItems);
}, []);
2022-08-29 15:19:04 +02:00
// Sort from highest priority to lowest
buttons.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
return buttons;
}, [props.buttons]);
2022-08-29 15:19:04 +02:00
const allButtonComponents: ReactElement[] = [];
let key = 0;
for (const spec of allButtonSpecs) {
key++;
allButtonComponents.push(
<ToolbarButton
key={key.toString()}
styleSheet={props.styleSheet}
spec={spec}
/>,
2022-08-29 15:19:04 +02:00
);
}
const onContainerLayout = useCallback((event: LayoutChangeEvent) => {
const containerWidth = event.nativeEvent.layout.width;
const maxButtonsTotal = Math.floor(containerWidth / buttonSize);
setMaxButtonsEachSide(Math.floor(
Math.min((maxButtonsTotal - 1) / 2, allButtonSpecs.length / 2),
2022-08-29 15:19:04 +02:00
));
}, [allButtonSpecs.length]);
const onToggleOverflowVisible = useCallback(() => {
setOverflowPopupVisible(!overflowButtonsVisible);
}, [overflowButtonsVisible]);
const toggleOverflowButton = (
<ToggleOverflowButton
key={(++key).toString()}
styleSheet={props.styleSheet}
overflowVisible={overflowButtonsVisible}
onToggleOverflowVisible={onToggleOverflowVisible}
/>
);
const mainButtons: ReactElement[] = [];
if (maxButtonsEachSide >= allButtonComponents.length) {
mainButtons.push(...allButtonComponents);
} else if (maxButtonsEachSide > 0) {
2022-08-29 15:19:04 +02:00
// We want the menu to look something like this:
// B I (…) 🔍 ⌨
// where (…) shows/hides overflow.
// Add from the left and right of [allButtonComponents] to ensure that
// the (…) button is in the center:
mainButtons.push(...allButtonComponents.slice(0, maxButtonsEachSide));
mainButtons.push(toggleOverflowButton);
mainButtons.push(...allButtonComponents.slice(-maxButtonsEachSide));
} else {
mainButtons.push(toggleOverflowButton);
2022-08-29 15:19:04 +02:00
}
const styles = props.styleSheet.styles;
const mainButtonRow = (
<View style={styles.toolbarRow}>
{ mainButtons }
2022-08-29 15:19:04 +02:00
</View>
);
return (
<View
style={{
...styles.toolbarContainer,
// The number of buttons displayed is based on the width of the
// container. As such, we can't base the container's width on the
// size of its content.
width: '100%',
...props.style,
2022-08-29 15:19:04 +02:00
}}
onLayout={onContainerLayout}
>
<ScrollView>
<ToolbarOverflowRows
buttonGroups={props.buttons}
styleSheet={props.styleSheet}
visible={overflowButtonsVisible}
onToggleOverflow={onToggleOverflowVisible}
/>
</ScrollView>
{ !overflowButtonsVisible ? mainButtonRow : null }
2022-08-29 15:19:04 +02:00
</View>
);
};
export default Toolbar;