| 1 | import type { Icon } from "@tabler/icons-react-native/types"; |
| 2 | /* |
| 3 | * Keep these as per-icon exports. Importing the package root eagerly registers |
| 4 | * the entire Tabler icon set in Metro. |
| 5 | */ |
| 6 | import IconAdjustmentsHorizontal from "@tabler/icons-react-native/IconAdjustmentsHorizontal"; |
| 7 | import IconAlignLeft from "@tabler/icons-react-native/IconAlignLeft"; |
| 8 | import IconAlertCircle from "@tabler/icons-react-native/IconAlertCircle"; |
| 9 | import IconAlertTriangle from "@tabler/icons-react-native/IconAlertTriangle"; |
| 10 | import IconApps from "@tabler/icons-react-native/IconApps"; |
| 11 | import IconArchive from "@tabler/icons-react-native/IconArchive"; |
| 12 | import IconArrowBackUp from "@tabler/icons-react-native/IconArrowBackUp"; |
| 13 | import IconArrowDown from "@tabler/icons-react-native/IconArrowDown"; |
| 14 | import IconArrowForwardUp from "@tabler/icons-react-native/IconArrowForwardUp"; |
| 15 | import IconArrowLeft from "@tabler/icons-react-native/IconArrowLeft"; |
| 16 | import IconArrowRight from "@tabler/icons-react-native/IconArrowRight"; |
| 17 | import IconArrowDownCircle from "@tabler/icons-react-native/IconArrowDownCircle"; |
| 18 | import IconArrowRightCircle from "@tabler/icons-react-native/IconArrowRightCircle"; |
| 19 | import IconArrowUp from "@tabler/icons-react-native/IconArrowUp"; |
| 20 | import IconArrowUpCircle from "@tabler/icons-react-native/IconArrowUpCircle"; |
| 21 | import IconArrowUpRight from "@tabler/icons-react-native/IconArrowUpRight"; |
| 22 | import IconArrowUpRightCircle from "@tabler/icons-react-native/IconArrowUpRightCircle"; |
| 23 | import IconArrowsLeftRight from "@tabler/icons-react-native/IconArrowsLeftRight"; |
| 24 | import IconArrowsDiagonal2 from "@tabler/icons-react-native/IconArrowsDiagonal2"; |
| 25 | import IconArrowsMinimize from "@tabler/icons-react-native/IconArrowsMinimize"; |
| 26 | import IconBellRinging from "@tabler/icons-react-native/IconBellRinging"; |
| 27 | import IconBolt from "@tabler/icons-react-native/IconBolt"; |
| 28 | import IconBox from "@tabler/icons-react-native/IconBox"; |
| 29 | import IconBrain from "@tabler/icons-react-native/IconBrain"; |
| 30 | import IconCamera from "@tabler/icons-react-native/IconCamera"; |
| 31 | import IconCar from "@tabler/icons-react-native/IconCar"; |
| 32 | import IconChartBar from "@tabler/icons-react-native/IconChartBar"; |
| 33 | import IconCheck from "@tabler/icons-react-native/IconCheck"; |
| 34 | import IconCloud from "@tabler/icons-react-native/IconCloud"; |
| 35 | import IconChevronDown from "@tabler/icons-react-native/IconChevronDown"; |
| 36 | import IconChevronLeft from "@tabler/icons-react-native/IconChevronLeft"; |
| 37 | import IconChevronRight from "@tabler/icons-react-native/IconChevronRight"; |
| 38 | import IconChevronUp from "@tabler/icons-react-native/IconChevronUp"; |
| 39 | import IconCircle from "@tabler/icons-react-native/IconCircle"; |
| 40 | import IconCircleCheck from "@tabler/icons-react-native/IconCircleCheck"; |
| 41 | import IconCircleDashed from "@tabler/icons-react-native/IconCircleDashed"; |
| 42 | import IconCircleDot from "@tabler/icons-react-native/IconCircleDot"; |
| 43 | import IconCircleXFilled from "@tabler/icons-react-native/IconCircleXFilled"; |
| 44 | import IconTicket from "@tabler/icons-react-native/IconTicket"; |
| 45 | import IconClock from "@tabler/icons-react-native/IconClock"; |
| 46 | import IconCode from "@tabler/icons-react-native/IconCode"; |
| 47 | import IconCopy from "@tabler/icons-react-native/IconCopy"; |
| 48 | import IconDatabase from "@tabler/icons-react-native/IconDatabase"; |
| 49 | import IconDeviceDesktop from "@tabler/icons-react-native/IconDeviceDesktop"; |
| 50 | import IconDeviceLaptop from "@tabler/icons-react-native/IconDeviceLaptop"; |
| 51 | import IconDeviceMobile from "@tabler/icons-react-native/IconDeviceMobile"; |
| 52 | import IconDots from "@tabler/icons-react-native/IconDots"; |
| 53 | import IconPlusMinus from "@tabler/icons-react-native/IconPlusMinus"; |
| 54 | import IconDotsVertical from "@tabler/icons-react-native/IconDotsVertical"; |
| 55 | import IconDotsCircleHorizontal from "@tabler/icons-react-native/IconDotsCircleHorizontal"; |
| 56 | import IconEdit from "@tabler/icons-react-native/IconEdit"; |
| 57 | import IconExternalLink from "@tabler/icons-react-native/IconExternalLink"; |
| 58 | import IconEyeOff from "@tabler/icons-react-native/IconEyeOff"; |
| 59 | import IconEye from "@tabler/icons-react-native/IconEye"; |
| 60 | import IconFileText from "@tabler/icons-react-native/IconFileText"; |
| 61 | import IconFileDiff from "@tabler/icons-react-native/IconFileDiff"; |
| 62 | import IconFilter from "@tabler/icons-react-native/IconFilter"; |
| 63 | import IconFilterFilled from "@tabler/icons-react-native/IconFilterFilled"; |
| 64 | import IconFolder from "@tabler/icons-react-native/IconFolder"; |
| 65 | import IconFolderOpen from "@tabler/icons-react-native/IconFolderOpen"; |
| 66 | import IconHeadphones from "@tabler/icons-react-native/IconHeadphones"; |
| 67 | import IconFolderPlus from "@tabler/icons-react-native/IconFolderPlus"; |
| 68 | import IconGitPullRequestClosed from "@tabler/icons-react-native/IconGitPullRequestClosed"; |
| 69 | import IconGitPullRequestDraft from "@tabler/icons-react-native/IconGitPullRequestDraft"; |
| 70 | import IconGitCommit from "@tabler/icons-react-native/IconGitCommit"; |
| 71 | import IconGitBranch from "@tabler/icons-react-native/IconGitBranch"; |
| 72 | import IconGitMerge from "@tabler/icons-react-native/IconGitMerge"; |
| 73 | import IconGitPullRequest from "@tabler/icons-react-native/IconGitPullRequest"; |
| 74 | import IconHammer from "@tabler/icons-react-native/IconHammer"; |
| 75 | import IconHome from "@tabler/icons-react-native/IconHome"; |
| 76 | import IconInfoCircle from "@tabler/icons-react-native/IconInfoCircle"; |
| 77 | import IconKeyboard from "@tabler/icons-react-native/IconKeyboard"; |
| 78 | import IconKeyboardHide from "@tabler/icons-react-native/IconKeyboardHide"; |
| 79 | import IconLock from "@tabler/icons-react-native/IconLock"; |
| 80 | import IconLayoutColumns from "@tabler/icons-react-native/IconLayoutColumns"; |
| 81 | import IconLayoutSidebar from "@tabler/icons-react-native/IconLayoutSidebar"; |
| 82 | import IconLayoutSidebarRight from "@tabler/icons-react-native/IconLayoutSidebarRight"; |
| 83 | import IconLetterSpacing from "@tabler/icons-react-native/IconLetterSpacing"; |
| 84 | import IconMicrophone from "@tabler/icons-react-native/IconMicrophone"; |
| 85 | import IconLink from "@tabler/icons-react-native/IconLink"; |
| 86 | import IconListNumbers from "@tabler/icons-react-native/IconListNumbers"; |
| 87 | import IconMenu2 from "@tabler/icons-react-native/IconMenu2"; |
| 88 | import IconMessageCircleQuestion from "@tabler/icons-react-native/IconMessageCircleQuestion"; |
| 89 | import IconMessage from "@tabler/icons-react-native/IconMessage"; |
| 90 | import IconMessagePlus from "@tabler/icons-react-native/IconMessagePlus"; |
| 91 | import IconMinus from "@tabler/icons-react-native/IconMinus"; |
| 92 | import IconMoon from "@tabler/icons-react-native/IconMoon"; |
| 93 | import IconNetwork from "@tabler/icons-react-native/IconNetwork"; |
| 94 | import IconPalette from "@tabler/icons-react-native/IconPalette"; |
| 95 | import IconPencil from "@tabler/icons-react-native/IconPencil"; |
| 96 | import IconPlug from "@tabler/icons-react-native/IconPlug"; |
| 97 | import IconPhoto from "@tabler/icons-react-native/IconPhoto"; |
| 98 | import IconPin from "@tabler/icons-react-native/IconPin"; |
| 99 | import IconPinnedOff from "@tabler/icons-react-native/IconPinnedOff"; |
| 100 | import IconPlayerPlay from "@tabler/icons-react-native/IconPlayerPlay"; |
| 101 | import IconPlayerStopFilled from "@tabler/icons-react-native/IconPlayerStopFilled"; |
| 102 | import IconPlus from "@tabler/icons-react-native/IconPlus"; |
| 103 | import IconQrcode from "@tabler/icons-react-native/IconQrcode"; |
| 104 | import IconRefresh from "@tabler/icons-react-native/IconRefresh"; |
| 105 | import IconSearch from "@tabler/icons-react-native/IconSearch"; |
| 106 | import IconServer from "@tabler/icons-react-native/IconServer"; |
| 107 | import IconSettings from "@tabler/icons-react-native/IconSettings"; |
| 108 | import IconSparkles from "@tabler/icons-react-native/IconSparkles"; |
| 109 | import IconStack2 from "@tabler/icons-react-native/IconStack2"; |
| 110 | import IconStar from "@tabler/icons-react-native/IconStar"; |
| 111 | import IconStarFilled from "@tabler/icons-react-native/IconStarFilled"; |
| 112 | import IconStethoscope from "@tabler/icons-react-native/IconStethoscope"; |
| 113 | import IconSun from "@tabler/icons-react-native/IconSun"; |
| 114 | import IconShieldQuestion from "@tabler/icons-react-native/IconShieldQuestion"; |
| 115 | import IconTarget from "@tabler/icons-react-native/IconTarget"; |
| 116 | import IconTerminal2 from "@tabler/icons-react-native/IconTerminal2"; |
| 117 | import IconTextDecrease from "@tabler/icons-react-native/IconTextDecrease"; |
| 118 | import IconTextIncrease from "@tabler/icons-react-native/IconTextIncrease"; |
| 119 | import IconTool from "@tabler/icons-react-native/IconTool"; |
| 120 | import IconTrash from "@tabler/icons-react-native/IconTrash"; |
| 121 | import IconTypography from "@tabler/icons-react-native/IconTypography"; |
| 122 | import IconUpload from "@tabler/icons-react-native/IconUpload"; |
| 123 | import IconUserCircle from "@tabler/icons-react-native/IconUserCircle"; |
| 124 | import IconUserCheck from "@tabler/icons-react-native/IconUserCheck"; |
| 125 | import IconUserX from "@tabler/icons-react-native/IconUserX"; |
| 126 | import IconUserQuestion from "@tabler/icons-react-native/IconUserQuestion"; |
| 127 | import IconUserPlus from "@tabler/icons-react-native/IconUserPlus"; |
| 128 | import IconUsers from "@tabler/icons-react-native/IconUsers"; |
| 129 | import IconWifi from "@tabler/icons-react-native/IconWifi"; |
| 130 | import IconWifiOff from "@tabler/icons-react-native/IconWifiOff"; |
| 131 | import IconWorld from "@tabler/icons-react-native/IconWorld"; |
| 132 | import IconX from "@tabler/icons-react-native/IconX"; |
| 133 | import type { AndroidSymbol, SFSymbol, SymbolViewProps } from "expo-symbols"; |
| 134 | import { withUniwind } from "uniwind"; |
| 135 | |
| 136 | const ANDROID_ICON_BY_SF_SYMBOL = { |
| 137 | plusminus: IconPlusMinus, |
| 138 | "git.pull-request.closed": IconGitPullRequestClosed, |
| 139 | "git.pull-request.draft": IconGitPullRequestDraft, |
| 140 | "git.commit": IconGitCommit, |
| 141 | "arrow.branch": IconGitBranch, |
| 142 | "arrow.left": IconArrowLeft, |
| 143 | "arrow.right": IconArrowRight, |
| 144 | "arrow.left.arrow.right": IconArrowsLeftRight, |
| 145 | "arrow.clockwise": IconRefresh, |
| 146 | "arrow.down": IconArrowDown, |
| 147 | "arrow.down.circle": IconArrowDownCircle, |
| 148 | "arrow.right.circle": IconArrowRightCircle, |
| 149 | "arrow.triangle.branch": IconGitBranch, |
| 150 | "arrow.triangle.merge": IconGitMerge, |
| 151 | "arrow.triangle.pull": IconGitPullRequest, |
| 152 | "square.3.layers.3d": IconStack2, |
| 153 | "arrow.turn.left.up": IconArrowBackUp, |
| 154 | "arrow.up": IconArrowUp, |
| 155 | "arrow.up.circle": IconArrowUpCircle, |
| 156 | "arrow.up.left.and.arrow.down.right": IconArrowsDiagonal2, |
| 157 | "arrow.down.right.and.arrow.up.left": IconArrowsMinimize, |
| 158 | "arrow.up.right": IconArrowUpRight, |
| 159 | "arrow.up.right.circle": IconArrowUpRightCircle, |
| 160 | "arrow.uturn.backward": IconArrowBackUp, |
| 161 | "arrow.uturn.forward": IconArrowForwardUp, |
| 162 | archivebox: IconArchive, |
| 163 | "archivebox.fill": IconArchive, |
| 164 | "bell.badge": IconBellRinging, |
| 165 | "bolt.circle": IconBolt, |
| 166 | "bolt.horizontal.circle": IconBolt, |
| 167 | brain: IconBrain, |
| 168 | "cable.connector": IconPlug, |
| 169 | camera: IconCamera, |
| 170 | car: IconCar, |
| 171 | "chart.bar.xaxis": IconChartBar, |
| 172 | checkmark: IconCheck, |
| 173 | "checkmark.circle": IconCircleCheck, |
| 174 | "checkmark.circle.fill": IconCircleCheck, |
| 175 | circle: IconCircle, |
| 176 | "circle.dashed": IconCircleDashed, |
| 177 | "circle.inset.filled": IconCircleDot, |
| 178 | "circle.dashed.inset.filled": IconCircleDot, |
| 179 | clock: IconClock, |
| 180 | timer: IconClock, |
| 181 | ticket: IconTicket, |
| 182 | cloud: IconCloud, |
| 183 | cube: IconBox, |
| 184 | "chevron.down": IconChevronDown, |
| 185 | "chevron.left": IconChevronLeft, |
| 186 | "chevron.left.forwardslash.chevron.right": IconCode, |
| 187 | "chevron.right": IconChevronRight, |
| 188 | "chevron.up": IconChevronUp, |
| 189 | desktopcomputer: IconDeviceDesktop, |
| 190 | doc: IconFileText, |
| 191 | "doc.on.doc": IconCopy, |
| 192 | "doc.text": IconFileText, |
| 193 | "file.diff": IconFileDiff, |
| 194 | ellipsis: IconDots, |
| 195 | moon: IconMoon, |
| 196 | "ellipsis.circle": IconDotsCircleHorizontal, |
| 197 | "exclamationmark.triangle": IconAlertTriangle, |
| 198 | "exclamationmark.circle": IconAlertCircle, |
| 199 | eye: IconEye, |
| 200 | "eye.slash": IconEyeOff, |
| 201 | folder: IconFolder, |
| 202 | "folder.badge.plus": IconFolderPlus, |
| 203 | "folder.fill": IconFolder, |
| 204 | gearshape: IconSettings, |
| 205 | globe: IconWorld, |
| 206 | hammer: IconHammer, |
| 207 | headphones: IconHeadphones, |
| 208 | house: IconHome, |
| 209 | "info.circle": IconInfoCircle, |
| 210 | internaldrive: IconDatabase, |
| 211 | iphone: IconDeviceMobile, |
| 212 | keyboard: IconKeyboard, |
| 213 | laptopcomputer: IconDeviceLaptop, |
| 214 | link: IconLink, |
| 215 | "list.number": IconListNumbers, |
| 216 | "line.3.horizontal": IconMenu2, |
| 217 | "line.3.horizontal.decrease": IconFilter, |
| 218 | "line.3.horizontal.decrease.circle": IconFilter, |
| 219 | "line.3.horizontal.decrease.circle.fill": IconFilterFilled, |
| 220 | // Tabler has no Apple desktops; the closest silhouettes stand in on Android. |
| 221 | macmini: IconServer, |
| 222 | macstudio: IconDeviceDesktop, |
| 223 | magnifyingglass: IconSearch, |
| 224 | mic: IconMicrophone, |
| 225 | paintbrush: IconPalette, |
| 226 | pencil: IconPencil, |
| 227 | "person.crop.circle": IconUserCircle, |
| 228 | "person.2": IconUsers, |
| 229 | "person.fill.checkmark": IconUserCheck, |
| 230 | "person.fill.xmark": IconUserX, |
| 231 | "person.badge.clock": IconUserQuestion, |
| 232 | "person.badge.plus": IconUserPlus, |
| 233 | photo: IconPhoto, |
| 234 | pin: IconPin, |
| 235 | "pin.slash": IconPinnedOff, |
| 236 | play: IconPlayerPlay, |
| 237 | plus: IconPlus, |
| 238 | minus: IconMinus, |
| 239 | "qrcode.viewfinder": IconQrcode, |
| 240 | "point.3.connected.trianglepath.dotted": IconNetwork, |
| 241 | "point.topleft.down.curvedto.point.bottomright.up": IconGitMerge, |
| 242 | safari: IconExternalLink, |
| 243 | "server.rack": IconServer, |
| 244 | stethoscope: IconStethoscope, |
| 245 | "sidebar.left": IconLayoutSidebar, |
| 246 | "sidebar.right": IconLayoutSidebarRight, |
| 247 | "slider.horizontal.3": IconAdjustmentsHorizontal, |
| 248 | "square.and.pencil": IconEdit, |
| 249 | "square.on.square": IconCopy, |
| 250 | "square.grid.2x2": IconApps, |
| 251 | "square.split.2x1": IconLayoutColumns, |
| 252 | star: IconStar, |
| 253 | "star.fill": IconStarFilled, |
| 254 | "sun.max": IconSun, |
| 255 | "stop.fill": IconPlayerStopFilled, |
| 256 | target: IconTarget, |
| 257 | terminal: IconTerminal2, |
| 258 | "text.alignleft": IconAlignLeft, |
| 259 | "text.bubble": IconMessage, |
| 260 | "questionmark.bubble": IconMessageCircleQuestion, |
| 261 | "exclamationmark.shield": IconShieldQuestion, |
| 262 | "bubble.left.and.bubble.right": IconMessage, |
| 263 | "bubble.left": IconMessage, |
| 264 | sparkles: IconSparkles, |
| 265 | "plus.bubble": IconMessagePlus, |
| 266 | "text.word.spacing": IconLetterSpacing, |
| 267 | "textformat.size": IconTypography, |
| 268 | "textformat.size.larger": IconTextIncrease, |
| 269 | "textformat.size.smaller": IconTextDecrease, |
| 270 | "tray.and.arrow.up": IconUpload, |
| 271 | trash: IconTrash, |
| 272 | wifi: IconWifi, |
| 273 | "wifi.slash": IconWifiOff, |
| 274 | xmark: IconX, |
| 275 | "xmark.circle.fill": IconCircleXFilled, |
| 276 | } satisfies Partial< |
| 277 | Record< |
| 278 | SFSymbol | "git.pull-request.closed" | "git.pull-request.draft" | "git.commit" | "file.diff", |
| 279 | Icon |
| 280 | > |
| 281 | >; |
| 282 | const SF_ICON_LOOKUP: Partial< |
| 283 | Record< |
| 284 | SFSymbol | "git.pull-request.closed" | "git.pull-request.draft" | "git.commit" | "file.diff", |
| 285 | Icon |
| 286 | > |
| 287 | > = ANDROID_ICON_BY_SF_SYMBOL; |
| 288 | |
| 289 | // Callers can pass `{ ios, android }` names where `android` is a Material |
| 290 | // icon name (the raw expo-symbols contract). Resolve those here too so the |
| 291 | // android key keeps working through this wrapper — it wins over the SF map |
| 292 | // when both match (e.g. folder vs folder_open for expanded project groups). |
| 293 | const ANDROID_ICON_BY_MATERIAL_NAME = { |
| 294 | auto_awesome: IconSparkles, |
| 295 | bolt: IconBolt, |
| 296 | build: IconTool, |
| 297 | chat: IconMessage, |
| 298 | chat_bubble: IconMessage, |
| 299 | check: IconCheck, |
| 300 | close: IconX, |
| 301 | construction: IconHammer, |
| 302 | content_copy: IconCopy, |
| 303 | desktop_windows: IconDeviceDesktop, |
| 304 | edit: IconEdit, |
| 305 | error: IconAlertCircle, |
| 306 | folder: IconFolder, |
| 307 | folder_open: IconFolderOpen, |
| 308 | keyboard: IconKeyboard, |
| 309 | keyboard_arrow_down: IconChevronDown, |
| 310 | keyboard_arrow_up: IconChevronUp, |
| 311 | keyboard_hide: IconKeyboardHide, |
| 312 | lock: IconLock, |
| 313 | more_vert: IconDotsVertical, |
| 314 | merge: IconGitMerge, |
| 315 | psychology: IconBrain, |
| 316 | public: IconWorld, |
| 317 | remove: IconMinus, |
| 318 | smartphone: IconDeviceMobile, |
| 319 | terminal: IconTerminal2, |
| 320 | visibility: IconEye, |
| 321 | } satisfies Partial<Record<AndroidSymbol, Icon>>; |
| 322 | |
| 323 | export type { SFSymbol } from "expo-symbols"; |
| 324 | export type AppSymbolName = |
| 325 | | keyof typeof ANDROID_ICON_BY_SF_SYMBOL |
| 326 | | { |
| 327 | ios: SFSymbol; |
| 328 | android: keyof typeof ANDROID_ICON_BY_MATERIAL_NAME; |
| 329 | }; |
| 330 | |
| 331 | export type AppSymbolViewProps = Omit<SymbolViewProps, "name"> & { name: AppSymbolName }; |
| 332 | |
| 333 | export function isAppSymbolName(name: string): name is Extract<AppSymbolName, string> { |
| 334 | return Object.prototype.hasOwnProperty.call(ANDROID_ICON_BY_SF_SYMBOL, name); |
| 335 | } |
| 336 | |
| 337 | function AppSymbolView(props: AppSymbolViewProps) { |
| 338 | const materialName = typeof props.name === "string" ? undefined : props.name.android; |
| 339 | const sfSymbol = typeof props.name === "string" ? props.name : props.name.ios; |
| 340 | const AndroidIcon = |
| 341 | (materialName ? ANDROID_ICON_BY_MATERIAL_NAME[materialName] : undefined) ?? |
| 342 | (sfSymbol ? SF_ICON_LOOKUP[sfSymbol] : undefined); |
| 343 | |
| 344 | if (!AndroidIcon) { |
| 345 | return props.fallback ?? null; |
| 346 | } |
| 347 | |
| 348 | return ( |
| 349 | <AndroidIcon |
| 350 | accessibilityLabel={props.accessibilityLabel} |
| 351 | color={props.tintColor} |
| 352 | size={props.size} |
| 353 | strokeWidth={2} |
| 354 | style={props.style} |
| 355 | testID={props.testID} |
| 356 | /> |
| 357 | ); |
| 358 | } |
| 359 | |
| 360 | /** |
| 361 | * expo-symbols and the Android Tabler fallback both expose tint as a native |
| 362 | * prop rather than a React Native style. Keep that third-party boundary here |
| 363 | * so callers can use Uniwind's `tintColorClassName` instead of subscribing to |
| 364 | * theme variables in every parent component. |
| 365 | */ |
| 366 | export const SymbolView = withUniwind(AppSymbolView); |