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