mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-02 12:47:41 +02:00
This commit is contained in:
parent
1fb392ff4e
commit
d9dadf28cb
@ -611,7 +611,8 @@ packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/expo
|
||||
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/makeImportExportCacheDirectory.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SectionDescription.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SectionHeader.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SectionSelector.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SectionSelector/SectionTab.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SectionSelector/index.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SettingComponent.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SettingItem.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SettingsButton.js
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -590,7 +590,8 @@ packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/expo
|
||||
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/makeImportExportCacheDirectory.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SectionDescription.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SectionHeader.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SectionSelector.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SectionSelector/SectionTab.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SectionSelector/index.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SettingComponent.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SettingItem.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/SettingsButton.js
|
||||
|
@ -1,135 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import Setting, { AppType, SettingMetadataSection, SettingSectionSource } from '@joplin/lib/models/Setting';
|
||||
import { FunctionComponent, useEffect, useMemo, useState } from 'react';
|
||||
import { ConfigScreenStyles } from './configScreenStyles';
|
||||
import { FlatList, Text, View, ViewStyle } from 'react-native';
|
||||
import { settingsSections } from '@joplin/lib/components/shared/config/config-shared';
|
||||
import Icon from '../../Icon';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { TouchableRipple } from 'react-native-paper';
|
||||
import BetaChip from '../../BetaChip';
|
||||
|
||||
interface Props {
|
||||
styles: ConfigScreenStyles;
|
||||
|
||||
width: number|undefined;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
settings: any;
|
||||
selectedSectionName: string|null;
|
||||
openSection: (sectionName: string)=> void;
|
||||
}
|
||||
|
||||
const SectionSelector: FunctionComponent<Props> = props => {
|
||||
const sections = useMemo(() => {
|
||||
return settingsSections({ device: AppType.Mobile, settings: props.settings });
|
||||
}, [props.settings]);
|
||||
const styles = props.styles.styleSheet;
|
||||
|
||||
const itemHeight = styles.sidebarButton.height;
|
||||
|
||||
const onRenderButton = ({ item }: { item: SettingMetadataSection }) => {
|
||||
const section = item;
|
||||
const selected = props.selectedSectionName === section.name;
|
||||
const icon = Setting.sectionNameToIcon(section.name, AppType.Mobile);
|
||||
const label = Setting.sectionNameToLabel(section.name);
|
||||
const shortDescription = Setting.sectionMetadataToSummary(section);
|
||||
const isPlugin = item.source === SettingSectionSource.Plugin;
|
||||
|
||||
const titleStyle = selected ? styles.sidebarSelectedButtonText : styles.sidebarButtonMainText;
|
||||
|
||||
const sourceIcon = isPlugin ? (
|
||||
<Icon
|
||||
name='fas fa-puzzle-piece'
|
||||
accessibilityLabel={_('From a plugin')}
|
||||
style={titleStyle}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const isBeta = item.name === 'plugins';
|
||||
const betaChip = isBeta ? <BetaChip size={10}/> : null;
|
||||
|
||||
return (
|
||||
<TouchableRipple
|
||||
key={section.name}
|
||||
role='tab'
|
||||
aria-selected={selected}
|
||||
onPress={() => props.openSection(section.name)}
|
||||
>
|
||||
<View
|
||||
style={selected ? styles.selectedSidebarButton : styles.sidebarButton}
|
||||
>
|
||||
<Icon
|
||||
name={icon}
|
||||
accessibilityLabel={null}
|
||||
style={styles.sidebarIcon}
|
||||
/>
|
||||
<View style={{ display: 'flex', flexDirection: 'column', flex: 1 }}>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={titleStyle}
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
|
||||
{betaChip}
|
||||
</View>
|
||||
<Text
|
||||
style={styles.sidebarButtonDescriptionText}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode='tail'
|
||||
>
|
||||
{shortDescription ?? ''}
|
||||
</Text>
|
||||
</View>
|
||||
{sourceIcon}
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
};
|
||||
|
||||
const [flatListRef, setFlatListRef] = useState<FlatList|null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (flatListRef && props.selectedSectionName) {
|
||||
let selectedIndex = 0;
|
||||
for (const section of sections) {
|
||||
if (section.name === props.selectedSectionName) {
|
||||
break;
|
||||
}
|
||||
selectedIndex ++;
|
||||
}
|
||||
|
||||
flatListRef.scrollToIndex({
|
||||
index: selectedIndex,
|
||||
viewPosition: 0.5,
|
||||
});
|
||||
}
|
||||
}, [props.selectedSectionName, flatListRef, sections]);
|
||||
|
||||
const containerStyle: ViewStyle = useMemo(() => ({
|
||||
width: props.width,
|
||||
maxWidth: props.width,
|
||||
minWidth: props.width,
|
||||
flex: 1,
|
||||
}), [props.width]);
|
||||
|
||||
return (
|
||||
<View style={containerStyle}>
|
||||
<FlatList
|
||||
role='tablist'
|
||||
ref={setFlatListRef}
|
||||
data={sections}
|
||||
renderItem={onRenderButton}
|
||||
keyExtractor={item => item.name}
|
||||
getItemLayout={(_data, index) => ({
|
||||
length: itemHeight, offset: itemHeight * index, index,
|
||||
})}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default SectionSelector;
|
@ -0,0 +1,66 @@
|
||||
import * as React from 'react';
|
||||
import { ConfigScreenStyles } from '../configScreenStyles';
|
||||
import Icon from '../../../Icon';
|
||||
import BetaChip from '../../../BetaChip';
|
||||
import { TouchableRipple, Text } from 'react-native-paper';
|
||||
import { View } from 'react-native';
|
||||
import Setting, { AppType, SettingMetadataSection } from '@joplin/lib/models/Setting';
|
||||
|
||||
interface Props {
|
||||
selected: boolean;
|
||||
section: SettingMetadataSection;
|
||||
styles: ConfigScreenStyles;
|
||||
onPress: ()=> void;
|
||||
}
|
||||
|
||||
const SectionTab: React.FC<Props> = ({ styles, onPress, selected, section }) => {
|
||||
const icon = Setting.sectionNameToIcon(section.name, AppType.Mobile);
|
||||
const label = Setting.sectionNameToLabel(section.name);
|
||||
const shortDescription = Setting.sectionMetadataToSummary(section);
|
||||
|
||||
const styleSheet = styles.styleSheet;
|
||||
const titleStyle = selected ? styleSheet.sidebarSelectedButtonText : styleSheet.sidebarButtonMainText;
|
||||
|
||||
const isBeta = section.name === 'plugins';
|
||||
const betaChip = isBeta ? <BetaChip size={10}/> : null;
|
||||
|
||||
return (
|
||||
<TouchableRipple
|
||||
key={section.name}
|
||||
role='tab'
|
||||
aria-selected={selected}
|
||||
onPress={onPress}
|
||||
>
|
||||
<View
|
||||
style={selected ? styleSheet.selectedSidebarButton : styleSheet.sidebarButton}
|
||||
>
|
||||
<Icon
|
||||
name={icon}
|
||||
accessibilityLabel={null}
|
||||
style={styleSheet.sidebarIcon}
|
||||
/>
|
||||
<View style={{ display: 'flex', flexDirection: 'column', flex: 1 }}>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={titleStyle}
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
|
||||
{betaChip}
|
||||
</View>
|
||||
<Text
|
||||
style={styleSheet.sidebarButtonDescriptionText}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode='tail'
|
||||
>
|
||||
{shortDescription ?? ''}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
};
|
||||
|
||||
export default SectionTab;
|
@ -0,0 +1,116 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { AppType, SettingMetadataSection, SettingSectionSource } from '@joplin/lib/models/Setting';
|
||||
import { FunctionComponent, useEffect, useMemo, useState } from 'react';
|
||||
import { ConfigScreenStyles } from '../configScreenStyles';
|
||||
import { FlatList, View, ViewStyle } from 'react-native';
|
||||
import { Text } from 'react-native-paper';
|
||||
import { settingsSections } from '@joplin/lib/components/shared/config/config-shared';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import SectionTab from './SectionTab';
|
||||
|
||||
interface Props {
|
||||
styles: ConfigScreenStyles;
|
||||
|
||||
width: number|undefined;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
settings: Record<string, any>;
|
||||
selectedSectionName: string|null;
|
||||
openSection: (sectionName: string)=> void;
|
||||
}
|
||||
type SectionDivider = { divider: boolean; label: string; name: string; source?: string };
|
||||
|
||||
const SectionSelector: FunctionComponent<Props> = props => {
|
||||
type SectionListType = (SectionDivider|SettingMetadataSection)[];
|
||||
const sections = useMemo((): SectionListType => {
|
||||
const allSections = settingsSections({ device: AppType.Mobile, settings: props.settings });
|
||||
|
||||
const builtInSections = [];
|
||||
const pluginSections = [];
|
||||
for (const section of allSections) {
|
||||
if (section.source === SettingSectionSource.Plugin) {
|
||||
pluginSections.push(section);
|
||||
} else {
|
||||
builtInSections.push(section);
|
||||
}
|
||||
}
|
||||
|
||||
let result: SectionListType = builtInSections;
|
||||
if (pluginSections.length > 0) {
|
||||
result = result.concat([
|
||||
{ label: _('Plugins'), name: 'plugins-divider', divider: true },
|
||||
...pluginSections,
|
||||
]);
|
||||
}
|
||||
return result;
|
||||
}, [props.settings]);
|
||||
|
||||
const styles = props.styles.styleSheet;
|
||||
const onRenderButton = ({ item }: { item: SettingMetadataSection|SectionDivider }) => {
|
||||
const section = item;
|
||||
const selected = props.selectedSectionName === section.name;
|
||||
|
||||
if ('divider' in item && item.divider) {
|
||||
return (
|
||||
<View style={styles.sidebarHeader}>
|
||||
<Text variant='labelLarge' style={styles.sidebarHeaderText}>{item.label}</Text>
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<SectionTab
|
||||
selected={selected}
|
||||
section={section as SettingMetadataSection}
|
||||
styles={props.styles}
|
||||
onPress={() => props.openSection(section.name)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const [flatListRef, setFlatListRef] = useState<FlatList|null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (flatListRef && props.selectedSectionName) {
|
||||
let selectedIndex = 0;
|
||||
for (const section of sections) {
|
||||
if (section.name === props.selectedSectionName) {
|
||||
break;
|
||||
}
|
||||
selectedIndex ++;
|
||||
}
|
||||
|
||||
flatListRef.scrollToIndex({
|
||||
index: selectedIndex,
|
||||
viewPosition: 0.5,
|
||||
});
|
||||
}
|
||||
}, [props.selectedSectionName, flatListRef, sections]);
|
||||
|
||||
const containerStyle: ViewStyle = useMemo(() => ({
|
||||
width: props.width,
|
||||
maxWidth: props.width,
|
||||
minWidth: props.width,
|
||||
flex: 1,
|
||||
}), [props.width]);
|
||||
|
||||
return (
|
||||
<View style={containerStyle}>
|
||||
<FlatList
|
||||
role='tablist'
|
||||
ref={setFlatListRef}
|
||||
data={sections}
|
||||
renderItem={onRenderButton}
|
||||
keyExtractor={item => item.name}
|
||||
onScrollToIndexFailed={({ index, averageItemLength }) => {
|
||||
// Scrolling to a particular index can fail if the item at that index hasn't been rendered yet.
|
||||
// This shouldn't happen often, so a guess should be sufficient.
|
||||
flatListRef.scrollToOffset({ offset: (index + 0.5) * averageItemLength });
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default SectionSelector;
|
@ -33,6 +33,8 @@ export interface ConfigScreenStyleSheet {
|
||||
sidebarButtonMainText: TextStyle;
|
||||
sidebarSelectedButtonText: TextStyle;
|
||||
sidebarButtonDescriptionText: TextStyle;
|
||||
sidebarHeader: ViewStyle;
|
||||
sidebarHeaderText: TextStyle;
|
||||
|
||||
settingControl: TextStyle;
|
||||
}
|
||||
@ -204,6 +206,18 @@ const configScreenStyles = (themeId: number): ConfigScreenStyles => {
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
sidebarButtonDescriptionText,
|
||||
sidebarHeader: {
|
||||
paddingLeft: 12,
|
||||
height: sidebarButtonHeight / 2,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: theme.oddBackgroundColor,
|
||||
},
|
||||
sidebarHeaderText: {
|
||||
color: theme.color,
|
||||
fontWeight: 'bold',
|
||||
fontSize: theme.fontSize,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user