import * as accounts from '@planningcenter/icons/paths/accounts' import * as api from '@planningcenter/icons/paths/api' import * as brand from '@planningcenter/icons/paths/brand' import * as calendar from '@planningcenter/icons/paths/calendar' import * as chat from '@planningcenter/icons/paths/chat' import * as churchCenter from '@planningcenter/icons/paths/church-center' import * as general from '@planningcenter/icons/paths/general' import * as groups from '@planningcenter/icons/paths/groups' import * as logomark from '@planningcenter/icons/paths/logomark' import * as people from '@planningcenter/icons/paths/people' import * as publishing from '@planningcenter/icons/paths/publishing' import * as registrations from '@planningcenter/icons/paths/registrations' import * as services from '@planningcenter/icons/paths/services' import React from 'react' import type { ColorValue, StyleProp, ViewStyle } from 'react-native' import { StyleSheet, View } from 'react-native' import type { XmlProps } from 'react-native-svg' import { SvgXml } from 'react-native-svg' import { useFontScale, useTheme } from '../../hooks' // ================================= // ====== Constants ================ // ================================= const FALLBACK_SIZE = 12 const ICONS = { accounts, api, brand, calendar, chat, churchCenter, general, groups, logomark, people, services, publishing, registrations, } as const export type IconStyle = ViewStyle & { fontSize?: number color?: string } export type IconSetName = keyof typeof ICONS type IconName = keyof (typeof ICONS)[T] & string export type IconString = | `accounts.${IconName<'accounts'>}` | `api.${IconName<'api'>}` | `brand.${IconName<'brand'>}` | `calendar.${IconName<'calendar'>}` | `chat.${IconName<'chat'>}` | `churchCenter.${IconName<'churchCenter'>}` | `general.${IconName<'general'>}` | `groups.${IconName<'groups'>}` | `logomark.${IconName<'logomark'>}` | `people.${IconName<'people'>}` | `services.${IconName<'services'>}` | `publishing.${IconName<'publishing'>}` | `registrations.${IconName<'registrations'>}` // ================================= // ====== Component ================ // ================================= export interface IconProps extends Omit { /** * Made up of the set.iconName. * Example: "general.textMessage" */ name: IconString /** * This sets a static size for the icon. * Providing a fontSize style will allow the icon to scale with the device's text a11y size. */ size?: number /** * Specifies the maximum size a font can reach when allowFontScaling is enabled. */ maxFontSizeMultiplier?: number /** * Changes the color of the icon. Most cases should use a theme color to support light/dark mode. */ color?: ColorValue /** * Icon can handle ViewStyle, color, and fontSize. */ style?: StyleProp } export function Icon({ color, name, size, style, accessibilityElementsHidden, accessibilityLabel, maxFontSizeMultiplier, ...props }: IconProps) { const flattenStyles = StyleSheet.flatten(style) const iconSize = useGetIconSize(size, flattenStyles, maxFontSizeMultiplier) const path = getIconPath(name) const styles = useStyles({ color, iconSize }) if (!path) { console.warn(`No icon available named ${name}. Remember to use the format "set.iconName"`) return } return ( `} height={iconSize} width={iconSize} style={{ ...styles.icon, ...flattenStyles }} {...props} /> ) } const useGetIconSize = (size?: number, style?: IconStyle, maxFontSizeMultiplier?: number) => { const fontSize = style?.fontSize const fontScale = useFontScale({ maxFontSizeMultiplier }) if (fontSize) return fontSize * fontScale return size || FALLBACK_SIZE } const getIconPath = (name: IconString): string => { const [setName, iconName] = name.split('.') return (ICONS[setName as IconSetName] as Record)?.[iconName] } // ================================= // ====== Styles =================== // ================================= const useStyles = ({ color, iconSize }: { color?: ColorValue; iconSize: number }) => { const { colors } = useTheme() return StyleSheet.create({ noIcon: { backgroundColor: colors.iconColorDefaultDisabled, width: iconSize, height: iconSize, borderRadius: iconSize / 2, }, icon: { color: color || colors.iconColorDefaultPrimary, }, }) }