文件
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

86 行
2.7 KiB
TypeScript

import * as React from 'react';
import { TextStyle, Text, StyleProp } from 'react-native';
import { FontAwesome5 } from '@react-native-vector-icons/fontawesome5';
import { MaterialDesignIcons } from '@react-native-vector-icons/material-design-icons';
import { Ionicons } from '@react-native-vector-icons/ionicons';
type IconNameProp<IconComponent> = IconComponent extends (props: { name: infer NameType })=> unknown ? NameType : never;
type FontAwesome5Name = IconNameProp<typeof FontAwesome5>;
type MaterialIconName = IconNameProp<typeof MaterialDesignIcons>;
type IoniconsName = IconNameProp<typeof Ionicons>;
interface Props {
name: string;
style: StyleProp<TextStyle>;
// If `null` is given, the content must be labeled elsewhere.
accessibilityLabel: string|null;
allowFontScaling?: boolean;
}
const Icon: React.FC<Props> = props => {
// Matches:
// 1. A prefix of word characters (\w+)
// 2. A suffix of non-spaces (\S+)
// An "fa-" at the beginning of the suffix is ignored.
const nameMatch = props.name.match(/^(\w+)\s+(?:fa-)?(\S+)$/);
const namePrefix = nameMatch ? nameMatch[1] : '';
const nameSuffix = nameMatch ? nameMatch[2] : props.name;
// If there's no label, make sure that the screen reader doesn't try
// to read the characters from the icon font (they don't make sense
// without the icon font applied).
const accessibilityHidden = props.accessibilityLabel === null;
const importantForAccessibility = accessibilityHidden ? 'no-hide-descendants' as const : 'yes' as const;
const sharedProps = {
importantForAccessibility, // Android
accessibilityElementsHidden: accessibilityHidden, // iOS
'aria-hidden': accessibilityHidden, // Web
accessibilityLabel: props.accessibilityLabel,
style: props.style,
allowFontScaling: props.allowFontScaling,
};
if (namePrefix.match(/^fa[bsr]?$/)) {
let iconStyle: 'solid'|'brand' = 'solid';
if (namePrefix.startsWith('fab')) {
iconStyle = 'brand';
} else if (namePrefix.startsWith('fas')) {
iconStyle = 'solid';
}
return (
<FontAwesome5
name={nameSuffix as FontAwesome5Name}
iconStyle={iconStyle}
{...sharedProps}
/>
);
} else if (namePrefix === 'material') {
return <MaterialDesignIcons name={nameSuffix as MaterialIconName} {...sharedProps}/>;
} else if (namePrefix === 'ionicon') {
return <Ionicons name={nameSuffix as IoniconsName} {...sharedProps}/>;
} else if (namePrefix === 'text') {
return (
<Text
style={props.style}
aria-hidden={accessibilityHidden}
importantForAccessibility={importantForAccessibility}
accessibilityElementsHidden={accessibilityHidden}
>
{nameSuffix}
</Text>
);
} else {
return <FontAwesome5 name='cog' iconStyle='solid' {...sharedProps}/>;
}
};
export default Icon;