Path: blob/master/src/packages/frontend/components/icon.tsx
5843 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45declare var DEBUG: boolean; // comes from static webpack; not defined in other contexts.67import React from "react";89import { CSS } from "@cocalc/frontend/app-framework";10import useOnFrontend from "./use-on-frontend";1112import {13AimOutlined,14AlignCenterOutlined,15AlignLeftOutlined,16AlignRightOutlined,17ApiOutlined,18AppstoreOutlined,19AreaChartOutlined,20ArrowDownOutlined,21ArrowLeftOutlined,22ArrowRightOutlined,23ArrowUpOutlined,24ArrowsAltOutlined,25AudioOutlined,26BackwardOutlined,27BankOutlined,28BellFilled,29BellOutlined,30BoldOutlined,31BookOutlined,32BorderOutlined,33BugOutlined,34BulbOutlined,35CalculatorOutlined,36CalendarOutlined,37CameraOutlined,38CaretDownFilled,39CaretLeftFilled,40CaretRightFilled,41CaretUpFilled,42CheckCircleOutlined,43CheckOutlined,44CheckSquareOutlined,45ClearOutlined,46ClockCircleOutlined,47CloseCircleFilled,48CloseCircleOutlined,49CloseCircleTwoTone,50CloseOutlined,51CloudDownloadOutlined,52CloudFilled,53CloudServerOutlined,54CloudUploadOutlined,55ClusterOutlined,56CodeOutlined,57CoffeeOutlined,58ColumnHeightOutlined,59ColumnWidthOutlined,60CommentOutlined,61CompassOutlined,62ContainerOutlined,63ControlOutlined,64CopyOutlined,65CreditCardOutlined,66DashboardOutlined,67DatabaseFilled,68DatabaseOutlined,69DeleteOutlined,70DeliveredProcedureOutlined,71DeploymentUnitOutlined,72DesktopOutlined,73DoubleLeftOutlined,74DoubleRightOutlined,75DownCircleOutlined,76DownOutlined,77DownSquareOutlined,78DownloadOutlined,79EditFilled,80EditOutlined,81EllipsisOutlined,82ExclamationCircleFilled,83ExpandOutlined,84ExperimentOutlined,85ExportOutlined,86EyeInvisibleOutlined,87EyeOutlined,88FacebookFilled,89FacebookOutlined,90FieldTimeOutlined,91FileImageOutlined,92FileOutlined,93FilePdfOutlined,94FileTextOutlined,95FileZipOutlined,96FireOutlined,97FolderOpenOutlined,98FolderOutlined,99FontSizeOutlined,100ForkOutlined,101ForwardOutlined,102FrownOutlined,103FunctionOutlined,104FundProjectionScreenOutlined,105GatewayOutlined,106GiftOutlined,107GiftTwoTone,108GithubFilled,109GithubOutlined,110GlobalOutlined,111GoogleOutlined,112GroupOutlined,113HddOutlined,114HighlightOutlined,115HistoryOutlined,116HomeOutlined,117HourglassOutlined,118Html5Outlined,119IdcardOutlined,120InfoCircleOutlined,121InfoOutlined,122InstagramFilled,123InstagramOutlined,124ItalicOutlined,125KeyOutlined,126LaptopOutlined,127LayoutOutlined,128LeftCircleOutlined,129LeftOutlined,130LeftSquareFilled,131LineChartOutlined,132LineHeightOutlined,133LinkOutlined,134LinkedinFilled,135LinkedinOutlined,136LoadingOutlined,137LockFilled,138LockOutlined,139LoginOutlined,140MailOutlined,141MedicineBoxOutlined,142MehOutlined,143MenuOutlined,144MergeCellsOutlined,145MinusCircleOutlined,146MinusOutlined,147MinusSquareOutlined,148OrderedListOutlined,149PauseCircleOutlined,150PercentageOutlined,151PicCenterOutlined,152PieChartOutlined,153PlayCircleFilled,154PlayCircleOutlined,155PlaySquareOutlined,156PlusCircleFilled,157PlusCircleOutlined,158PlusOutlined,159PlusSquareOutlined,160PoweroffOutlined,161PrinterOutlined,162ProjectOutlined,163QuestionCircleOutlined,164RedoOutlined,165ReloadOutlined,166RetweetOutlined,167RightCircleFilled,168RightCircleOutlined,169RightOutlined,170RightSquareFilled,171RiseOutlined,172RobotOutlined,173RocketOutlined,174SaveOutlined,175ScissorOutlined,176SearchOutlined,177SelectOutlined,178SendOutlined,179SettingOutlined,180ShareAltOutlined,181ShoppingCartOutlined,182ShrinkOutlined,183SignatureOutlined,184SlidersOutlined,185SmileOutlined,186SolutionOutlined,187SoundOutlined,188StarFilled,189StarOutlined,190StepBackwardOutlined,191StepForwardOutlined,192StopFilled,193StopOutlined,194StrikethroughOutlined,195SwapLeftOutlined,196SwapOutlined,197SwapRightOutlined,198SyncOutlined,199TableOutlined,200TagFilled,201TagOutlined,202TagTwoTone,203TagsFilled,204TagsOutlined,205TagsTwoTone,206TeamOutlined,207ThunderboltOutlined,208ToTopOutlined,209ToolOutlined,210TranslationOutlined,211UnderlineOutlined,212UndoOutlined,213UnlockFilled,214UnorderedListOutlined,215UpCircleOutlined,216UpOutlined,217UpSquareOutlined,218UploadOutlined,219UserAddOutlined,220UserDeleteOutlined,221UserOutlined,222UsergroupAddOutlined,223VerticalAlignBottomOutlined,224VerticalAlignMiddleOutlined,225VerticalLeftOutlined,226VerticalRightOutlined,227VideoCameraOutlined,228WalletOutlined,229WarningOutlined,230WifiOutlined,231XOutlined,232YoutubeFilled,233YoutubeOutlined,234ZoomInOutlined,235ZoomOutOutlined,236} from "@ant-design/icons";237238// Unfortunately -- "error TS7056: The inferred type of this node exceeds the maximum length the239// compiler will serialize. An explicit type annotation is needed."240const IconSpec = {241"address-card": IdcardOutlined,242aim: AimOutlined,243"align-left": AlignLeftOutlined,244"align-center": AlignCenterOutlined,245"align-justify": { IconFont: "align-justify" },246"align-right": AlignRightOutlined,247"angle-double-left": DoubleLeftOutlined,248"angle-double-right": DoubleRightOutlined,249"angle-down": DownOutlined,250"angle-right": RightOutlined,251"arrow-circle-o-left": { IconFont: "arrowcircleoleft" },252"arrow-circle-down": DownCircleOutlined,253"arrow-circle-up": UpCircleOutlined,254"arrow-down": ArrowDownOutlined,255"arrow-left": ArrowLeftOutlined,256"arrow-right": ArrowRightOutlined,257"arrow-up": ArrowUpOutlined,258atom: { IconFont: "Atom" },259api: ApiOutlined,260audio: AudioOutlined,261aws: { IconFont: "aws" },262backward: BackwardOutlined,263"battery-empty": { IconFont: "battery-empty" },264"battery-quarter": { IconFont: "battery-quarter" },265"battery-half": { IconFont: "battery-half" },266"battery-three-quarters": { IconFont: "battery-three-quarters" },267"battery-full": { IconFont: "battery-full" },268ban: StopOutlined,269bank: BankOutlined,270bars: { IconFont: "bars" },271bell: BellFilled,272"bell-o": BellOutlined,273blog: { IconFont: "blog" },274bold: BoldOutlined,275bolt: ThunderboltOutlined,276book: BookOutlined,277briefcase: { IconFont: "briefcase" },278brush: { IconFont: "brush" },279bullhorn: { IconFont: "bullhorn" },280bug: BugOutlined,281calculator: CalculatorOutlined,282calendar: CalendarOutlined,283"calendar-week": { IconFont: "calendar-week" },284"calendar-check": { IconFont: "calendar-check" },285"calendar-times": { IconFont: "calendar-times" },286camera: CameraOutlined,287"caret-down": CaretDownFilled,288"caret-left": CaretLeftFilled,289"caret-right": CaretRightFilled,290"caret-up": CaretUpFilled,291"caret-square-left": LeftSquareFilled,292"caret-square-right": RightSquareFilled,293"cc-discover": { IconFont: "cc-discover" },294"cc-mastercard": { IconFont: "cc-mastercard" },295"cc-visa": { IconFont: "cc-visa" },296"cc-stripe": { IconFont: "cc-stripe" },297areaChart: AreaChartOutlined,298check: CheckOutlined,299"check-circle": CheckCircleOutlined,300"check-square": CheckSquareOutlined,301"check-square-o": CheckSquareOutlined,302"chevron-down": DownOutlined,303"chevron-left": LeftOutlined,304"chevron-right": RightOutlined,305"chevron-circle-right": RightCircleFilled,306"chevron-up": UpOutlined,307circle: { IconFont: "circle" },308"circle-notch": LoadingOutlined,309clipboard: { IconFont: "clipboard" },310"clipboard-check": { IconFont: "clipboard-check" },311clock: ClockCircleOutlined,312"close-circle-two-tone": CloseCircleTwoTone,313"close-circle-filled": CloseCircleFilled,314clone: { IconFont: "clone" },315cloud: CloudFilled,316"cloud-dev": { IconFont: "cloud-dev" },317"cloud-download": CloudDownloadOutlined,318"cloud-download-alt": CloudDownloadOutlined,319"cloud-upload": CloudUploadOutlined,320"cocalc-ring": { IconFont: "cocalc-ring" },321code: { IconFont: "code" },322"code-outlined": CodeOutlined,323CodeOutlined,324coffee: CoffeeOutlined,325cog: ControlOutlined,326cogs: ControlOutlined,327colors: { IconFont: "colors" },328ColumnHeightOutlined,329ColumnWidthOutlined,330comment: CommentOutlined,331comments: CommentOutlined,332compass: CompassOutlined,333compress: ShrinkOutlined,334container: ContainerOutlined,335copy: CopyOutlined,336"credit-card": CreditCardOutlined,337csv: { IconFont: "csv" },338cube: { IconFont: "cube" },339cut: ScissorOutlined,340dashboard: DashboardOutlined,341database: DatabaseOutlined,342"database-filled": DatabaseFilled,343"deployment-unit": DeploymentUnitOutlined,344dedicated: DatabaseOutlined, // icon for "dedicated resources", looks like a server rack345desktop: DesktopOutlined,346"delivered-procedure-outlined": DeliveredProcedureOutlined,347digitalocean: { IconFont: "digitalocean" },348discord: { IconFont: "discord" },349"disk-drive": { IconFont: "disk-drive" },350"disk-round": { IconFont: "disk-round" },351"disk-snapshot": { IconFont: "disk-snapshot" },352dns: { IconFont: "dns" },353docker: { IconFont: "docker" },354download: DownloadOutlined,355"dot-circle": { IconFont: "dot-circle" },356edit: EditOutlined,357"edit-filled": EditFilled,358eraser: { IconFont: "Eraser-Tool" },359ellipsis: EllipsisOutlined,360envelope: { IconFont: "envelope" },361exchange: { IconFont: "exchange" },362"exclamation-circle": ExclamationCircleFilled,363"exclamation-triangle": WarningOutlined,364expand: ExpandOutlined,365"expand-arrows": ArrowsAltOutlined,366experiment: ExperimentOutlined,367"external-link": { IconFont: "external-link-alt" },368eye: EyeOutlined,369"eye-slash": EyeInvisibleOutlined,370"facebook-filled": FacebookFilled,371facebook: FacebookOutlined,372file: FileOutlined,373"file-archive": FileZipOutlined,374"file-alt": FileTextOutlined,375"file-code": FileTextOutlined,376"file-image": FileImageOutlined,377"file-pdf": FilePdfOutlined,378"file-zip": FileZipOutlined,379files: CopyOutlined,380"file-export": ExportOutlined,381fire: FireOutlined,382firefox: { IconFont: "firefox" },383flash: ThunderboltOutlined,384"flow-chart": { IconFont: "flow-chart" },385folder: FolderOutlined,386"folder-open": FolderOpenOutlined,387font: { IconFont: "font" },388"font-size": FontSizeOutlined,389forward: ForwardOutlined,390frame: { IconFont: "frame" },391frown: FrownOutlined,392fx: FunctionOutlined,393slides: FundProjectionScreenOutlined,394"project-outlined": ProjectOutlined,395"gateway-outlined": GatewayOutlined,396gavel: { IconFont: "gavel" },397gears: ControlOutlined,398gear: ControlOutlined,399gift: GiftOutlined,400gift2: GiftTwoTone,401"github-filled": GithubFilled,402github: GithubOutlined,403git: { IconFont: "git1" },404"git-square": { IconFont: "git-square" },405global: GlobalOutlined,406emacs: { IconFont: "gnuemacs" },407google: GoogleOutlined,408googlecloud: { IconFont: "googlecloud" },409"graduation-cap": { IconFont: "graduation" },410graph: { IconFont: "graph" },411grass: { IconFont: "grass" },412group: { IconFont: "group" },413"group-outlined": GroupOutlined,414gpu: { IconFont: "gpu" },415hand: { IconFont: "hand" },416"hand-stop": PoweroffOutlined,417header: { IconFont: "header" },418heart: { IconFont: "heart" },419hdd: HddOutlined,420highlighter: HighlightOutlined,421history: HistoryOutlined,422home: HomeOutlined,423"horizontal-split": { IconFont: "horizontal-split" },424"hourglass-half": HourglassOutlined,425html5: Html5Outlined,426image: { IconFont: "image" },427icons: { IconFont: "icons" },428"info-circle": InfoCircleOutlined,429indent: { IconFont: "indent" },430info: InfoOutlined,431inkscape: { IconFont: "inkscape" },432"instagram-filled": InstagramFilled,433instagram: InstagramOutlined,434ipynb: { IconFont: "ipynb" },435italic: ItalicOutlined,436"js-square": { IconFont: "js-square" },437julia: { IconFont: "julia" },438jupyter: { IconFont: "ipynb" },439key: KeyOutlined,440keyboard: { IconFont: "keyboard" },441laptop: LaptopOutlined,442layout: LayoutOutlined,443"skull-crossbones": { IconFont: "leave_conference" },444libreoffice: { IconFont: "libreoffice" },445"life-ring": { IconFont: "life-ring" },446"life-saver": { IconFont: "life-ring" },447lightbulb: BulbOutlined,448"line-chart": LineChartOutlined,449link: LinkOutlined,450"linkedin-filled": LinkedinFilled,451linkedin: LinkedinOutlined,452linux: { IconFont: "linux" },453list: UnorderedListOutlined,454"list-ul": UnorderedListOutlined,455"list-alt": UnorderedListOutlined,456"list-ol": OrderedListOutlined,457"lock-open": UnlockFilled,458lock: LockFilled,459"lock-outlined": LockOutlined,460magic: { IconFont: "magic" },461mail: MailOutlined,462map: { IconFont: "map" },463markdown: { IconFont: "markdown" },464mask: { IconFont: "mask" },465maple: { IconFont: "maple" },466mathematica: { IconFont: "mathematica" },467matlab: { IconFont: "matlab" },468medkit: MedicineBoxOutlined,469"menu-outlined": MenuOutlined,470meh: MehOutlined,471microchip: { IconFont: "microchip" },472microsoft: { IconFont: "microsoft" },473"minus-circle": MinusCircleOutlined,474"minus-square": MinusSquareOutlined,475money: CreditCardOutlined,476"money-check": { IconFont: "money-check" },477mousepointer: { IconFont: "mousepointer" },478move: { IconFont: "move" },479arrows: { IconFont: "move" }, // fa-arrows is used by the bqplot custom widget480network: { IconFont: "network" },481"network-server": { IconFont: "network-server" },482"network-wired": ClusterOutlined,483"node-js": { IconFont: "node-js" },484note: { IconFont: "note-text" },485nvidia: { IconFont: "nvidia" },486octave: { IconFont: "octave" },487overview: AppstoreOutlined,488outdent: { IconFont: "outdent" },489pause: PauseCircleOutlined,490"paper-plane": SendOutlined,491paste: { IconFont: "paste" },492"pic-centered": PicCenterOutlined,493pen: { IconFont: "pen" },494pencil: EditOutlined,495"pencil-alt": EditOutlined,496percentage: PercentageOutlined,497"pie-chart": PieChartOutlined,498play: PlayCircleOutlined,499"play-circle": PlayCircleFilled,500"play-square": PlaySquareOutlined,501plus: PlusOutlined,502minus: MinusOutlined,503"plus-circle": PlusCircleOutlined,504"plus-circle-o": PlusCircleOutlined,505"plus-circle-filled": PlusCircleFilled,506"plus-one": { IconFont: "plus-one" },507"plus-square": PlusSquareOutlined,508"plus-square-o": PlusSquareOutlined,509PoweroffOutlined,510print: PrinterOutlined,511python: { IconFont: "python" },512pytorch: { IconFont: "pytorch" },513qgis: { IconFont: "qgis" },514"question-circle": QuestionCircleOutlined,515"quote-left": { IconFont: "quote-left" },516r: { IconFont: "r" },517racket: { IconFont: "racket" },518redo: RedoOutlined,519refresh: RedoOutlined,520reload: ReloadOutlined,521remove: CloseOutlined,522repeat: RedoOutlined,523replace: { IconFont: "find-replace" },524reply: { IconFont: "reply" },525retweet: RetweetOutlined,526robot: RobotOutlined,527rocket: RocketOutlined,528run: { IconFont: "run" },529sagemath: { IconFont: "sagemath" },530"sagemath-bold": { IconFont: "sagemath-bold" },531"sagemath-file": { IconFont: "sagemath-file" },532save: SaveOutlined,533scheme: { IconFont: "scheme" },534scissors: ScissorOutlined,535search: SearchOutlined,536"search-minus": ZoomOutOutlined,537"search-plus": ZoomInOutlined,538"select-outlined": SelectOutlined,539settings: SettingOutlined,540server: CloudServerOutlined,541servers: { IconFont: "servers" },542"sign-in": LoginOutlined,543"sign-out-alt": LoginOutlined, // Yes, since the logout one breaks darkreader, weirdly! they both look reasonable.544sitemap: ClusterOutlined,545"share-square": ShareAltOutlined,546"shopping-cart": ShoppingCartOutlined,547sliders: SlidersOutlined,548smile: SmileOutlined,549"sort-amount-up": { IconFont: "sort-amount-up" },550spinner: { IconFont: "cocalc-ring" },551square: BorderOutlined,552solution: SolutionOutlined,553"square-o": BorderOutlined,554"square-root-alt": { IconFont: "square-root-alt" },555"star-filled": StarFilled,556star: StarOutlined,557"step-backward": StepBackwardOutlined,558"step-forward": StepForwardOutlined,559stop: { IconFont: "stop" }, // the ant-design "stop" looks weird.560"stop-filled": StopFilled,561stopwatch: FieldTimeOutlined,562store: { IconFont: "store" },563strikethrough: StrikethroughOutlined,564subscript: { IconFont: "subscript" },565sun: { IconFont: "sun" },566superscript: { IconFont: "superscript" },567support: { IconFont: "life-ring" },568sync: { IconFont: "sync" },569"sync-alt": SyncOutlined,570tab: { IconFont: "tab" },571table: TableOutlined,572"tachometer-alt": DashboardOutlined,573tasks: { IconFont: "tasks" },574"tag-outlined": TagOutlined,575"tags-outlined": TagsOutlined,576"tag-filled": TagFilled,577"tags-filled": TagsFilled,578"tag-two-tone": TagTwoTone,579"tags-two-tone": TagsTwoTone,580"team-outlined": TeamOutlined,581tensorflow: { IconFont: "tensorflow" },582terminal: CodeOutlined,583tex: { IconFont: "tex" },584text: { IconFont: "text" },585text1: { IconFont: "text1" },586"tex-file": { IconFont: "tex-file" },587"text-height": LineHeightOutlined,588times: CloseOutlined,589"times-circle": CloseCircleOutlined,590"times-rectangle": { IconFont: "times-rectangle" },591"thumbs-down": { IconFont: "thumbs-down" },592"thumbs-up": { IconFont: "thumbs-up" },593"toggle-off": { IconFont: "toggle-off" },594"toggle-on": { IconFont: "toggle-on" },595tool: ToolOutlined,596trash: DeleteOutlined,597"twitter-filled": XOutlined,598twitter: XOutlined,599ubuntu: { IconFont: "ubuntu" },600underline: UnderlineOutlined,601undo: UndoOutlined,602ungroup: { IconFont: "ungroup" },603"signature-outlined": SignatureOutlined,604swap: SwapOutlined,605"sync-left": SwapLeftOutlined,606"sync-right": SwapRightOutlined,607unlink: { IconFont: "unlink" },608upload: UploadOutlined,609user: UserOutlined,610"user-secret": { IconFont: "user-secret" },611UserAddOutlined,612"user-check": { IconFont: "user-check" },613"user-plus": UsergroupAddOutlined,614"user-slash": { IconFont: "user-slash" },615"user-times": UserDeleteOutlined,616users: UsergroupAddOutlined,617"vertical-split": { IconFont: "vertical-split" },618"vertical-right-outlined": VerticalRightOutlined,619"video-camera": VideoCameraOutlined,620"vertical-align-middle": VerticalAlignMiddleOutlined,621"vertical-align-bottom": VerticalAlignBottomOutlined,622"vertical-left-outlined": VerticalLeftOutlined,623vim: { IconFont: "vim" },624vscode: { IconFont: "vscode" },625wallet: WalletOutlined,626warning: WarningOutlined,627wifi: WifiOutlined,628"window-maximize": { IconFont: "window-maximize" },629"window-restore": DesktopOutlined, // we only use for x11 and this has big X.630wrench: { IconFont: "wrench" },631youtube: YoutubeOutlined,632"youtube-filled": YoutubeFilled,633"left-circle-o": LeftCircleOutlined,634"right-circle-o": RightCircleOutlined,635"down-circle-o": DownCircleOutlined,636"translation-outlined": TranslationOutlined,637"clean-outlined": ClearOutlined,638"sound-outlined": SoundOutlined,639"rise-outlined": RiseOutlined,640"up-square-outlined": UpSquareOutlined,641"down-square-outlined": DownSquareOutlined,642"merge-cells-outlined": MergeCellsOutlined,643"fork-outlined": ForkOutlined,644"to-top-outlined": ToTopOutlined,645"column-width": ColumnWidthOutlined,646"column-height": ColumnHeightOutlined,647} as const;648649// Icon Fonts coming from https://www.iconfont.cn/?lang=en-us650import { createFromIconfontCN } from "@ant-design/icons";651export let IconFont: any = undefined;652try {653if (typeof window != "undefined") {654// obviously won't work if window is undefined based on looking at the code...655// This loads a bunch of svg elements of the form <svg id="icon-<name>"... into the DOM.656// The antd Icon code then duplicates these via the <use> html tag657// (https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use)658require("./iconfont.cn");659// note -- we do NOT pass scriptUrl in, as in the docs! Why? Because660// we want everything bundled up into webpack, rather than having to pull661// from some random place, which just causes confusion with releases662// and caching. Fortunately, just evaluating the js from iconfont, then663// running createFromIconfontCN with no arguments does work, as I deduced664// by reading the code, then trying this.665// https://github.com/ant-design/ant-design-icons/blob/5be2afd296636ab4cfec5d3a2793d6cd41b1789b/packages/icons-vue/src/components/IconFont.tsx666667IconFont = createFromIconfontCN();668669// It would be easy to screw up and put an entry like670// "arrow-circle-o-left": { IconFont: "arrowcircleoleft" }671// in IconSpec, but forget to actually include "arrowcircleoleft" in672// iconfont.cn, or -- just as bad -- make a typo or put the wrong name in.673// So we double check that all iconfonts are actually defined here:674if (typeof DEBUG != "undefined" && DEBUG) {675setTimeout(() => {676// only do this during dev to save time.677for (const name in IconSpec) {678const spec = IconSpec[name];679const x = spec?.IconFont;680if (x != null) {681const id = `icon-${x}`;682if (document.getElementById(id) == null) {683console.error(684`ERROR -- the IconFont ${x} is not in components/iconfont.cn! Fix this or the icon ${name} will be broken.`,685);686}687}688}689}, 5000);690}691}692} catch (err) {693// Might as well have option for a graceful fallback, e.g., when694// used from node.js...695if (!process.env.COCALC_TEST_MODE) {696console.log(`IconFont not available -- ${err}`);697}698}699700// This used to exceed TypeScript limits, but apparently it is ok now…701export type IconName = keyof typeof IconSpec;702export const IconName = undefined; // Javascript needs this, though we are only using IconName for the type703704// Typeguard so can tell if a string is name of an icon and also705// make typescript happy.706export function isIconName(name?: unknown): name is IconName {707if (name == null) return false;708if (typeof name !== "string") return false;709return IconSpec[name] != null;710}711712export const iconNames: IconName[] = Object.keys(IconSpec) as any;713714export type IconRotation = "45" | "90" | "135" | "180" | "225" | "270" | "315";715716interface Props {717name?: IconName;718unicode?: number; // (optional) set a hex 16 bit charcode to render a unicode char, e.g. 0x2620719className?: string;720size?: "lg" | "2x" | "3x" | "4x" | "5x";721rotate?: IconRotation;722flip?: "horizontal" | "vertical";723spin?: boolean;724pulse?: boolean;725stack?: "1x" | "2x";726inverse?: boolean;727style?: CSS;728onClick?: (event?: React.MouseEvent) => void; // https://fettblog.eu/typescript-react/events/729onMouseOver?: () => void;730onMouseOut?: () => void;731onMouseEnter?: () => void;732onMouseLeave?: () => void;733}734735const UNICODE_STYLE = {736fontSize: "120%",737fontWeight: "bold",738lineHeight: "1",739verticalAlign: "middle",740} as React.CSSProperties;741742const missing: any = {};743// Converted from https://github.com/andreypopp/react-fa744745export const Icon: React.FC<Props> = (props: Props) => {746// IMPORTANT: This hook is needed for next.js to support server side rendering.747// Otherwise, at least with next 13, it crashes when rendering icons.748const onFrontend = useOnFrontend();749if (!onFrontend) return null;750751if (props.unicode != null) {752const style: React.CSSProperties = { ...UNICODE_STYLE, ...props.style };753754// Apply CSS transformations for unicode characters755if (props.rotate) {756style.display = style.display ?? "inline-block";757style.transform = `rotate(${props.rotate}deg)`;758}759if (props.flip) {760style.display = style.display ?? "inline-block";761const flipTransform =762props.flip === "horizontal" ? "scaleX(-1)" : "scaleY(-1)";763style.transform = style.transform764? `${style.transform} ${flipTransform}`765: flipTransform;766}767768return (769<span style={style}>770{String.fromCharCode(props.unicode!)}771</span>772);773}774775let name: IconName = props.name ?? "square";776let C;777C = IconSpec[name];778if (C == null && name.endsWith("-o")) {779// should be impossible because of typescript...780// try without -o781C = IconSpec[name.slice(0, name.length - 2)];782}783if (C != null) {784if (typeof C.IconFont == "string") {785// @ts-ignore786if (IconFont == null) {787return <span>(IconFonts not available)</span>;788}789return <IconFont type={"icon-" + C.IconFont} {...props} alt={name} />;790}791return <C {...props} alt={name} />;792}793794// this is when the icon is broken.795if (typeof DEBUG != "undefined" && DEBUG) {796if (missing[props.name ?? ""] == null) {797missing[props.name ?? ""] = true;798console.warn(799`Icon "${props.name}" is not defined -- fix this in components/icon.tsx.`,800);801}802// make it hopefully clear to devs that this icon is broken803return (804<span805style={{ background: "red", color: "white" }}806className="blink"807title={`Icon "${props.name}" is not defined -- fix this in components/icon.tsx.`}808>809{/* @ts-ignore */}810<BugOutlined {...props} alt={name} />811</span>812);813} else {814// In production, just show a very generic icon so the user815// doesn't realize we messed up.816// @ts-ignore817return <BorderOutlined {...props} alt={name} />;818}819};820821// TOOD move this to a shared lib, which can import the IconName type822export const PAYASYOUGO_ICON: IconName = "compass";823824825