From d00a23d5644af21d72e2ad7b4ac3edfbfe9806db Mon Sep 17 00:00:00 2001 From: itsmeow Date: Tue, 31 Dec 2024 05:41:03 -0500 Subject: [PATCH] TGUI Minor Updates (2024 Pre-React Typescript Edition) (#12066) * color.js: Fix alpha specification (#61324) Bitwise OR coerces to an integral value. This is fine for the rgb components as they have a domain of [0,255], but is less useful for the alpha component, which has a domain of [0,1]. This retains the safely of | while permitting colors that are neither fully opaque nor fully transparent, including NaN handling. * tgui: Add new and disambiguate old gas colors (#61381) This PR adds color mappings for new gases, and disambiguates color mappings for old gases where multiple gases used one color - many used red, or purple. Most of these have been around for a while, but were missing color mappings in interfaces. This mainly affects the HFR gas list interface. * Fixes progress bars (#61604) Co-authored-by: Watermelon914 <3052169-Watermelon914@users.noreply.gitlab.com> * New string ops for tgui (#68337) * initial commit * by suggestion Co-Authored-By: Aleksej Komarov Co-authored-by: Aleksej Komarov * Automatically insert current year for TGUI bluescreen (#72490) Title Less hassle * Renames folder for tgchat CSS styles (#73907) I think it was previously named "goon" in relation to "goonchat", but that was phased out three years ago, so it's just confusing to have a "goon" folder especially when historically those tend to refer to specialized folders meant for containing content under a non aGPL license However, everything in this folder appears to just be styles ported from goonchat when tgchat was created, which is fine, but it's just confusing since none of the stuff is licensed under anything from goon- just an odd name to my eyes. It's a much more fitting name as to what these files specifically pertain too, rather than be an arcane reference to a system that has not been used in quite a while... nothing that concerns players * Refactors some core tgui components to typescript (#74547) Mind you I am very tired while making this and it's purely a thought project (... unless?) Otherwise, title. No gameplay changes Typescript is just universally better. Even if the types are wrong, we still can get some sort of idea what the author was going for, and autocompletion :cl: refactor: Tgui's state manager is in typescript now, huzzah /:cl: --------- Co-authored-by: Jeremiah * Converts core tgui to ts (#74638) Converts the remaining core tgui files into typescript Builds tests for these files Completed some todos to rewrite/refactor functions Typescript good N/A nothing player facing --------- Co-authored-by: Jeremiah * Dropdown selected element highlighting (#75255) This PR makes selected elements in Dropdowns to be highlighted. Without this: ![image](https://user-images.githubusercontent.com/5000549/236760396-5eb71a23-7b3d-4a9e-98d4-6bd4dc5fe1c7.png) With this: ![image](https://user-images.githubusercontent.com/5000549/236758675-b2ab90a7-0482-492e-b911-9e59f827f6b8.png) It's convenient to have this kind of selection indication, especially in long lists. :cl: qol: Made selected elements highlighted in TGUI Dropdowns /:cl: * Fix: Fixed dropdown buttons empty selected case (#75309) ## About The Pull Request If dropdown not receive `selected` prop, buttons will be change value to first(when press next) or last element(when press back) ## Changelog :cl: fix: Fixed dropdown buttons empty selected case /:cl: * TGUI timer.ts and keys.ts port (partial https://github.com/tgstation/tgstation/pull/75431) * Prettier * JSX Conversions * Fix Debug Stories not picking up JSX * Bump a bunch of packages (soft port of https://github.com/tgstation/tgstation/pull/79895) * Fix ESLint * Switch from Terser to ESBuild for minification (https://github.com/tgstation/tgstation/pull/79916) * Drops ie8 support (#79974) We currently transpile TGUI down into ie8. This was primarily for Linux compatibility, which, to my understanding, hasn't worked for quite some time. As we approach 2024, consider that ie8 is nearly 15 years old and had its support ended in 2016. I believe sunsetting ie8 is in order. >I have no objections to remove IE8 support and fully target IE11. https://github.com/tgstation/tgstation/issues/79943#issuecomment-1826735705_ - Probably improves performance to some degree, you're only as slow as your weakest link. - Reduced maintenance burden N/A if they can even see this message it doesn't affect them * Support for newer browsers (my own implementation of https://github.com/tgstation/tgstation/pull/79244) * Fix Typescript errors * Prettier --------- Co-authored-by: esainane Co-authored-by: Watermelon914 <37270891+Watermelon914@users.noreply.github.com> Co-authored-by: Watermelon914 <3052169-Watermelon914@users.noreply.gitlab.com> Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com> Co-authored-by: Aleksej Komarov Co-authored-by: Zephyr <12817816+ZephyrTFA@users.noreply.github.com> Co-authored-by: san7890 Co-authored-by: Jeremiah Co-authored-by: Serge K Lebedev Co-authored-by: KoJIT2009 --- .github/CODEOWNERS | 4 +- code/controllers/subsystem/tgui.dm | 1 + interface/stylesheet.dm | 2 +- tgui/.eslintrc.yml | 2 +- tgui/.yarnrc.yml | 5 + tgui/babel.config.js | 2 +- tgui/docs/component-reference.md | 6 +- tgui/global.d.ts | 2 + tgui/jest.config.js | 6 +- tgui/package.json | 70 +- tgui/packages/common/color.ts | 10 +- .../common/{keycodes.js => keycodes.ts} | 0 tgui/packages/common/keys.ts | 39 + tgui/packages/common/redux.test.ts | 58 + tgui/packages/common/{redux.js => redux.ts} | 122 +- tgui/packages/common/string.js | 32 + tgui/packages/common/{timer.js => timer.ts} | 26 +- tgui/packages/tgui-bench/index.js | 2 +- tgui/packages/tgui-bench/package.json | 10 +- .../tgui-bench/tests/DisposalUnit.test.tsx | 7 +- tgui/packages/tgui-dev-server/package.json | 6 +- .../{Notifications.js => Notifications.jsx} | 0 .../tgui-panel/{Panel.js => Panel.jsx} | 4 +- ...wPlayingWidget.js => NowPlayingWidget.jsx} | 0 .../tgui-panel/audio/{index.js => index.ts} | 0 ...atPageSettings.js => ChatPageSettings.jsx} | 0 .../chat/{ChatPanel.js => ChatPanel.jsx} | 0 .../chat/{ChatTabs.js => ChatTabs.jsx} | 0 .../chat/{constants.js => constants.ts} | 0 .../tgui-panel/chat/{index.js => index.ts} | 0 .../chat/{renderer.js => renderer.jsx} | 2 +- .../game/{constants.js => constants.ts} | 0 .../tgui-panel/game/{index.js => index.ts} | 0 .../tgui-panel/{index.js => index.jsx} | 0 tgui/packages/tgui-panel/package.json | 2 +- .../{PingIndicator.js => PingIndicator.jsx} | 0 .../ping/{constants.js => constants.ts} | 0 .../tgui-panel/ping/{index.js => index.ts} | 0 .../{SettingsPanel.js => SettingsPanel.jsx} | 0 .../settings/{constants.js => constants.ts} | 0 .../settings/{index.js => index.ts} | 0 tgui/packages/tgui-panel/styles/main.scss | 4 +- .../styles/{goon => tgchat}/chat-dark.scss | 0 .../styles/{goon => tgchat}/chat-light.scss | 0 .../tgui-panel/styles/themes/light.scss | 4 +- tgui/packages/tgui-polyfill/00-html5shiv.js | 330 - tgui/packages/tgui-polyfill/01-ie8.js | 836 --- tgui/packages/tgui-polyfill/02-dom4.js | 970 --- tgui/packages/tgui-polyfill/03-css-om.js | 44 - tgui/packages/tgui-polyfill/1-misc.js | 51 + tgui/packages/tgui-polyfill/10-misc.js | 61 - tgui/packages/tgui-polyfill/package.json | 10 +- tgui/packages/tgui-say/package.json | 2 +- tgui/packages/tgui/assets.ts | 67 +- .../{AnimatedNumber.js => AnimatedNumber.jsx} | 0 .../tgui/components/{Blink.js => Blink.jsx} | 0 .../{BlockQuote.js => BlockQuote.jsx} | 0 .../tgui/components/BufferedTextArea.tsx | 2 +- .../tgui/components/{Button.js => Button.jsx} | 0 .../components/{ByondUi.js => ByondUi.jsx} | 0 .../tgui/components/{Chart.js => Chart.jsx} | 0 .../{Collapsible.js => Collapsible.jsx} | 0 ...sibleSection.js => CollapsibleSection.jsx} | 0 .../components/{ColorBox.js => ColorBox.jsx} | 0 .../tgui/components/{Dimmer.js => Dimmer.jsx} | 0 .../components/{Divider.js => Divider.jsx} | 0 ...ntrol.js => DraggableClickableControl.jsx} | 0 ...aggableControl.js => DraggableControl.jsx} | 0 tgui/packages/tgui/components/Dropdown.tsx | 50 +- .../tgui/components/{Grid.js => Grid.jsx} | 0 .../{InfinitePlane.js => InfinitePlane.jsx} | 0 .../tgui/components/{Input.js => Input.jsx} | 0 .../tgui/components/{Knob.js => Knob.jsx} | 0 ...LabeledControls.js => LabeledControls.jsx} | 0 .../tgui/components/{Modal.js => Modal.jsx} | 0 .../{NoticeBox.js => NoticeBox.jsx} | 0 .../{NumberInput.js => NumberInput.jsx} | 0 ...apComponent.js => OrbitalMapComponent.jsx} | 0 .../{OrbitalMapSvg.js => OrbitalMapSvg.jsx} | 0 tgui/packages/tgui/components/ProgressBar.js | 31 - tgui/packages/tgui/components/ProgressBar.jsx | 42 + ...RestrictedInput.js => RestrictedInput.jsx} | 0 .../{ScrollableBox.js => ScrollableBox.jsx} | 0 tgui/packages/tgui/components/Section.tsx | 14 +- .../tgui/components/{Slider.js => Slider.jsx} | 0 .../tgui/components/{Table.js => Table.jsx} | 0 .../tgui/components/{Tabs.js => Tabs.jsx} | 0 .../components/{TextArea.js => TextArea.jsx} | 0 .../{TimeDisplay.js => TimeDisplay.jsx} | 0 .../tgui/components/{index.js => index.ts} | 0 .../tgui/{constants.js => constants.ts} | 109 +- tgui/packages/tgui/contants.test.ts | 72 + .../debug/{KitchenSink.js => KitchenSink.jsx} | 2 +- .../tgui/debug/{index.js => index.ts} | 0 tgui/packages/tgui/{drag.js => drag.ts} | 91 +- tgui/packages/tgui/events.test.ts | 60 + tgui/packages/tgui/{events.js => events.ts} | 58 +- tgui/packages/tgui/{focus.js => focus.ts} | 0 tgui/packages/tgui/format.js | 98 - tgui/packages/tgui/format.test.ts | 112 + tgui/packages/tgui/format.ts | 157 + tgui/packages/tgui/{index.js => index.tsx} | 9 +- ...AbductorConsole.js => AbductorConsole.jsx} | 0 .../{Achievements.js => Achievements.jsx} | 0 .../interfaces/{AdminFax.js => AdminFax.jsx} | 0 ...nSecretsPanel.js => AdminSecretsPanel.jsx} | 0 ...oller.js => AdvancedAirlockController.jsx} | 0 .../{AiAirlock.js => AiAirlock.jsx} | 0 .../{AiRestorer.js => AiRestorer.jsx} | 0 .../interfaces/{AirAlarm.js => AirAlarm.jsx} | 0 ...kElectronics.js => AirlockElectronics.jsx} | 0 .../tgui/interfaces/{Apc.js => Apc.jsx} | 0 .../interfaces/{Aquarium.js => Aquarium.jsx} | 0 ...sAlertConsole.js => AtmosAlertConsole.jsx} | 0 ...trolConsole.js => AtmosControlConsole.jsx} | 0 .../{AtmosFilter.js => AtmosFilter.jsx} | 2 +- .../{AtmosMixer.js => AtmosMixer.jsx} | 0 .../{AtmosPump.js => AtmosPump.jsx} | 0 .../{AtmosTempGate.js => AtmosTempGate.jsx} | 0 .../{AtmosTempPump.js => AtmosTempPump.jsx} | 0 ...ouncement.js => AutomatedAnnouncement.jsx} | 0 .../{BankMachine.js => BankMachine.jsx} | 0 .../{BanningPanel.js => BanningPanel.jsx} | 0 .../{Biogenerator.js => Biogenerator.jsx} | 0 ...aceArtillery.js => BluespaceArtillery.jsx} | 0 ...uespaceLocator.js => BluespaceLocator.jsx} | 0 .../{BluespaceTap.js => BluespaceTap.jsx} | 0 .../{BorgPanel.js => BorgPanel.jsx} | 0 .../{BountyBoard.js => BountyBoard.jsx} | 0 .../{BrigTimer.js => BrigTimer.jsx} | 0 .../{CameraConsole.js => CameraConsole.jsx} | 0 .../interfaces/{Canister.js => Canister.jsx} | 0 .../tgui/interfaces/{Canvas.js => Canvas.jsx} | 0 .../tgui/interfaces/{Cargo.js => Cargo.jsx} | 0 ...ountyConsole.js => CargoBountyConsole.jsx} | 0 .../{CargoExpress.js => CargoExpress.jsx} | 0 ...oHoldTerminal.js => CargoHoldTerminal.jsx} | 0 ...llularEmporium.js => CellularEmporium.jsx} | 0 ...mPodLauncher.js => CentcomPodLauncher.jsx} | 0 .../{ChemAcclimator.js => ChemAcclimator.jsx} | 0 ...ynthesizer.js => ChemDebugSynthesizer.jsx} | 0 .../{ChemDispenser.js => ChemDispenser.jsx} | 0 .../{ChemFilter.js => ChemFilter.jsx} | 0 .../{ChemHeater.js => ChemHeater.jsx} | 0 .../{ChemMaster.js => ChemMaster.jsx} | 0 ...tionChamber.js => ChemReactionChamber.jsx} | 0 .../{ChemSplitter.js => ChemSplitter.jsx} | 0 ...ChemSynthesizer.js => ChemSynthesizer.jsx} | 0 .../{CircuitModule.js => CircuitModule.jsx} | 0 .../{Clipboard.js => Clipboard.jsx} | 0 .../{ClockworkSlab.js => ClockworkSlab.jsx} | 0 .../{CloningConsole.js => CloningConsole.jsx} | 0 .../{CodexGigas.js => CodexGigas.jsx} | 0 ...rMatrixEditor.js => ColorMatrixEditor.jsx} | 0 ...nsConsole.js => CommunicationsConsole.jsx} | 0 ...erFabricator.js => ComputerFabricator.jsx} | 0 .../tgui/interfaces/{Crayon.js => Crayon.jsx} | 0 .../tgui/interfaces/{Cryo.js => Cryo.jsx} | 0 .../{DisposalUnit.js => DisposalUnit.jsx} | 0 .../{DnaConsole.js => DnaConsole.jsx} | 0 .../interfaces/{DnaVault.js => DnaVault.jsx} | 0 .../{EightBallVote.js => EightBallVote.jsx} | 0 .../{Electropack.js => Electropack.jsx} | 0 .../interfaces/{Elevator.js => Elevator.jsx} | 0 .../{EmagConsole.js => EmagConsole.jsx} | 0 ...Console.js => EmergencyShuttleConsole.jsx} | 0 ...EmojiInputModal.js => EmojiInputModal.jsx} | 0 ...EngravedMessage.js => EngravedMessage.jsx} | 0 ...olConsole.js => ExosuitControlConsole.jsx} | 0 .../{Filteriffic.js => Filteriffic.jsx} | 0 .../{FishCatalog.js => FishCatalog.jsx} | 0 .../tgui/interfaces/{Folder.js => Folder.jsx} | 0 .../{ForbiddenLore.js => ForbiddenLore.jsx} | 0 ...eConsole.js => FugitiveCaptureConsole.jsx} | 0 .../tgui/interfaces/{GenPop.js => GenPop.jsx} | 0 ...lProtection.js => GhostPoolProtection.jsx} | 0 .../{GlandDispenser.js => GlandDispenser.jsx} | 0 .../tgui/interfaces/{Gps.js => Gps.jsx} | 0 ...avityGenerator.js => GravityGenerator.jsx} | 0 ...temReclaimer.js => GulagItemReclaimer.jsx} | 0 ...rConsole.js => GulagTeleporterConsole.jsx} | 0 .../interfaces/{Holodeck.js => Holodeck.jsx} | 0 .../{HypnoChair.js => HypnoChair.jsx} | 0 .../{ImplantChair.js => ImplantChair.jsx} | 0 ...InfraredEmitter.js => InfraredEmitter.jsx} | 0 .../{BasicInput.js => BasicInput.jsx} | 0 .../{CircuitInfo.js => CircuitInfo.jsx} | 0 .../{Connections.js => Connections.jsx} | 2 +- .../{DisplayName.js => DisplayName.jsx} | 0 ...ndamentalTypes.js => FundamentalTypes.jsx} | 0 ...ObjectComponent.js => ObjectComponent.jsx} | 4 +- .../IntegratedCircuit/{Port.js => Port.jsx} | 0 .../{VariableMenu.js => VariableMenu.jsx} | 0 .../{constants.js => constants.ts} | 0 .../IntegratedCircuit/{index.js => index.jsx} | 0 .../{Intellicard.js => Intellicard.jsx} | 0 .../{Interview.js => Interview.jsx} | 0 ...terviewManager.js => InterviewManager.jsx} | 0 .../interfaces/{Jukebox.js => Jukebox.jsx} | 0 .../{KeycardAuth.js => KeycardAuth.jsx} | 0 ...rClaimConsole.js => LaborClaimConsole.jsx} | 0 .../{LanguageMenu.js => LanguageMenu.jsx} | 0 ...unchpadConsole.js => LaunchpadConsole.jsx} | 0 ...LaunchpadRemote.js => LaunchpadRemote.jsx} | 0 .../tgui/interfaces/ListInputModal.tsx | 6 +- ...owerConsole.js => MechBayPowerConsole.jsx} | 0 .../{MessageMonitor.js => MessageMonitor.jsx} | 0 .../{MiningVendor.js => MiningVendor.jsx} | 0 .../tgui/interfaces/{Mint.js => Mint.jsx} | 0 ...larFabricator.js => ModularFabricator.jsx} | 0 .../tgui/interfaces/{Morph.js => Morph.jsx} | 0 .../tgui/interfaces/{Mule.js => Mule.jsx} | 0 ...berControl.js => NaniteChamberControl.jsx} | 0 ...CloudControl.js => NaniteCloudControl.jsx} | 0 ...niteProgramHub.js => NaniteProgramHub.jsx} | 0 ...niteProgrammer.js => NaniteProgrammer.jsx} | 0 .../{NaniteRemote.js => NaniteRemote.jsx} | 0 .../{Newscaster.js => Newscaster.jsx} | 0 .../{NoticeBoard.js => NoticeBoard.jsx} | 0 .../{NtnetRelay.js => NtnetRelay.jsx} | 0 .../{NtosAiRestorer.js => NtosAiRestorer.jsx} | 0 ...rlockControl.js => NtosAirlockControl.jsx} | 0 .../{NtosArcade.js => NtosArcade.jsx} | 0 .../{NtosAtmos.js => NtosAtmos.jsx} | 0 ...NtosBountyBoard.js => NtosBountyBoard.jsx} | 0 ...BountyConsole.js => NtosBountyConsole.jsx} | 0 .../interfaces/{NtosCard.js => NtosCard.jsx} | 0 .../{NtosCargo.js => NtosCargo.jsx} | 2 +- ...Configuration.js => NtosConfiguration.jsx} | 0 ...osCrewManifest.js => NtosCrewManifest.jsx} | 0 ...Monitor.js => NtosCyborgRemoteMonitor.jsx} | 0 ...s => NtosCyborgRemoteMonitorSyndicate.jsx} | 0 ...lfMonitor.js => NtosCyborgSelfMonitor.jsx} | 0 ...NtosEmagConsole.js => NtosEmagConsole.jsx} | 0 ...NtosFileManager.js => NtosFileManager.jsx} | 0 ...ostRbmkStats.js => NtosGhostRbmkStats.jsx} | 0 .../{NtosJobManager.js => NtosJobManager.jsx} | 0 .../{NtosLogViewer.js => NtosLogViewer.jsx} | 0 .../interfaces/{NtosMain.js => NtosMain.jsx} | 0 .../{NtosMessenger.js => NtosMessenger.jsx} | 0 .../{NtosNetChat.js => NtosNetChat.jsx} | 0 .../{NtosNetDos.js => NtosNetDos.jsx} | 0 ...NetDownloader.js => NtosNetDownloader.jsx} | 0 .../{NtosNetMonitor.js => NtosNetMonitor.jsx} | 0 .../{NtosNewscaster.js => NtosNewscaster.jsx} | 0 .../{NtosNotepad.js => NtosNotepad.jsx} | 0 ...NtosPhysScanner.js => NtosPhysScanner.jsx} | 0 ...raitPrinter.js => NtosPortraitPrinter.jsx} | 0 ...osPowerMonitor.js => NtosPowerMonitor.jsx} | 0 .../{NtosRadar.js => NtosRadar.jsx} | 0 ...darSyndicate.js => NtosRadarSyndicate.jsx} | 0 .../{NtosRbmkStats.js => NtosRbmkStats.jsx} | 0 .../{NtosRecords.js => NtosRecords.jsx} | 0 .../{NtosRevelation.js => NtosRevelation.jsx} | 0 ...NtosRoboControl.js => NtosRoboControl.jsx} | 0 .../{NtosSecurEye.js => NtosSecurEye.jsx} | 0 .../{NtosSignaller.js => NtosSignaller.jsx} | 0 ...Console.js => NtosStationAlertConsole.jsx} | 0 ...rMonitor.js => NtosSupermatterMonitor.jsx} | 0 .../{NuclearBomb.js => NuclearBomb.jsx} | 0 .../{Objective.js => Objective.jsx} | 0 ...atingComputer.js => OperatingComputer.jsx} | 0 .../tgui/interfaces/{Orbit.js => Orbit.jsx} | 0 .../{OrbitalMap.js => OrbitalMap.jsx} | 0 .../tgui/interfaces/{OreBox.js => OreBox.jsx} | 0 ...ionMachine.js => OreRedemptionMachine.jsx} | 0 .../{OutfitEditor.js => OutfitEditor.jsx} | 0 .../{OutfitManager.js => OutfitManager.jsx} | 0 .../{PDAInputModal.js => PDAInputModal.jsx} | 0 .../packages/tgui/interfaces/PaiInterface.tsx | 18 +- .../interfaces/{Pandemic.js => Pandemic.jsx} | 0 ...Accelerator.js => ParticleAccelerator.jsx} | 0 .../{Particool.js => Particool.jsx} | 0 ...rsonalCrafting.js => PersonalCrafting.jsx} | 0 .../{Photocopier.js => Photocopier.jsx} | 0 ...alNewscaster.js => PhysicalNewscaster.jsx} | 0 ...eSelectModal.js => PictureSelectModal.jsx} | 0 ...Manipulator.js => PlantDNAManipulator.jsx} | 0 .../{PlayerPanel.js => PlayerPanel.jsx} | 0 ...ableGenerator.js => PortableGenerator.jsx} | 0 .../{PortablePump.js => PortablePump.jsx} | 0 ...rtableScrubber.js => PortableScrubber.jsx} | 0 ...momachine.js => PortableThermomachine.jsx} | 0 .../{PortraitPicker.js => PortraitPicker.jsx} | 0 .../{PowerMonitor.js => PowerMonitor.jsx} | 0 .../{ProbingConsole.js => ProbingConsole.jsx} | 0 ...ProximitySensor.js => ProximitySensor.jsx} | 0 .../{PsychicPlane.js => PsychicPlane.jsx} | 0 .../{RDConsole.js => RDConsole.jsx} | 0 .../tgui/interfaces/{Radio.js => Radio.jsx} | 0 ...icrolaser.js => RadioactiveMicrolaser.jsx} | 0 ...ipeDispenser.js => RapidPipeDispenser.jsx} | 0 ...RbmkControlRods.js => RbmkControlRods.jsx} | 0 .../{ReligiousTool.js => ReligiousTool.jsx} | 0 ...RobotControl.js => RemoteRobotControl.jsx} | 0 .../{RequestManager.js => RequestManager.jsx} | 0 ...lConsole.js => RoboticsControlConsole.jsx} | 0 ...telliteControl.js => SatelliteControl.jsx} | 0 .../{ScannerGate.js => ScannerGate.jsx} | 0 .../{SeedExtractor.js => SeedExtractor.jsx} | 0 ...SelectEquipment.js => SelectEquipment.jsx} | 0 ...tleDesignator.js => ShuttleDesignator.jsx} | 0 ...eManipulator.js => ShuttleManipulator.jsx} | 0 .../interfaces/{Signaler.js => Signaler.jsx} | 0 tgui/packages/tgui/interfaces/SimpleBot.tsx | 15 +- .../interfaces/{Sleeper.js => Sleeper.jsx} | 0 ...imeBodySwapper.js => SlimeBodySwapper.jsx} | 0 .../{SmartVend.js => SmartVend.jsx} | 0 .../interfaces/{Smelter.js => Smelter.jsx} | 0 .../tgui/interfaces/{Smes.js => Smes.jsx} | 0 .../{SmokeMachine.js => SmokeMachine.jsx} | 0 .../{SolarControl.js => SolarControl.jsx} | 0 .../{SpawnersMenu.js => SpawnersMenu.jsx} | 0 .../tgui/interfaces/{Stack.js => Stack.jsx} | 0 ...lertConsole.js => StationAlertConsole.jsx} | 0 ...atterMonitor.js => SupermatterMonitor.jsx} | 0 .../{SyndContractor.js => SyndContractor.jsx} | 0 .../{TachyonArray.js => TachyonArray.jsx} | 0 .../tgui/interfaces/{Tank.js => Tank.jsx} | 0 .../{TankDispenser.js => TankDispenser.jsx} | 0 .../interfaces/{TechFab.js => TechFab.jsx} | 0 .../interfaces/{Techweb.js => Techweb.jsx} | 0 .../{Telecomms.js => Telecomms.jsx} | 0 .../{Teleporter.js => Teleporter.jsx} | 0 .../{ThermoMachine.js => ThermoMachine.jsx} | 0 .../{TicketBrowser.js => TicketBrowser.jsx} | 0 ...TicketMessenger.js => TicketMessenger.jsx} | 0 .../tgui/interfaces/{Timer.js => Timer.jsx} | 0 ...TrackedPlaytime.js => TrackedPlaytime.jsx} | 0 ...kstoryMenu.js => TraitorBackstoryMenu.jsx} | 0 .../{TransferValve.js => TransferValve.jsx} | 0 ...TurbineComputer.js => TurbineComputer.jsx} | 0 .../{TurretControl.js => TurretControl.jsx} | 0 .../tgui/interfaces/{Uplink.js => Uplink.jsx} | 0 .../{Vendatray.js => Vendatray.jsx} | 0 tgui/packages/tgui/interfaces/Vending.tsx | 3 +- .../tgui/interfaces/{Vote.js => Vote.jsx} | 20 +- .../tgui/interfaces/{Wires.js => Wires.jsx} | 0 .../interfaces/{Workshop.js => Workshop.jsx} | 0 ...factConsole.js => XenoartifactConsole.jsx} | 0 ...factLabeler.js => XenoartifactLabeler.jsx} | 0 .../common/{AccessList.js => AccessList.jsx} | 0 .../{AtmosControls.js => AtmosControls.jsx} | 0 .../{BeakerContents.js => BeakerContents.jsx} | 0 ...oticeBox.js => InterfaceLockNoticeBox.jsx} | 0 .../{PortableAtmos.js => PortableAtmos.jsx} | 0 .../{KitchenSink.js => KitchenSink.jsx} | 0 .../tgui/layouts/{Layout.js => Layout.jsx} | 0 .../layouts/{NtosWindow.js => NtosWindow.jsx} | 0 .../tgui/layouts/{Pane.js => Pane.jsx} | 0 .../tgui/layouts/{Window.js => Window.jsx} | 0 .../tgui/layouts/{index.js => index.ts} | 0 tgui/packages/tgui/links.js | 34 - tgui/packages/tgui/links.test.ts | 76 + tgui/packages/tgui/links.ts | 45 + tgui/packages/tgui/{logging.js => logging.ts} | 29 +- tgui/packages/tgui/package.json | 10 +- tgui/packages/tgui/{routes.js => routes.tsx} | 23 +- tgui/packages/tgui/sanitize.test.ts | 33 + .../tgui/{sanitize.js => sanitize.ts} | 14 +- tgui/packages/tgui/store.js | 92 - tgui/packages/tgui/store.ts | 111 + .../{Blink.stories.js => Blink.stories.jsx} | 0 ...uote.stories.js => BlockQuote.stories.jsx} | 0 .../{Box.stories.js => Box.stories.jsx} | 0 .../{Button.stories.js => Button.stories.jsx} | 0 ...ByondUi.stories.js => ByondUi.stories.jsx} | 0 ...ble.stories.js => Collapsible.stories.jsx} | 0 .../{Flex.stories.js => Flex.stories.jsx} | 0 .../{Input.stories.js => Input.stories.jsx} | 0 .../{Popper.stories.js => Popper.stories.jsx} | 0 .../tgui/stories/ProgressBar.stories.js | 36 - .../tgui/stories/ProgressBar.stories.jsx | 47 + .../{Stack.stories.js => Stack.stories.jsx} | 0 ...Storage.stories.js => Storage.stories.jsx} | 0 .../{Tabs.stories.js => Tabs.stories.jsx} | 0 .../{Themes.stories.js => Themes.stories.jsx} | 0 ...Tooltip.stories.js => Tooltip.stories.jsx} | 0 .../tgui/stories/{common.js => common.jsx} | 0 .../tgui/styles/components/Dropdown.scss | 5 + .../tgui/styles/components/ProgressBar.scss | 4 +- tgui/public/tgui-polyfill.min.js | 2 +- tgui/public/tgui.html | 8 +- tgui/tsconfig.json | 2 +- tgui/webpack.config.js | 45 +- tgui/yarn.lock | 6146 ++++++++++------- 386 files changed, 5306 insertions(+), 5234 deletions(-) rename tgui/packages/common/{keycodes.js => keycodes.ts} (100%) create mode 100644 tgui/packages/common/keys.ts create mode 100644 tgui/packages/common/redux.test.ts rename tgui/packages/common/{redux.js => redux.ts} (50%) rename tgui/packages/common/{timer.js => timer.ts} (54%) rename tgui/packages/tgui-panel/{Notifications.js => Notifications.jsx} (100%) rename tgui/packages/tgui-panel/{Panel.js => Panel.jsx} (98%) rename tgui/packages/tgui-panel/audio/{NowPlayingWidget.js => NowPlayingWidget.jsx} (100%) rename tgui/packages/tgui-panel/audio/{index.js => index.ts} (100%) rename tgui/packages/tgui-panel/chat/{ChatPageSettings.js => ChatPageSettings.jsx} (100%) rename tgui/packages/tgui-panel/chat/{ChatPanel.js => ChatPanel.jsx} (100%) rename tgui/packages/tgui-panel/chat/{ChatTabs.js => ChatTabs.jsx} (100%) rename tgui/packages/tgui-panel/chat/{constants.js => constants.ts} (100%) rename tgui/packages/tgui-panel/chat/{index.js => index.ts} (100%) rename tgui/packages/tgui-panel/chat/{renderer.js => renderer.jsx} (99%) rename tgui/packages/tgui-panel/game/{constants.js => constants.ts} (100%) rename tgui/packages/tgui-panel/game/{index.js => index.ts} (100%) rename tgui/packages/tgui-panel/{index.js => index.jsx} (100%) rename tgui/packages/tgui-panel/ping/{PingIndicator.js => PingIndicator.jsx} (100%) rename tgui/packages/tgui-panel/ping/{constants.js => constants.ts} (100%) rename tgui/packages/tgui-panel/ping/{index.js => index.ts} (100%) rename tgui/packages/tgui-panel/settings/{SettingsPanel.js => SettingsPanel.jsx} (100%) rename tgui/packages/tgui-panel/settings/{constants.js => constants.ts} (100%) rename tgui/packages/tgui-panel/settings/{index.js => index.ts} (100%) rename tgui/packages/tgui-panel/styles/{goon => tgchat}/chat-dark.scss (100%) rename tgui/packages/tgui-panel/styles/{goon => tgchat}/chat-light.scss (100%) delete mode 100644 tgui/packages/tgui-polyfill/00-html5shiv.js delete mode 100644 tgui/packages/tgui-polyfill/01-ie8.js delete mode 100644 tgui/packages/tgui-polyfill/02-dom4.js delete mode 100644 tgui/packages/tgui-polyfill/03-css-om.js create mode 100644 tgui/packages/tgui-polyfill/1-misc.js delete mode 100644 tgui/packages/tgui-polyfill/10-misc.js rename tgui/packages/tgui/components/{AnimatedNumber.js => AnimatedNumber.jsx} (100%) rename tgui/packages/tgui/components/{Blink.js => Blink.jsx} (100%) rename tgui/packages/tgui/components/{BlockQuote.js => BlockQuote.jsx} (100%) rename tgui/packages/tgui/components/{Button.js => Button.jsx} (100%) rename tgui/packages/tgui/components/{ByondUi.js => ByondUi.jsx} (100%) rename tgui/packages/tgui/components/{Chart.js => Chart.jsx} (100%) rename tgui/packages/tgui/components/{Collapsible.js => Collapsible.jsx} (100%) rename tgui/packages/tgui/components/{CollapsibleSection.js => CollapsibleSection.jsx} (100%) rename tgui/packages/tgui/components/{ColorBox.js => ColorBox.jsx} (100%) rename tgui/packages/tgui/components/{Dimmer.js => Dimmer.jsx} (100%) rename tgui/packages/tgui/components/{Divider.js => Divider.jsx} (100%) rename tgui/packages/tgui/components/{DraggableClickableControl.js => DraggableClickableControl.jsx} (100%) rename tgui/packages/tgui/components/{DraggableControl.js => DraggableControl.jsx} (100%) rename tgui/packages/tgui/components/{Grid.js => Grid.jsx} (100%) rename tgui/packages/tgui/components/{InfinitePlane.js => InfinitePlane.jsx} (100%) rename tgui/packages/tgui/components/{Input.js => Input.jsx} (100%) rename tgui/packages/tgui/components/{Knob.js => Knob.jsx} (100%) rename tgui/packages/tgui/components/{LabeledControls.js => LabeledControls.jsx} (100%) rename tgui/packages/tgui/components/{Modal.js => Modal.jsx} (100%) rename tgui/packages/tgui/components/{NoticeBox.js => NoticeBox.jsx} (100%) rename tgui/packages/tgui/components/{NumberInput.js => NumberInput.jsx} (100%) rename tgui/packages/tgui/components/{OrbitalMapComponent.js => OrbitalMapComponent.jsx} (100%) rename tgui/packages/tgui/components/{OrbitalMapSvg.js => OrbitalMapSvg.jsx} (100%) delete mode 100644 tgui/packages/tgui/components/ProgressBar.js create mode 100644 tgui/packages/tgui/components/ProgressBar.jsx rename tgui/packages/tgui/components/{RestrictedInput.js => RestrictedInput.jsx} (100%) rename tgui/packages/tgui/components/{ScrollableBox.js => ScrollableBox.jsx} (100%) rename tgui/packages/tgui/components/{Slider.js => Slider.jsx} (100%) rename tgui/packages/tgui/components/{Table.js => Table.jsx} (100%) rename tgui/packages/tgui/components/{Tabs.js => Tabs.jsx} (100%) rename tgui/packages/tgui/components/{TextArea.js => TextArea.jsx} (100%) rename tgui/packages/tgui/components/{TimeDisplay.js => TimeDisplay.jsx} (100%) rename tgui/packages/tgui/components/{index.js => index.ts} (100%) rename tgui/packages/tgui/{constants.js => constants.ts} (61%) create mode 100644 tgui/packages/tgui/contants.test.ts rename tgui/packages/tgui/debug/{KitchenSink.js => KitchenSink.jsx} (95%) rename tgui/packages/tgui/debug/{index.js => index.ts} (100%) rename tgui/packages/tgui/{drag.js => drag.ts} (69%) create mode 100644 tgui/packages/tgui/events.test.ts rename tgui/packages/tgui/{events.js => events.ts} (76%) rename tgui/packages/tgui/{focus.js => focus.ts} (100%) delete mode 100644 tgui/packages/tgui/format.js create mode 100644 tgui/packages/tgui/format.test.ts create mode 100644 tgui/packages/tgui/format.ts rename tgui/packages/tgui/{index.js => index.tsx} (98%) rename tgui/packages/tgui/interfaces/{AbductorConsole.js => AbductorConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{Achievements.js => Achievements.jsx} (100%) rename tgui/packages/tgui/interfaces/{AdminFax.js => AdminFax.jsx} (100%) rename tgui/packages/tgui/interfaces/{AdminSecretsPanel.js => AdminSecretsPanel.jsx} (100%) rename tgui/packages/tgui/interfaces/{AdvancedAirlockController.js => AdvancedAirlockController.jsx} (100%) rename tgui/packages/tgui/interfaces/{AiAirlock.js => AiAirlock.jsx} (100%) rename tgui/packages/tgui/interfaces/{AiRestorer.js => AiRestorer.jsx} (100%) rename tgui/packages/tgui/interfaces/{AirAlarm.js => AirAlarm.jsx} (100%) rename tgui/packages/tgui/interfaces/{AirlockElectronics.js => AirlockElectronics.jsx} (100%) rename tgui/packages/tgui/interfaces/{Apc.js => Apc.jsx} (100%) rename tgui/packages/tgui/interfaces/{Aquarium.js => Aquarium.jsx} (100%) rename tgui/packages/tgui/interfaces/{AtmosAlertConsole.js => AtmosAlertConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{AtmosControlConsole.js => AtmosControlConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{AtmosFilter.js => AtmosFilter.jsx} (100%) rename tgui/packages/tgui/interfaces/{AtmosMixer.js => AtmosMixer.jsx} (100%) rename tgui/packages/tgui/interfaces/{AtmosPump.js => AtmosPump.jsx} (100%) rename tgui/packages/tgui/interfaces/{AtmosTempGate.js => AtmosTempGate.jsx} (100%) rename tgui/packages/tgui/interfaces/{AtmosTempPump.js => AtmosTempPump.jsx} (100%) rename tgui/packages/tgui/interfaces/{AutomatedAnnouncement.js => AutomatedAnnouncement.jsx} (100%) rename tgui/packages/tgui/interfaces/{BankMachine.js => BankMachine.jsx} (100%) rename tgui/packages/tgui/interfaces/{BanningPanel.js => BanningPanel.jsx} (100%) rename tgui/packages/tgui/interfaces/{Biogenerator.js => Biogenerator.jsx} (100%) rename tgui/packages/tgui/interfaces/{BluespaceArtillery.js => BluespaceArtillery.jsx} (100%) rename tgui/packages/tgui/interfaces/{BluespaceLocator.js => BluespaceLocator.jsx} (100%) rename tgui/packages/tgui/interfaces/{BluespaceTap.js => BluespaceTap.jsx} (100%) rename tgui/packages/tgui/interfaces/{BorgPanel.js => BorgPanel.jsx} (100%) rename tgui/packages/tgui/interfaces/{BountyBoard.js => BountyBoard.jsx} (100%) rename tgui/packages/tgui/interfaces/{BrigTimer.js => BrigTimer.jsx} (100%) rename tgui/packages/tgui/interfaces/{CameraConsole.js => CameraConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{Canister.js => Canister.jsx} (100%) rename tgui/packages/tgui/interfaces/{Canvas.js => Canvas.jsx} (100%) rename tgui/packages/tgui/interfaces/{Cargo.js => Cargo.jsx} (100%) rename tgui/packages/tgui/interfaces/{CargoBountyConsole.js => CargoBountyConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{CargoExpress.js => CargoExpress.jsx} (100%) rename tgui/packages/tgui/interfaces/{CargoHoldTerminal.js => CargoHoldTerminal.jsx} (100%) rename tgui/packages/tgui/interfaces/{CellularEmporium.js => CellularEmporium.jsx} (100%) rename tgui/packages/tgui/interfaces/{CentcomPodLauncher.js => CentcomPodLauncher.jsx} (100%) rename tgui/packages/tgui/interfaces/{ChemAcclimator.js => ChemAcclimator.jsx} (100%) rename tgui/packages/tgui/interfaces/{ChemDebugSynthesizer.js => ChemDebugSynthesizer.jsx} (100%) rename tgui/packages/tgui/interfaces/{ChemDispenser.js => ChemDispenser.jsx} (100%) rename tgui/packages/tgui/interfaces/{ChemFilter.js => ChemFilter.jsx} (100%) rename tgui/packages/tgui/interfaces/{ChemHeater.js => ChemHeater.jsx} (100%) rename tgui/packages/tgui/interfaces/{ChemMaster.js => ChemMaster.jsx} (100%) rename tgui/packages/tgui/interfaces/{ChemReactionChamber.js => ChemReactionChamber.jsx} (100%) rename tgui/packages/tgui/interfaces/{ChemSplitter.js => ChemSplitter.jsx} (100%) rename tgui/packages/tgui/interfaces/{ChemSynthesizer.js => ChemSynthesizer.jsx} (100%) rename tgui/packages/tgui/interfaces/{CircuitModule.js => CircuitModule.jsx} (100%) rename tgui/packages/tgui/interfaces/{Clipboard.js => Clipboard.jsx} (100%) rename tgui/packages/tgui/interfaces/{ClockworkSlab.js => ClockworkSlab.jsx} (100%) rename tgui/packages/tgui/interfaces/{CloningConsole.js => CloningConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{CodexGigas.js => CodexGigas.jsx} (100%) rename tgui/packages/tgui/interfaces/{ColorMatrixEditor.js => ColorMatrixEditor.jsx} (100%) rename tgui/packages/tgui/interfaces/{CommunicationsConsole.js => CommunicationsConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{ComputerFabricator.js => ComputerFabricator.jsx} (100%) rename tgui/packages/tgui/interfaces/{Crayon.js => Crayon.jsx} (100%) rename tgui/packages/tgui/interfaces/{Cryo.js => Cryo.jsx} (100%) rename tgui/packages/tgui/interfaces/{DisposalUnit.js => DisposalUnit.jsx} (100%) rename tgui/packages/tgui/interfaces/{DnaConsole.js => DnaConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{DnaVault.js => DnaVault.jsx} (100%) rename tgui/packages/tgui/interfaces/{EightBallVote.js => EightBallVote.jsx} (100%) rename tgui/packages/tgui/interfaces/{Electropack.js => Electropack.jsx} (100%) rename tgui/packages/tgui/interfaces/{Elevator.js => Elevator.jsx} (100%) rename tgui/packages/tgui/interfaces/{EmagConsole.js => EmagConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{EmergencyShuttleConsole.js => EmergencyShuttleConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{EmojiInputModal.js => EmojiInputModal.jsx} (100%) rename tgui/packages/tgui/interfaces/{EngravedMessage.js => EngravedMessage.jsx} (100%) rename tgui/packages/tgui/interfaces/{ExosuitControlConsole.js => ExosuitControlConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{Filteriffic.js => Filteriffic.jsx} (100%) rename tgui/packages/tgui/interfaces/{FishCatalog.js => FishCatalog.jsx} (100%) rename tgui/packages/tgui/interfaces/{Folder.js => Folder.jsx} (100%) rename tgui/packages/tgui/interfaces/{ForbiddenLore.js => ForbiddenLore.jsx} (100%) rename tgui/packages/tgui/interfaces/{FugitiveCaptureConsole.js => FugitiveCaptureConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{GenPop.js => GenPop.jsx} (100%) rename tgui/packages/tgui/interfaces/{GhostPoolProtection.js => GhostPoolProtection.jsx} (100%) rename tgui/packages/tgui/interfaces/{GlandDispenser.js => GlandDispenser.jsx} (100%) rename tgui/packages/tgui/interfaces/{Gps.js => Gps.jsx} (100%) rename tgui/packages/tgui/interfaces/{GravityGenerator.js => GravityGenerator.jsx} (100%) rename tgui/packages/tgui/interfaces/{GulagItemReclaimer.js => GulagItemReclaimer.jsx} (100%) rename tgui/packages/tgui/interfaces/{GulagTeleporterConsole.js => GulagTeleporterConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{Holodeck.js => Holodeck.jsx} (100%) rename tgui/packages/tgui/interfaces/{HypnoChair.js => HypnoChair.jsx} (100%) rename tgui/packages/tgui/interfaces/{ImplantChair.js => ImplantChair.jsx} (100%) rename tgui/packages/tgui/interfaces/{InfraredEmitter.js => InfraredEmitter.jsx} (100%) rename tgui/packages/tgui/interfaces/IntegratedCircuit/{BasicInput.js => BasicInput.jsx} (100%) rename tgui/packages/tgui/interfaces/IntegratedCircuit/{CircuitInfo.js => CircuitInfo.jsx} (100%) rename tgui/packages/tgui/interfaces/IntegratedCircuit/{Connections.js => Connections.jsx} (96%) rename tgui/packages/tgui/interfaces/IntegratedCircuit/{DisplayName.js => DisplayName.jsx} (100%) rename tgui/packages/tgui/interfaces/IntegratedCircuit/{FundamentalTypes.js => FundamentalTypes.jsx} (100%) rename tgui/packages/tgui/interfaces/IntegratedCircuit/{ObjectComponent.js => ObjectComponent.jsx} (97%) rename tgui/packages/tgui/interfaces/IntegratedCircuit/{Port.js => Port.jsx} (100%) rename tgui/packages/tgui/interfaces/IntegratedCircuit/{VariableMenu.js => VariableMenu.jsx} (100%) rename tgui/packages/tgui/interfaces/IntegratedCircuit/{constants.js => constants.ts} (100%) rename tgui/packages/tgui/interfaces/IntegratedCircuit/{index.js => index.jsx} (100%) rename tgui/packages/tgui/interfaces/{Intellicard.js => Intellicard.jsx} (100%) rename tgui/packages/tgui/interfaces/{Interview.js => Interview.jsx} (100%) rename tgui/packages/tgui/interfaces/{InterviewManager.js => InterviewManager.jsx} (100%) rename tgui/packages/tgui/interfaces/{Jukebox.js => Jukebox.jsx} (100%) rename tgui/packages/tgui/interfaces/{KeycardAuth.js => KeycardAuth.jsx} (100%) rename tgui/packages/tgui/interfaces/{LaborClaimConsole.js => LaborClaimConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{LanguageMenu.js => LanguageMenu.jsx} (100%) rename tgui/packages/tgui/interfaces/{LaunchpadConsole.js => LaunchpadConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{LaunchpadRemote.js => LaunchpadRemote.jsx} (100%) rename tgui/packages/tgui/interfaces/{MechBayPowerConsole.js => MechBayPowerConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{MessageMonitor.js => MessageMonitor.jsx} (100%) rename tgui/packages/tgui/interfaces/{MiningVendor.js => MiningVendor.jsx} (100%) rename tgui/packages/tgui/interfaces/{Mint.js => Mint.jsx} (100%) rename tgui/packages/tgui/interfaces/{ModularFabricator.js => ModularFabricator.jsx} (100%) rename tgui/packages/tgui/interfaces/{Morph.js => Morph.jsx} (100%) rename tgui/packages/tgui/interfaces/{Mule.js => Mule.jsx} (100%) rename tgui/packages/tgui/interfaces/{NaniteChamberControl.js => NaniteChamberControl.jsx} (100%) rename tgui/packages/tgui/interfaces/{NaniteCloudControl.js => NaniteCloudControl.jsx} (100%) rename tgui/packages/tgui/interfaces/{NaniteProgramHub.js => NaniteProgramHub.jsx} (100%) rename tgui/packages/tgui/interfaces/{NaniteProgrammer.js => NaniteProgrammer.jsx} (100%) rename tgui/packages/tgui/interfaces/{NaniteRemote.js => NaniteRemote.jsx} (100%) rename tgui/packages/tgui/interfaces/{Newscaster.js => Newscaster.jsx} (100%) rename tgui/packages/tgui/interfaces/{NoticeBoard.js => NoticeBoard.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtnetRelay.js => NtnetRelay.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosAiRestorer.js => NtosAiRestorer.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosAirlockControl.js => NtosAirlockControl.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosArcade.js => NtosArcade.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosAtmos.js => NtosAtmos.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosBountyBoard.js => NtosBountyBoard.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosBountyConsole.js => NtosBountyConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosCard.js => NtosCard.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosCargo.js => NtosCargo.jsx} (85%) rename tgui/packages/tgui/interfaces/{NtosConfiguration.js => NtosConfiguration.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosCrewManifest.js => NtosCrewManifest.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosCyborgRemoteMonitor.js => NtosCyborgRemoteMonitor.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosCyborgRemoteMonitorSyndicate.js => NtosCyborgRemoteMonitorSyndicate.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosCyborgSelfMonitor.js => NtosCyborgSelfMonitor.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosEmagConsole.js => NtosEmagConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosFileManager.js => NtosFileManager.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosGhostRbmkStats.js => NtosGhostRbmkStats.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosJobManager.js => NtosJobManager.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosLogViewer.js => NtosLogViewer.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosMain.js => NtosMain.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosMessenger.js => NtosMessenger.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosNetChat.js => NtosNetChat.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosNetDos.js => NtosNetDos.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosNetDownloader.js => NtosNetDownloader.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosNetMonitor.js => NtosNetMonitor.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosNewscaster.js => NtosNewscaster.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosNotepad.js => NtosNotepad.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosPhysScanner.js => NtosPhysScanner.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosPortraitPrinter.js => NtosPortraitPrinter.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosPowerMonitor.js => NtosPowerMonitor.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosRadar.js => NtosRadar.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosRadarSyndicate.js => NtosRadarSyndicate.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosRbmkStats.js => NtosRbmkStats.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosRecords.js => NtosRecords.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosRevelation.js => NtosRevelation.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosRoboControl.js => NtosRoboControl.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosSecurEye.js => NtosSecurEye.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosSignaller.js => NtosSignaller.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosStationAlertConsole.js => NtosStationAlertConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{NtosSupermatterMonitor.js => NtosSupermatterMonitor.jsx} (100%) rename tgui/packages/tgui/interfaces/{NuclearBomb.js => NuclearBomb.jsx} (100%) rename tgui/packages/tgui/interfaces/{Objective.js => Objective.jsx} (100%) rename tgui/packages/tgui/interfaces/{OperatingComputer.js => OperatingComputer.jsx} (100%) rename tgui/packages/tgui/interfaces/{Orbit.js => Orbit.jsx} (100%) rename tgui/packages/tgui/interfaces/{OrbitalMap.js => OrbitalMap.jsx} (100%) rename tgui/packages/tgui/interfaces/{OreBox.js => OreBox.jsx} (100%) rename tgui/packages/tgui/interfaces/{OreRedemptionMachine.js => OreRedemptionMachine.jsx} (100%) rename tgui/packages/tgui/interfaces/{OutfitEditor.js => OutfitEditor.jsx} (100%) rename tgui/packages/tgui/interfaces/{OutfitManager.js => OutfitManager.jsx} (100%) rename tgui/packages/tgui/interfaces/{PDAInputModal.js => PDAInputModal.jsx} (100%) rename tgui/packages/tgui/interfaces/{Pandemic.js => Pandemic.jsx} (100%) rename tgui/packages/tgui/interfaces/{ParticleAccelerator.js => ParticleAccelerator.jsx} (100%) rename tgui/packages/tgui/interfaces/{Particool.js => Particool.jsx} (100%) rename tgui/packages/tgui/interfaces/{PersonalCrafting.js => PersonalCrafting.jsx} (100%) rename tgui/packages/tgui/interfaces/{Photocopier.js => Photocopier.jsx} (100%) rename tgui/packages/tgui/interfaces/{PhysicalNewscaster.js => PhysicalNewscaster.jsx} (100%) rename tgui/packages/tgui/interfaces/{PictureSelectModal.js => PictureSelectModal.jsx} (100%) rename tgui/packages/tgui/interfaces/{PlantDNAManipulator.js => PlantDNAManipulator.jsx} (100%) rename tgui/packages/tgui/interfaces/{PlayerPanel.js => PlayerPanel.jsx} (100%) rename tgui/packages/tgui/interfaces/{PortableGenerator.js => PortableGenerator.jsx} (100%) rename tgui/packages/tgui/interfaces/{PortablePump.js => PortablePump.jsx} (100%) rename tgui/packages/tgui/interfaces/{PortableScrubber.js => PortableScrubber.jsx} (100%) rename tgui/packages/tgui/interfaces/{PortableThermomachine.js => PortableThermomachine.jsx} (100%) rename tgui/packages/tgui/interfaces/{PortraitPicker.js => PortraitPicker.jsx} (100%) rename tgui/packages/tgui/interfaces/{PowerMonitor.js => PowerMonitor.jsx} (100%) rename tgui/packages/tgui/interfaces/{ProbingConsole.js => ProbingConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{ProximitySensor.js => ProximitySensor.jsx} (100%) rename tgui/packages/tgui/interfaces/{PsychicPlane.js => PsychicPlane.jsx} (100%) rename tgui/packages/tgui/interfaces/{RDConsole.js => RDConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{Radio.js => Radio.jsx} (100%) rename tgui/packages/tgui/interfaces/{RadioactiveMicrolaser.js => RadioactiveMicrolaser.jsx} (100%) rename tgui/packages/tgui/interfaces/{RapidPipeDispenser.js => RapidPipeDispenser.jsx} (100%) rename tgui/packages/tgui/interfaces/{RbmkControlRods.js => RbmkControlRods.jsx} (100%) rename tgui/packages/tgui/interfaces/{ReligiousTool.js => ReligiousTool.jsx} (100%) rename tgui/packages/tgui/interfaces/{RemoteRobotControl.js => RemoteRobotControl.jsx} (100%) rename tgui/packages/tgui/interfaces/{RequestManager.js => RequestManager.jsx} (100%) rename tgui/packages/tgui/interfaces/{RoboticsControlConsole.js => RoboticsControlConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{SatelliteControl.js => SatelliteControl.jsx} (100%) rename tgui/packages/tgui/interfaces/{ScannerGate.js => ScannerGate.jsx} (100%) rename tgui/packages/tgui/interfaces/{SeedExtractor.js => SeedExtractor.jsx} (100%) rename tgui/packages/tgui/interfaces/{SelectEquipment.js => SelectEquipment.jsx} (100%) rename tgui/packages/tgui/interfaces/{ShuttleDesignator.js => ShuttleDesignator.jsx} (100%) rename tgui/packages/tgui/interfaces/{ShuttleManipulator.js => ShuttleManipulator.jsx} (100%) rename tgui/packages/tgui/interfaces/{Signaler.js => Signaler.jsx} (100%) rename tgui/packages/tgui/interfaces/{Sleeper.js => Sleeper.jsx} (100%) rename tgui/packages/tgui/interfaces/{SlimeBodySwapper.js => SlimeBodySwapper.jsx} (100%) rename tgui/packages/tgui/interfaces/{SmartVend.js => SmartVend.jsx} (100%) rename tgui/packages/tgui/interfaces/{Smelter.js => Smelter.jsx} (100%) rename tgui/packages/tgui/interfaces/{Smes.js => Smes.jsx} (100%) rename tgui/packages/tgui/interfaces/{SmokeMachine.js => SmokeMachine.jsx} (100%) rename tgui/packages/tgui/interfaces/{SolarControl.js => SolarControl.jsx} (100%) rename tgui/packages/tgui/interfaces/{SpawnersMenu.js => SpawnersMenu.jsx} (100%) rename tgui/packages/tgui/interfaces/{Stack.js => Stack.jsx} (100%) rename tgui/packages/tgui/interfaces/{StationAlertConsole.js => StationAlertConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{SupermatterMonitor.js => SupermatterMonitor.jsx} (100%) rename tgui/packages/tgui/interfaces/{SyndContractor.js => SyndContractor.jsx} (100%) rename tgui/packages/tgui/interfaces/{TachyonArray.js => TachyonArray.jsx} (100%) rename tgui/packages/tgui/interfaces/{Tank.js => Tank.jsx} (100%) rename tgui/packages/tgui/interfaces/{TankDispenser.js => TankDispenser.jsx} (100%) rename tgui/packages/tgui/interfaces/{TechFab.js => TechFab.jsx} (100%) rename tgui/packages/tgui/interfaces/{Techweb.js => Techweb.jsx} (100%) rename tgui/packages/tgui/interfaces/{Telecomms.js => Telecomms.jsx} (100%) rename tgui/packages/tgui/interfaces/{Teleporter.js => Teleporter.jsx} (100%) rename tgui/packages/tgui/interfaces/{ThermoMachine.js => ThermoMachine.jsx} (100%) rename tgui/packages/tgui/interfaces/{TicketBrowser.js => TicketBrowser.jsx} (100%) rename tgui/packages/tgui/interfaces/{TicketMessenger.js => TicketMessenger.jsx} (100%) rename tgui/packages/tgui/interfaces/{Timer.js => Timer.jsx} (100%) rename tgui/packages/tgui/interfaces/{TrackedPlaytime.js => TrackedPlaytime.jsx} (100%) rename tgui/packages/tgui/interfaces/{TraitorBackstoryMenu.js => TraitorBackstoryMenu.jsx} (100%) rename tgui/packages/tgui/interfaces/{TransferValve.js => TransferValve.jsx} (100%) rename tgui/packages/tgui/interfaces/{TurbineComputer.js => TurbineComputer.jsx} (100%) rename tgui/packages/tgui/interfaces/{TurretControl.js => TurretControl.jsx} (100%) rename tgui/packages/tgui/interfaces/{Uplink.js => Uplink.jsx} (100%) rename tgui/packages/tgui/interfaces/{Vendatray.js => Vendatray.jsx} (100%) rename tgui/packages/tgui/interfaces/{Vote.js => Vote.jsx} (91%) rename tgui/packages/tgui/interfaces/{Wires.js => Wires.jsx} (100%) rename tgui/packages/tgui/interfaces/{Workshop.js => Workshop.jsx} (100%) rename tgui/packages/tgui/interfaces/{XenoartifactConsole.js => XenoartifactConsole.jsx} (100%) rename tgui/packages/tgui/interfaces/{XenoartifactLabeler.js => XenoartifactLabeler.jsx} (100%) rename tgui/packages/tgui/interfaces/common/{AccessList.js => AccessList.jsx} (100%) rename tgui/packages/tgui/interfaces/common/{AtmosControls.js => AtmosControls.jsx} (100%) rename tgui/packages/tgui/interfaces/common/{BeakerContents.js => BeakerContents.jsx} (100%) rename tgui/packages/tgui/interfaces/common/{InterfaceLockNoticeBox.js => InterfaceLockNoticeBox.jsx} (100%) rename tgui/packages/tgui/interfaces/common/{PortableAtmos.js => PortableAtmos.jsx} (100%) rename tgui/packages/tgui/interfaces/manually-routed/{KitchenSink.js => KitchenSink.jsx} (100%) rename tgui/packages/tgui/layouts/{Layout.js => Layout.jsx} (100%) rename tgui/packages/tgui/layouts/{NtosWindow.js => NtosWindow.jsx} (100%) rename tgui/packages/tgui/layouts/{Pane.js => Pane.jsx} (100%) rename tgui/packages/tgui/layouts/{Window.js => Window.jsx} (100%) rename tgui/packages/tgui/layouts/{index.js => index.ts} (100%) delete mode 100644 tgui/packages/tgui/links.js create mode 100644 tgui/packages/tgui/links.test.ts create mode 100644 tgui/packages/tgui/links.ts rename tgui/packages/tgui/{logging.js => logging.ts} (56%) rename tgui/packages/tgui/{routes.js => routes.tsx} (79%) create mode 100644 tgui/packages/tgui/sanitize.test.ts rename tgui/packages/tgui/{sanitize.js => sanitize.ts} (68%) delete mode 100644 tgui/packages/tgui/store.js create mode 100644 tgui/packages/tgui/store.ts rename tgui/packages/tgui/stories/{Blink.stories.js => Blink.stories.jsx} (100%) rename tgui/packages/tgui/stories/{BlockQuote.stories.js => BlockQuote.stories.jsx} (100%) rename tgui/packages/tgui/stories/{Box.stories.js => Box.stories.jsx} (100%) rename tgui/packages/tgui/stories/{Button.stories.js => Button.stories.jsx} (100%) rename tgui/packages/tgui/stories/{ByondUi.stories.js => ByondUi.stories.jsx} (100%) rename tgui/packages/tgui/stories/{Collapsible.stories.js => Collapsible.stories.jsx} (100%) rename tgui/packages/tgui/stories/{Flex.stories.js => Flex.stories.jsx} (100%) rename tgui/packages/tgui/stories/{Input.stories.js => Input.stories.jsx} (100%) rename tgui/packages/tgui/stories/{Popper.stories.js => Popper.stories.jsx} (100%) delete mode 100644 tgui/packages/tgui/stories/ProgressBar.stories.js create mode 100644 tgui/packages/tgui/stories/ProgressBar.stories.jsx rename tgui/packages/tgui/stories/{Stack.stories.js => Stack.stories.jsx} (100%) rename tgui/packages/tgui/stories/{Storage.stories.js => Storage.stories.jsx} (100%) rename tgui/packages/tgui/stories/{Tabs.stories.js => Tabs.stories.jsx} (100%) rename tgui/packages/tgui/stories/{Themes.stories.js => Themes.stories.jsx} (100%) rename tgui/packages/tgui/stories/{Tooltip.stories.js => Tooltip.stories.jsx} (100%) rename tgui/packages/tgui/stories/{common.js => common.jsx} (100%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d5b07ed7735a9..d0e0eb63731ee 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -47,5 +47,5 @@ Dockerfile @crossedfall /tgui/packages/tgui/interfaces/ /tgui/packages/tgui/styles/interfaces/ -/tgui/packages/tgui-panel/styles/goon/chat-dark.scss -/tgui/packages/tgui-panel/styles/goon/chat-light.scss +/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss +/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm index f83c2edfcfce3..14046bc9510d7 100644 --- a/code/controllers/subsystem/tgui.dm +++ b/code/controllers/subsystem/tgui.dm @@ -29,6 +29,7 @@ SUBSYSTEM_DEF(tgui) var/polyfill = file2text('tgui/public/tgui-polyfill.min.js') polyfill = "" basehtml = replacetextEx(basehtml, "", polyfill) + basehtml = replacetextEx(basehtml, "", "Nanotrasen (c) 2525-[GLOB.year_integer+YEAR_OFFSET]") /datum/controller/subsystem/tgui/Shutdown() close_all_uis() diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm index f5624fe9aed9d..1fd93a1131a1d 100644 --- a/interface/stylesheet.dm +++ b/interface/stylesheet.dm @@ -2,7 +2,7 @@ /// !!!!!!!!!!HEY LISTEN!!!!!!!!!!!!!!!!!!!!!!!! /// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// If you modify this file you ALSO need to modify code/modules/goonchat/browserAssets/browserOutput.css +// If you modify this file you ALSO need to modify tgui/packages/tgui-panel/styles/tgchat/chat-light.scss and chat-dark.scss // BUT you have to use PX font sizes with are on a x8 scale of these font sizes // Sample font-size: DM: 8 CSS: 64px diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml index 44c4b1ebe5623..b3b3af7e7d263 100644 --- a/tgui/.eslintrc.yml +++ b/tgui/.eslintrc.yml @@ -651,7 +651,7 @@ rules: ## Enforce ES5 or ES6 class for React Components react/prefer-es6-class: error ## Enforce that props are read-only - react/prefer-read-only-props: error + # react/prefer-read-only-props: error ## Enforce stateless React Components to be written as a pure function react/prefer-stateless-function: error ## Prevent missing props validation in a React component definition diff --git a/tgui/.yarnrc.yml b/tgui/.yarnrc.yml index 6e0f3f922216d..51d463ba52cdd 100644 --- a/tgui/.yarnrc.yml +++ b/tgui/.yarnrc.yml @@ -6,6 +6,11 @@ logFilters: - code: YN0062 level: discard +packageExtensions: + 'babel-plugin-inferno@*': + dependencies: + '@babel/core': '*' + plugins: - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs spec: '@yarnpkg/plugin-interactive-tools' diff --git a/tgui/babel.config.js b/tgui/babel.config.js index e702c9a7119d6..5f86bada5064d 100644 --- a/tgui/babel.config.js +++ b/tgui/babel.config.js @@ -23,7 +23,7 @@ const createBabelConfig = (options) => { ...presets, ].filter(Boolean), plugins: [ - [require.resolve('@babel/plugin-proposal-class-properties'), { + [require.resolve('@babel/plugin-transform-class-properties'), { loose: true, }], require.resolve('@babel/plugin-transform-jscript'), diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md index a8a22274409e4..57e0efe8067ea 100644 --- a/tgui/docs/component-reference.md +++ b/tgui/docs/component-reference.md @@ -773,7 +773,11 @@ percentage and how filled the bar is. - `maxValue: number` - Highest possible value. - `ranges: { color: [from, to] }` - Applies a `color` to the progress bar based on whether the value lands in the range between `from` and `to`. -- `color: string` - Color of the progress bar. +- `color: string` - Color of the progress bar. Can take any of the following formats: + - `#ffffff` - Hex format + - `rgb(r,g,b) / rgba(r,g,b,a)` - RGB format + - `` - the name of a `color-` CSS class. See `CSS_COLORS` in `constants.js`. + - `` - the name of a base CSS color, if not overridden by the definitions above. - `children: any` - Content to render inside the progress bar. ### `Section` diff --git a/tgui/global.d.ts b/tgui/global.d.ts index af2db9ac7fd59..bb6e208f20f93 100644 --- a/tgui/global.d.ts +++ b/tgui/global.d.ts @@ -201,4 +201,6 @@ const Byond: ByondType; interface Window { Byond: ByondType; + __store__: Store; + __augmentStack__: (store: Store) => StackAugmentor; } diff --git a/tgui/jest.config.js b/tgui/jest.config.js index de06e1c229add..bc1eb21555cfd 100644 --- a/tgui/jest.config.js +++ b/tgui/jest.config.js @@ -1,12 +1,12 @@ module.exports = { roots: ['/packages'], - testMatch: ['/packages/**/__tests__/*.{js,ts,tsx}', '/packages/**/*.{spec,test}.{js,ts,tsx}'], + testMatch: ['/packages/**/__tests__/*.{js,jsx,ts,tsx}', '/packages/**/*.{spec,test}.{js,jsx,ts,tsx}'], testPathIgnorePatterns: ['/packages/tgui-bench'], testEnvironment: 'jsdom', testRunner: require.resolve('jest-circus/runner'), transform: { - '^.+\\.(js|cjs|ts|tsx)$': require.resolve('babel-jest'), + '^.+\\.(js|jsx|cjs|ts|tsx)$': require.resolve('babel-jest'), }, - moduleFileExtensions: ['js', 'cjs', 'ts', 'tsx', 'json'], + moduleFileExtensions: ['js', 'jsx', 'cjs', 'ts', 'tsx', 'json'], resetMocks: true, }; diff --git a/tgui/package.json b/tgui/package.json index 4893883c3847a..1a5db3ba868a1 100644 --- a/tgui/package.json +++ b/tgui/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "tgui-workspace", - "version": "4.3.0", + "version": "4.4.0", "packageManager": "yarn@3.4.1", "workspaces": [ "packages/*" @@ -11,58 +11,58 @@ "tgui:bench": "webpack --env TGUI_BENCH=1 && node packages/tgui-bench/index.js", "tgui:build": "BROWSERSLIST_IGNORE_OLD_DATA=true webpack", "tgui:dev": "node --experimental-modules packages/tgui-dev-server/index.js", - "tgui:lint": "eslint packages --ext .js,.cjs,.ts,.tsx", + "tgui:lint": "eslint packages --ext .js,.cjs,.jsx,.ts,.tsx", "tgui:prettier": "prettierx --check .", "tgui:prettier-format": "prettierx --write packages", - "tgui:sonar": "eslint packages --ext .js,.cjs,.ts,.tsx -c .eslintrc-sonar.yml", + "tgui:sonar": "eslint packages --ext .js,.cjs,.jsx,.ts,.tsx -c .eslintrc-sonar.yml", "tgui:test": "jest --watch", "tgui:test-simple": "CI=true jest --color", "tgui:test-ci": "CI=true jest --color --collect-coverage", "tgui:tsc": "tsc" }, "dependencies": { - "@babel/core": "^7.21.0", - "@babel/eslint-parser": "^7.19.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/plugin-transform-jscript": "^7.20.2", - "@babel/preset-env": "^7.20.2", - "@babel/preset-typescript": "^7.21.0", - "@types/jest": "^29.4.0", - "@types/jsdom": "^21.1.0", + "@babel/core": "^7.26.0", + "@babel/eslint-parser": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-jscript": "^7.25.9", + "@babel/preset-env": "^7.26.0", + "@babel/preset-typescript": "^7.26.0", + "@types/jest": "^29.5.14", + "@types/jsdom": "^21.1.7", "@types/node": "18.x", - "@types/webpack": "^5.28.0", - "@types/webpack-env": "^1.18.0", - "@typescript-eslint/parser": "^5.54.1", - "babel-jest": "^29.5.0", - "babel-loader": "^9.1.2", - "babel-plugin-inferno": "^6.6.0", + "@types/webpack": "^5.28.5", + "@types/webpack-env": "^1.18.5", + "@typescript-eslint/parser": "^5.62.0", + "babel-jest": "^29.7.0", + "babel-loader": "9.1.2", + "babel-plugin-inferno": "^6.7.2", "babel-plugin-transform-remove-console": "^6.9.4", "common": "workspace:*", - "css-loader": "^6.7.3", + "css-loader": "6.8.1", + "esbuild-loader": "^4.2.2", "eslint": "^8.35.0", "eslint-config-prettier": "^8.8.0", - "eslint-plugin-react": "^7.32.2", - "eslint-plugin-sonarjs": "^0.18.0", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-sonarjs": "^0.23.0", "eslint-plugin-unused-imports": "^2.0.0", "file-loader": "^6.2.0", - "inferno": "^8.2.1", - "jest": "^29.5.0", - "jest-circus": "^29.5.0", - "jest-environment-jsdom": "^29.5.0", - "jsdom": "^21.1.0", - "mini-css-extract-plugin": "^2.7.3", + "inferno": "^8.2.3", + "jest": "^29.7.0", + "jest-circus": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "jsdom": "^25.0.1", + "mini-css-extract-plugin": "2.7.6", "prettier": "npm:prettierx@0.19.0", - "sass": "^1.58.3", - "sass-loader": "^13.2.0", - "style-loader": "^3.3.1", - "terser-webpack-plugin": "^5.3.7", - "ts-plugin-inferno": "^6.0.0", + "sass": "1.69.5", + "sass-loader": "13.3.2", + "style-loader": "3.3.3", + "ts-plugin-inferno": "^6.1.0", "typescript": "^4.9.5", "url-loader": "^4.1.1", - "webpack": "^5.81.0", - "webpack-bundle-analyzer": "^4.8.0", - "webpack-cli": "^5.0.1" + "webpack": "^5.97.1", + "webpack-bundle-analyzer": "^4.10.2", + "webpack-cli": "^6.0.1" }, "resolutions": { "minimist": "^1.2.6", diff --git a/tgui/packages/common/color.ts b/tgui/packages/common/color.ts index d16022849b0eb..fd8a284f407a8 100644 --- a/tgui/packages/common/color.ts +++ b/tgui/packages/common/color.ts @@ -20,7 +20,15 @@ export class Color { } toString() { - return `rgba(${this.r | 0}, ${this.g | 0}, ${this.b | 0}, ${this.a | 0})`; + // Alpha component needs to permit fractional values, so cannot use | + let alpha = this.a; + if (typeof alpha === 'string') { + alpha = parseFloat(this.a as any); + } + if (isNaN(alpha)) { + alpha = 1; + } + return `rgba(${this.r | 0}, ${this.g | 0}, ${this.b | 0}, ${alpha})`; } /** diff --git a/tgui/packages/common/keycodes.js b/tgui/packages/common/keycodes.ts similarity index 100% rename from tgui/packages/common/keycodes.js rename to tgui/packages/common/keycodes.ts diff --git a/tgui/packages/common/keys.ts b/tgui/packages/common/keys.ts new file mode 100644 index 0000000000000..61b79992b486b --- /dev/null +++ b/tgui/packages/common/keys.ts @@ -0,0 +1,39 @@ +/** + * ### Key codes. + * event.keyCode is deprecated, use this reference instead. + * + * Handles modifier keys (Shift, Alt, Control) and arrow keys. + * + * For alphabetical keys, use the actual character (e.g. 'a') instead of the key code. + * + * Something isn't here that you want? Just add it: + * @url https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values + * @usage + * ```ts + * import { KEY } from 'tgui/common/keys'; + * + * if (event.key === KEY.Enter) { + * // do something + * } + * ``` + */ +export enum KEY { + Alt = 'Alt', + Backspace = 'Backspace', + Control = 'Control', + Delete = 'Delete', + Down = 'Down', + End = 'End', + Enter = 'Enter', + Escape = 'Esc', + Home = 'Home', + Insert = 'Insert', + Left = 'Left', + PageDown = 'PageDown', + PageUp = 'PageUp', + Right = 'Right', + Shift = 'Shift', + Space = ' ', + Tab = 'Tab', + Up = 'Up', +} diff --git a/tgui/packages/common/redux.test.ts b/tgui/packages/common/redux.test.ts new file mode 100644 index 0000000000000..238426f331bb1 --- /dev/null +++ b/tgui/packages/common/redux.test.ts @@ -0,0 +1,58 @@ +import { Action, Reducer, applyMiddleware, combineReducers, createAction, createStore } from './redux'; + +// Dummy Reducer +const counterReducer: Reducer> = (state = 0, action) => { + switch (action.type) { + case 'INCREMENT': + return state + 1; + case 'DECREMENT': + return state - 1; + default: + return state; + } +}; + +// Dummy Middleware +const loggingMiddleware = (storeApi) => (next) => (action) => { + console.log('Middleware:', action); + return next(action); +}; + +// Dummy Action Creators +const increment = createAction('INCREMENT'); +const decrement = createAction('DECREMENT'); + +describe('Redux implementation tests', () => { + test('createStore works', () => { + const store = createStore(counterReducer); + expect(store.getState()).toBe(0); + }); + + test('createStore with applyMiddleware works', () => { + const store = createStore(counterReducer, applyMiddleware(loggingMiddleware)); + expect(store.getState()).toBe(0); + }); + + test('dispatch works', () => { + const store = createStore(counterReducer); + store.dispatch(increment()); + expect(store.getState()).toBe(1); + store.dispatch(decrement()); + expect(store.getState()).toBe(0); + }); + + test('combineReducers works', () => { + const rootReducer = combineReducers({ + counter: counterReducer, + }); + const store = createStore(rootReducer); + expect(store.getState()).toEqual({ counter: 0 }); + }); + + test('createAction works', () => { + const incrementAction = increment(); + expect(incrementAction).toEqual({ type: 'INCREMENT' }); + const decrementAction = decrement(); + expect(decrementAction).toEqual({ type: 'DECREMENT' }); + }); +}); diff --git a/tgui/packages/common/redux.js b/tgui/packages/common/redux.ts similarity index 50% rename from tgui/packages/common/redux.js rename to tgui/packages/common/redux.ts index 8718dccc0cd73..7b2ecc9eee60c 100644 --- a/tgui/packages/common/redux.js +++ b/tgui/packages/common/redux.ts @@ -4,27 +4,65 @@ * @license MIT */ -import { compose } from './fp'; +export type Reducer = ( + state: State | undefined, + action: ActionType +) => State; + +export type Store = { + dispatch: Dispatch; + subscribe: (listener: () => void) => void; + getState: () => State; +}; + +type MiddlewareAPI = { + getState: () => State; + dispatch: Dispatch; +}; + +export type Middleware = ( + storeApi: MiddlewareAPI +) => (next: Dispatch) => Dispatch; + +export type Action = { + type: TType; +}; + +export type AnyAction = Action & { + [extraProps: string]: any; +}; + +export type Dispatch = (action: ActionType) => void; + +type StoreEnhancer = (createStoreFunction: Function) => Function; + +type PreparedAction = { + payload?: any; + meta?: any; +}; /** * Creates a Redux store. */ -export const createStore = (reducer, enhancer) => { +export const createStore = ( + reducer: Reducer, + enhancer?: StoreEnhancer +): Store => { // Apply a store enhancer (applyMiddleware is one of them). if (enhancer) { return enhancer(createStore)(reducer); } - let currentState; - let listeners = []; + let currentState: State; + let listeners: Array<() => void> = []; - const getState = () => currentState; + const getState = (): State => currentState; - const subscribe = (listener) => { + const subscribe = (listener: () => void): void => { listeners.push(listener); }; - const dispatch = (action) => { + const dispatch = (action: ActionType): void => { currentState = reducer(currentState, action); for (let i = 0; i < listeners.length; i++) { listeners[i](); @@ -33,9 +71,7 @@ export const createStore = (reducer, enhancer) => { // This creates the initial store by causing each reducer to be called // with an undefined state - dispatch({ - type: '@@INIT', - }); + dispatch({ type: '@@INIT' } as ActionType); return { dispatch, @@ -48,28 +84,29 @@ export const createStore = (reducer, enhancer) => { * Creates a store enhancer which applies middleware to all dispatched * actions. */ -export const applyMiddleware = (...middlewares) => { - return (createStore) => - (reducer, ...args) => { - const store = createStore(reducer, ...args); +export const applyMiddleware = (...middlewares: Middleware[]): StoreEnhancer => { + return (createStoreFunction: (reducer: Reducer, enhancer?: StoreEnhancer) => Store) => { + return (reducer, ...args): Store => { + const store = createStoreFunction(reducer, ...args); - let dispatch = () => { + let dispatch: Dispatch = () => { throw new Error('Dispatching while constructing your middleware is not allowed.'); }; - const storeApi = { + const storeApi: MiddlewareAPI = { getState: store.getState, dispatch: (action, ...args) => dispatch(action, ...args), }; const chain = middlewares.map((middleware) => middleware(storeApi)); - dispatch = compose(...chain)(store.dispatch); + dispatch = chain.reduceRight((next, middleware) => middleware(next), store.dispatch); return { ...store, dispatch, }; }; + }; }; /** @@ -80,20 +117,24 @@ export const applyMiddleware = (...middlewares) => { * in the state that are not present in the reducers object. This function * is also more flexible than the redux counterpart. */ -export const combineReducers = (reducersObj) => { +export const combineReducers = (reducersObj: Record): Reducer => { const keys = Object.keys(reducersObj); - let hasChanged = false; + return (prevState = {}, action) => { const nextState = { ...prevState }; - for (let key of keys) { + let hasChanged = false; + + for (const key of keys) { const reducer = reducersObj[key]; const prevDomainState = prevState[key]; const nextDomainState = reducer(prevDomainState, action); + if (prevDomainState !== nextDomainState) { hasChanged = true; nextState[key] = nextDomainState; } } + return hasChanged ? nextState : prevState; }; }; @@ -114,37 +155,42 @@ export const combineReducers = (reducersObj) => { * * @public */ -export const createAction = (type, prepare = null) => { - const actionCreator = (...args) => { - if (!prepare) { - return { type, payload: args[0] }; - } - const prepared = prepare(...args); - if (!prepared) { - throw new Error('prepare function did not return an object'); - } - const action = { type }; - if ('payload' in prepared) { - action.payload = prepared.payload; - } - if ('meta' in prepared) { - action.meta = prepared.meta; +export const createAction = (type: TAction, prepare?: (...args: any[]) => PreparedAction) => { + const actionCreator = (...args: any[]) => { + let action: Action & PreparedAction = { type }; + + if (prepare) { + const prepared = prepare(...args); + if (!prepared) { + throw new Error('prepare function did not return an object'); + } + action = { ...action, ...prepared }; + } else { + action.payload = args[0]; } + return action; }; - actionCreator.toString = () => '' + type; + + actionCreator.toString = () => type; actionCreator.type = type; actionCreator.match = (action) => action.type === type; + return actionCreator; }; // Implementation specific // -------------------------------------------------------- -export const useDispatch = (context) => { +export const useDispatch = (context: { + store: Store; +}): Dispatch => { return context.store.dispatch; }; -export const useSelector = (context, selector) => { +export const useSelector = ( + context: { store: Store }, + selector: (state: State) => Selected +): Selected => { return selector(context.store.getState()); }; diff --git a/tgui/packages/common/string.js b/tgui/packages/common/string.js index 3edd6dae826fb..4567aa3568fe5 100644 --- a/tgui/packages/common/string.js +++ b/tgui/packages/common/string.js @@ -81,6 +81,13 @@ export const createSearch = (searchText, stringifier) => { }; }; +/** + * Capitalizes a word and lowercases the rest. + * @param {string} str + * @returns {string} capitalized string + * + * @example capitalize('heLLo') === 'Hello' + */ export const capitalize = (str) => { // Handle array if (Array.isArray(str)) { @@ -90,6 +97,31 @@ export const capitalize = (str) => { return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); }; +/** + * Similar to capitalize, this takes a string and replaces all first letters + * of any words. + * + * @param {string} str + * @return {string} The string with the first letters capitalized. + * + * @example capitalizeAll('heLLo woRLd') === 'HeLLo WoRLd' + */ +export const capitalizeAll = (str) => { + return str.replace(/(^\w{1})|(\s+\w{1})/g, (letter) => letter.toUpperCase()); +}; + +/** + * Capitalizes only the first letter of the str. + * + * @param {string} str + * @return {string} capitalized string + * + * @example capitalizeFirst('heLLo woRLd') === 'HeLLo woRLd' + */ +export const capitalizeFirst = (str) => { + return str.replace(/^\w/, (letter) => letter.toUpperCase()); +}; + export const toTitleCase = (str) => { // Handle array if (Array.isArray(str)) { diff --git a/tgui/packages/common/timer.js b/tgui/packages/common/timer.ts similarity index 54% rename from tgui/packages/common/timer.js rename to tgui/packages/common/timer.ts index 9de7ee1d523bc..99e0c1b1029c0 100644 --- a/tgui/packages/common/timer.js +++ b/tgui/packages/common/timer.ts @@ -10,9 +10,13 @@ * called for N milliseconds. If `immediate` is passed, trigger the * function on the leading edge, instead of the trailing. */ -export const debounce = (fn, time, immediate = false) => { - let timeout; - return (...args) => { +export const debounce = any>( + fn: F, + time: number, + immediate = false +): ((...args: Parameters) => void) => { + let timeout: ReturnType | null; + return (...args: Parameters) => { const later = () => { timeout = null; if (!immediate) { @@ -20,7 +24,7 @@ export const debounce = (fn, time, immediate = false) => { } }; const callNow = immediate && !timeout; - clearTimeout(timeout); + clearTimeout(timeout!); timeout = setTimeout(later, time); if (callNow) { fn(...args); @@ -32,16 +36,18 @@ export const debounce = (fn, time, immediate = false) => { * Returns a function, that, when invoked, will only be triggered at most once * during a given window of time. */ -export const throttle = (fn, time) => { - let previouslyRun, queuedToRun; - return function invokeFn(...args) { +export const throttle = any>(fn: F, time: number): ((...args: Parameters) => void) => { + let previouslyRun: number | null, queuedToRun: ReturnType | null; + return function invokeFn(...args: Parameters) { const now = Date.now(); - queuedToRun = clearTimeout(queuedToRun); + if (queuedToRun) { + clearTimeout(queuedToRun); + } if (!previouslyRun || now - previouslyRun >= time) { fn.apply(null, args); previouslyRun = now; } else { - queuedToRun = setTimeout(invokeFn.bind(null, ...args), time - (now - previouslyRun)); + queuedToRun = setTimeout(() => invokeFn(...args), time - (now - (previouslyRun ?? 0))); } }; }; @@ -51,4 +57,4 @@ export const throttle = (fn, time) => { * * @param {number} time */ -export const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time)); +export const sleep = (time: number): Promise => new Promise((resolve) => setTimeout(resolve, time)); diff --git a/tgui/packages/tgui-bench/index.js b/tgui/packages/tgui-bench/index.js index 9db479007b5b2..b10e0a3e0b68d 100644 --- a/tgui/packages/tgui-bench/index.js +++ b/tgui/packages/tgui-bench/index.js @@ -29,7 +29,7 @@ const setup = async () => { const publicDir = path.resolve(__dirname, '../../public'); const page = fs.readFileSync(path.join(publicDir, 'tgui.html'), 'utf-8').replace('\n', assets); - server.register(require('fastify-static'), { + server.register(require('@fastify/static'), { root: publicDir, }); diff --git a/tgui/packages/tgui-bench/package.json b/tgui/packages/tgui-bench/package.json index ca808012b0492..6ccddde1f596c 100644 --- a/tgui/packages/tgui-bench/package.json +++ b/tgui/packages/tgui-bench/package.json @@ -1,13 +1,13 @@ { "private": true, "name": "tgui-bench", - "version": "4.3.0", + "version": "4.4.0", "dependencies": { + "@fastify/static": "^6.12.0", "common": "workspace:*", - "fastify": "^4.14.1", - "fastify-static": "^4.7.0", - "inferno": "^8.2.1", - "inferno-vnode-flags": "^8.2.1", + "fastify": "^3.29.5", + "inferno": "^8.2.3", + "inferno-vnode-flags": "^8.2.3", "lodash": "^4.17.21", "platform": "^1.3.6", "tgui": "workspace:*" diff --git a/tgui/packages/tgui-bench/tests/DisposalUnit.test.tsx b/tgui/packages/tgui-bench/tests/DisposalUnit.test.tsx index 843bd70367a5d..1ae610e2e2e15 100644 --- a/tgui/packages/tgui-bench/tests/DisposalUnit.test.tsx +++ b/tgui/packages/tgui-bench/tests/DisposalUnit.test.tsx @@ -1,9 +1,10 @@ -import { backendUpdate } from 'tgui/backend'; +import { StoreProvider, configureStore } from 'tgui/store'; + import { DisposalUnit } from 'tgui/interfaces/DisposalUnit'; +import { backendUpdate } from 'tgui/backend'; import { createRenderer } from 'tgui/renderer'; -import { configureStore, StoreProvider } from 'tgui/store'; -const store = configureStore({ sideEffets: false }); +const store = configureStore({ sideEffects: false }); const renderUi = createRenderer((dataJson: string) => { store.dispatch( diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json index a45a38c163d09..3121011fc7d1a 100644 --- a/tgui/packages/tgui-dev-server/package.json +++ b/tgui/packages/tgui-dev-server/package.json @@ -1,13 +1,13 @@ { "private": true, "name": "tgui-dev-server", - "version": "4.3.0", + "version": "4.4.0", "type": "module", "dependencies": { - "axios": "^1.6.0", + "axios": "^1.7.9", "glob": "^7.1.7", "source-map": "^0.7.4", "stacktrace-parser": "^0.1.10", - "ws": "^8.12.1" + "ws": "^8.18.0" } } diff --git a/tgui/packages/tgui-panel/Notifications.js b/tgui/packages/tgui-panel/Notifications.jsx similarity index 100% rename from tgui/packages/tgui-panel/Notifications.js rename to tgui/packages/tgui-panel/Notifications.jsx diff --git a/tgui/packages/tgui-panel/Panel.js b/tgui/packages/tgui-panel/Panel.jsx similarity index 98% rename from tgui/packages/tgui-panel/Panel.js rename to tgui/packages/tgui-panel/Panel.jsx index 42c651bc99723..f56d025dac272 100644 --- a/tgui/packages/tgui-panel/Panel.js +++ b/tgui/packages/tgui-panel/Panel.jsx @@ -15,8 +15,8 @@ import { Notifications } from './Notifications'; import { PingIndicator } from './ping'; import { ReconnectButtons } from './reconnect'; import { SettingsPanel, useSettings } from './settings'; -import { useLocalState } from '../tgui/backend'; -import { Box, Divider, DraggableControl } from '../tgui/components'; +import { useLocalState } from 'tgui/backend'; +import { Box, Divider, DraggableControl } from 'tgui/components'; import { updateSettings } from './settings/actions'; export const Panel = (props, context) => { diff --git a/tgui/packages/tgui-panel/audio/NowPlayingWidget.js b/tgui/packages/tgui-panel/audio/NowPlayingWidget.jsx similarity index 100% rename from tgui/packages/tgui-panel/audio/NowPlayingWidget.js rename to tgui/packages/tgui-panel/audio/NowPlayingWidget.jsx diff --git a/tgui/packages/tgui-panel/audio/index.js b/tgui/packages/tgui-panel/audio/index.ts similarity index 100% rename from tgui/packages/tgui-panel/audio/index.js rename to tgui/packages/tgui-panel/audio/index.ts diff --git a/tgui/packages/tgui-panel/chat/ChatPageSettings.js b/tgui/packages/tgui-panel/chat/ChatPageSettings.jsx similarity index 100% rename from tgui/packages/tgui-panel/chat/ChatPageSettings.js rename to tgui/packages/tgui-panel/chat/ChatPageSettings.jsx diff --git a/tgui/packages/tgui-panel/chat/ChatPanel.js b/tgui/packages/tgui-panel/chat/ChatPanel.jsx similarity index 100% rename from tgui/packages/tgui-panel/chat/ChatPanel.js rename to tgui/packages/tgui-panel/chat/ChatPanel.jsx diff --git a/tgui/packages/tgui-panel/chat/ChatTabs.js b/tgui/packages/tgui-panel/chat/ChatTabs.jsx similarity index 100% rename from tgui/packages/tgui-panel/chat/ChatTabs.js rename to tgui/packages/tgui-panel/chat/ChatTabs.jsx diff --git a/tgui/packages/tgui-panel/chat/constants.js b/tgui/packages/tgui-panel/chat/constants.ts similarity index 100% rename from tgui/packages/tgui-panel/chat/constants.js rename to tgui/packages/tgui-panel/chat/constants.ts diff --git a/tgui/packages/tgui-panel/chat/index.js b/tgui/packages/tgui-panel/chat/index.ts similarity index 100% rename from tgui/packages/tgui-panel/chat/index.js rename to tgui/packages/tgui-panel/chat/index.ts diff --git a/tgui/packages/tgui-panel/chat/renderer.js b/tgui/packages/tgui-panel/chat/renderer.jsx similarity index 99% rename from tgui/packages/tgui-panel/chat/renderer.js rename to tgui/packages/tgui-panel/chat/renderer.jsx index f08e8ac85e981..ac44b956f34bd 100644 --- a/tgui/packages/tgui-panel/chat/renderer.js +++ b/tgui/packages/tgui-panel/chat/renderer.jsx @@ -11,7 +11,7 @@ import { COMBINE_MAX_MESSAGES, COMBINE_MAX_TIME_WINDOW, IMAGE_RETRY_DELAY, IMAGE import { render } from 'inferno'; import { canPageAcceptType, createMessage, isSameMessage } from './model'; import { highlightNode, linkifyNode } from './replaceInTextNode'; -import { Tooltip, RadarChart } from '../../tgui/components'; +import { Tooltip, RadarChart } from 'tgui/components'; const logger = createLogger('chatRenderer'); diff --git a/tgui/packages/tgui-panel/game/constants.js b/tgui/packages/tgui-panel/game/constants.ts similarity index 100% rename from tgui/packages/tgui-panel/game/constants.js rename to tgui/packages/tgui-panel/game/constants.ts diff --git a/tgui/packages/tgui-panel/game/index.js b/tgui/packages/tgui-panel/game/index.ts similarity index 100% rename from tgui/packages/tgui-panel/game/index.js rename to tgui/packages/tgui-panel/game/index.ts diff --git a/tgui/packages/tgui-panel/index.js b/tgui/packages/tgui-panel/index.jsx similarity index 100% rename from tgui/packages/tgui-panel/index.js rename to tgui/packages/tgui-panel/index.jsx diff --git a/tgui/packages/tgui-panel/package.json b/tgui/packages/tgui-panel/package.json index 9182fc4144756..6635c2c249ae3 100644 --- a/tgui/packages/tgui-panel/package.json +++ b/tgui/packages/tgui-panel/package.json @@ -5,7 +5,7 @@ "dependencies": { "common": "workspace:*", "dompurify": "^3.0.1", - "inferno": "^8.2.1", + "inferno": "^8.2.3", "tgui": "workspace:*", "tgui-dev-server": "workspace:*", "tgui-polyfill": "workspace:*" diff --git a/tgui/packages/tgui-panel/ping/PingIndicator.js b/tgui/packages/tgui-panel/ping/PingIndicator.jsx similarity index 100% rename from tgui/packages/tgui-panel/ping/PingIndicator.js rename to tgui/packages/tgui-panel/ping/PingIndicator.jsx diff --git a/tgui/packages/tgui-panel/ping/constants.js b/tgui/packages/tgui-panel/ping/constants.ts similarity index 100% rename from tgui/packages/tgui-panel/ping/constants.js rename to tgui/packages/tgui-panel/ping/constants.ts diff --git a/tgui/packages/tgui-panel/ping/index.js b/tgui/packages/tgui-panel/ping/index.ts similarity index 100% rename from tgui/packages/tgui-panel/ping/index.js rename to tgui/packages/tgui-panel/ping/index.ts diff --git a/tgui/packages/tgui-panel/settings/SettingsPanel.js b/tgui/packages/tgui-panel/settings/SettingsPanel.jsx similarity index 100% rename from tgui/packages/tgui-panel/settings/SettingsPanel.js rename to tgui/packages/tgui-panel/settings/SettingsPanel.jsx diff --git a/tgui/packages/tgui-panel/settings/constants.js b/tgui/packages/tgui-panel/settings/constants.ts similarity index 100% rename from tgui/packages/tgui-panel/settings/constants.js rename to tgui/packages/tgui-panel/settings/constants.ts diff --git a/tgui/packages/tgui-panel/settings/index.js b/tgui/packages/tgui-panel/settings/index.ts similarity index 100% rename from tgui/packages/tgui-panel/settings/index.js rename to tgui/packages/tgui-panel/settings/index.ts diff --git a/tgui/packages/tgui-panel/styles/main.scss b/tgui/packages/tgui-panel/styles/main.scss index 4369351b7ebae..72faac7c45a4d 100644 --- a/tgui/packages/tgui-panel/styles/main.scss +++ b/tgui/packages/tgui-panel/styles/main.scss @@ -56,8 +56,8 @@ // @include meta.load-css('~tgui/styles/layouts/TitleBar.scss'); @include meta.load-css('~tgui/styles/layouts/Window.scss'); -// Goonchat styles -@include meta.load-css('./goon/chat-dark.scss'); +// tgchat styles +@include meta.load-css('./tgchat/chat-dark.scss'); // Chat formats (color, size, font, etc) @include meta.load-css('./chat-format/chat-dark-theme.scss'); diff --git a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss similarity index 100% rename from tgui/packages/tgui-panel/styles/goon/chat-dark.scss rename to tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss diff --git a/tgui/packages/tgui-panel/styles/goon/chat-light.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss similarity index 100% rename from tgui/packages/tgui-panel/styles/goon/chat-light.scss rename to tgui/packages/tgui-panel/styles/tgchat/chat-light.scss diff --git a/tgui/packages/tgui-panel/styles/themes/light.scss b/tgui/packages/tgui-panel/styles/themes/light.scss index 66e48ed9e6a98..8bd900b01c264 100644 --- a/tgui/packages/tgui-panel/styles/themes/light.scss +++ b/tgui/packages/tgui-panel/styles/themes/light.scss @@ -69,8 +69,8 @@ $with: ('text-color': rgba(0, 0, 0, 0.75), 'background-color': base.$color-bg, 'shadow-color-core': rgba(0, 0, 0, 0.25)) ); - // Goonchat styles - @include meta.load-css('../goon/chat-light.scss'); + // tgchat styles + @include meta.load-css('../tgchat/chat-light.scss'); // Chat formats (color, size, font, etc) @include meta.load-css('../chat-format/chat-light-theme.scss'); diff --git a/tgui/packages/tgui-polyfill/00-html5shiv.js b/tgui/packages/tgui-polyfill/00-html5shiv.js deleted file mode 100644 index c2f2060a1db9f..0000000000000 --- a/tgui/packages/tgui-polyfill/00-html5shiv.js +++ /dev/null @@ -1,330 +0,0 @@ -/** - * @file - * @copyright 2014 Alexander Farkas - * @license MIT - */ - -/* eslint-disable */ -(function(window, document) { -/*jshint evil:true */ - /** version */ - var version = '3.7.3'; - - /** Preset options */ - var options = window.html5 || {}; - - /** Used to skip problem elements */ - var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; - - /** Not all elements can be cloned in IE **/ - var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i; - - /** Detect whether the browser supports default html5 styles */ - var supportsHtml5Styles; - - /** Name of the expando, to work with multiple documents or to re-shiv one document */ - var expando = '_html5shiv'; - - /** The id for the the documents expando */ - var expanID = 0; - - /** Cached data for each document */ - var expandoData = {}; - - /** Detect whether the browser supports unknown elements */ - var supportsUnknownElements; - - (function() { - try { - var a = document.createElement('a'); - a.innerHTML = ''; - //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles - supportsHtml5Styles = ('hidden' in a); - - supportsUnknownElements = a.childNodes.length == 1 || (function() { - // assign a false positive if unable to shiv - (document.createElement)('a'); - var frag = document.createDocumentFragment(); - return ( - typeof frag.cloneNode == 'undefined' || - typeof frag.createDocumentFragment == 'undefined' || - typeof frag.createElement == 'undefined' - ); - }()); - } catch(e) { - // assign a false positive if detection fails => unable to shiv - supportsHtml5Styles = true; - supportsUnknownElements = true; - } - - }()); - - /*--------------------------------------------------------------------------*/ - - /** - * Creates a style sheet with the given CSS text and adds it to the document. - * @private - * @param {Document} ownerDocument The document. - * @param {String} cssText The CSS text. - * @returns {StyleSheet} The style element. - */ - function addStyleSheet(ownerDocument, cssText) { - var p = ownerDocument.createElement('p'), - parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; - - p.innerHTML = 'x'; - return parent.insertBefore(p.lastChild, parent.firstChild); - } - - /** - * Returns the value of `html5.elements` as an array. - * @private - * @returns {Array} An array of shived element node names. - */ - function getElements() { - var elements = html5.elements; - return typeof elements == 'string' ? elements.split(' ') : elements; - } - - /** - * Extends the built-in list of html5 elements - * @memberOf html5 - * @param {String|Array} newElements whitespace separated list or array of new element names to shiv - * @param {Document} ownerDocument The context document. - */ - function addElements(newElements, ownerDocument) { - var elements = html5.elements; - if(typeof elements != 'string'){ - elements = elements.join(' '); - } - if(typeof newElements != 'string'){ - newElements = newElements.join(' '); - } - html5.elements = elements +' '+ newElements; - shivDocument(ownerDocument); - } - - /** - * Returns the data associated to the given document - * @private - * @param {Document} ownerDocument The document. - * @returns {Object} An object of data. - */ - function getExpandoData(ownerDocument) { - var data = expandoData[ownerDocument[expando]]; - if (!data) { - data = {}; - expanID++; - ownerDocument[expando] = expanID; - expandoData[expanID] = data; - } - return data; - } - - /** - * returns a shived element for the given nodeName and document - * @memberOf html5 - * @param {String} nodeName name of the element - * @param {Document|DocumentFragment} ownerDocument The context document. - * @returns {Object} The shived element. - */ - function createElement(nodeName, ownerDocument, data){ - if (!ownerDocument) { - ownerDocument = document; - } - if(supportsUnknownElements){ - return ownerDocument.createElement(nodeName); - } - if (!data) { - data = getExpandoData(ownerDocument); - } - var node; - - if (data.cache[nodeName]) { - node = data.cache[nodeName].cloneNode(); - } else if (saveClones.test(nodeName)) { - node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); - } else { - node = data.createElem(nodeName); - } - - // Avoid adding some elements to fragments in IE < 9 because - // * Attributes like `name` or `type` cannot be set/changed once an element - // is inserted into a document/fragment - // * Link elements with `src` attributes that are inaccessible, as with - // a 403 response, will cause the tab/window to crash - // * Script elements appended to fragments will execute when their `src` - // or `text` property is set - return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; - } - - /** - * returns a shived DocumentFragment for the given document - * @memberOf html5 - * @param {Document} ownerDocument The context document. - * @returns {Object} The shived DocumentFragment. - */ - function createDocumentFragment(ownerDocument, data){ - if (!ownerDocument) { - ownerDocument = document; - } - if(supportsUnknownElements){ - return ownerDocument.createDocumentFragment(); - } - data = data || getExpandoData(ownerDocument); - var clone = data.frag.cloneNode(), - i = 0, - elems = getElements(), - l = elems.length; - for(;i 3 ? getModifier(init) : null, - key = String(init.key), - chr = String(init.char), - location = init.location, - keyCode = init.keyCode || ( - (init.keyCode = key) && - key.charCodeAt(0) - ) || 0, - charCode = init.charCode || ( - (init.charCode = chr) && - chr.charCodeAt(0) - ) || 0, - bubbles = init.bubbles, - cancelable = init.cancelable, - repeat = init.repeat, - locale = init.locale, - view = init.view || window, - args - ; - if (!init.which) init.which = init.keyCode; - if ('initKeyEvent' in out) { - out.initKeyEvent( - type, bubbles, cancelable, view, - ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode - ); - } else if (0 < initType && 'initKeyboardEvent' in out) { - args = [type, bubbles, cancelable, view]; - switch (initType) { - case 1: - args.push(key, location, ctrlKey, shiftKey, altKey, metaKey, altGraphKey); - break; - case 2: - args.push(ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode); - break; - case 3: - args.push(key, location, ctrlKey, altKey, shiftKey, metaKey, altGraphKey); - break; - case 4: - args.push(key, location, modifiers, repeat, locale); - break; - default: - args.push(char, key, location, modifiers, repeat, locale); - } - out.initKeyboardEvent.apply(out, args); - } else { - out.initEvent(type, bubbles, cancelable); - } - for (key in out) { - if (defaults.hasOwnProperty(key) && out[key] !== init[key]) { - withInitValues(key, out, init); - } - } - return out; - } - KeyboardEvent.prototype = $KeyboardEvent.prototype; - return KeyboardEvent; - }(window.KeyboardEvent || function KeyboardEvent() {})); - defineProperty(window, 'KeyboardEvent', {value: o_O}); - // Android 4 gotcha - if (KeyboardEvent !== o_O) KeyboardEvent = o_O; - } - - // window.MouseEvent as constructor - try { new MouseEvent('_', {}); } catch (o_O) { - /* jshint -W022 */ - o_O = (function ($MouseEvent) { - function MouseEvent(type, init) { - enoughArguments(arguments.length, 'MouseEvent'); - var out = document.createEvent('MouseEvent'); - if (!init) init = {}; - out.initMouseEvent( - type, - !!init.bubbles, - !!init.cancelable, - init.view || window, - init.detail || 1, - init.screenX || 0, - init.screenY || 0, - init.clientX || 0, - init.clientY || 0, - !!init.ctrlKey, - !!init.altKey, - !!init.shiftKey, - !!init.metaKey, - init.button || 0, - init.relatedTarget || null - ); - return out; - } - MouseEvent.prototype = $MouseEvent.prototype; - return MouseEvent; - }(window.MouseEvent || function MouseEvent() {})); - defineProperty(window, 'MouseEvent', {value: o_O}); - // Android 4 gotcha - if (MouseEvent !== o_O) MouseEvent = o_O; - } - - if (!document.querySelectorAll('*').forEach) { - (function () { - function patch(what) { - var querySelectorAll = what.querySelectorAll; - what.querySelectorAll = function qSA(css) { - var result = querySelectorAll.call(this, css); - result.forEach = Array.prototype.forEach; - return result; - }; - } - patch(document); - patch(Element.prototype); - }()); - } - - try { - // https://drafts.csswg.org/selectors-4/#the-scope-pseudo - document.querySelector(':scope *'); - } catch(o_O) { - (function () { - var dataScope = 'data-scope-' + (Math.random() * 1e9 >>> 0); - var proto = Element.prototype; - var querySelector = proto.querySelector; - var querySelectorAll = proto.querySelectorAll; - proto.querySelector = function qS(css) { - return find(this, querySelector, css); - }; - proto.querySelectorAll = function qSA(css) { - return find(this, querySelectorAll, css); - }; - function find(node, method, css) { - node.setAttribute(dataScope, null); - var result = method.call( - node, - String(css).replace( - /(^|,\s*)(:scope([ >]|$))/g, - function ($0, $1, $2, $3) { - return $1 + '[' + dataScope + ']' + ($3 || ' '); - } - ) - ); - node.removeAttribute(dataScope); - return result; - } - }()); - } -}(window)); -(function (global){'use strict'; - - // a WeakMap fallback for DOM nodes only used as key - var DOMMap = global.WeakMap || (function () { - - var - counter = 0, - dispatched = false, - drop = false, - value - ; - - function dispatch(key, ce, shouldDrop) { - drop = shouldDrop; - dispatched = false; - value = undefined; - key.dispatchEvent(ce); - } - - function Handler(value) { - this.value = value; - } - - Handler.prototype.handleEvent = function handleEvent(e) { - dispatched = true; - if (drop) { - e.currentTarget.removeEventListener(e.type, this, false); - } else { - value = this.value; - } - }; - - function DOMMap() { - counter++; // make id clashing highly improbable - this.__ce__ = new Event(('@DOMMap:' + counter) + Math.random()); - } - - DOMMap.prototype = { - 'constructor': DOMMap, - 'delete': function del(key) { - return dispatch(key, this.__ce__, true), dispatched; - }, - 'get': function get(key) { - dispatch(key, this.__ce__, false); - var v = value; - value = undefined; - return v; - }, - 'has': function has(key) { - return dispatch(key, this.__ce__, false), dispatched; - }, - 'set': function set(key, value) { - dispatch(key, this.__ce__, true); - key.addEventListener(this.__ce__.type, new Handler(value), false); - return this; - }, - }; - - return DOMMap; - - }()); - - function Dict() {} - Dict.prototype = (Object.create || Object)(null); - - // https://dom.spec.whatwg.org/#interface-eventtarget - - function createEventListener(type, callback, options) { - function eventListener(e) { - if (eventListener.once) { - e.currentTarget.removeEventListener( - e.type, - callback, - eventListener - ); - eventListener.removed = true; - } - if (eventListener.passive) { - e.preventDefault = createEventListener.preventDefault; - } - if (typeof eventListener.callback === 'function') { - /* jshint validthis: true */ - eventListener.callback.call(this, e); - } else if (eventListener.callback) { - eventListener.callback.handleEvent(e); - } - if (eventListener.passive) { - delete e.preventDefault; - } - } - eventListener.type = type; - eventListener.callback = callback; - eventListener.capture = !!options.capture; - eventListener.passive = !!options.passive; - eventListener.once = !!options.once; - // currently pointless but specs say to use it, so ... - eventListener.removed = false; - return eventListener; - } - - createEventListener.preventDefault = function preventDefault() {}; - - var - Event = global.CustomEvent, - dE = global.dispatchEvent, - aEL = global.addEventListener, - rEL = global.removeEventListener, - counter = 0, - increment = function () { counter++; }, - indexOf = [].indexOf || function indexOf(value){ - var length = this.length; - while(length--) { - if (this[length] === value) { - break; - } - } - return length; - }, - getListenerKey = function (options) { - return ''.concat( - options.capture ? '1' : '0', - options.passive ? '1' : '0', - options.once ? '1' : '0' - ); - }, - augment - ; - - try { - aEL('_', increment, {once: true}); - dE(new Event('_')); - dE(new Event('_')); - rEL('_', increment, {once: true}); - } catch(o_O) {} - - if (counter !== 1) { - (function () { - var dm = new DOMMap(); - function createAEL(aEL) { - return function addEventListener(type, handler, options) { - if (options && typeof options !== 'boolean') { - var - info = dm.get(this), - key = getListenerKey(options), - i, tmp, wrap - ; - if (!info) dm.set(this, (info = new Dict())); - if (!(type in info)) info[type] = { - handler: [], - wrap: [] - }; - tmp = info[type]; - i = indexOf.call(tmp.handler, handler); - if (i < 0) { - i = tmp.handler.push(handler) - 1; - tmp.wrap[i] = (wrap = new Dict()); - } else { - wrap = tmp.wrap[i]; - } - if (!(key in wrap)) { - wrap[key] = createEventListener(type, handler, options); - aEL.call(this, type, wrap[key], wrap[key].capture); - } - } else { - aEL.call(this, type, handler, options); - } - }; - } - function createREL(rEL) { - return function removeEventListener(type, handler, options) { - if (options && typeof options !== 'boolean') { - var - info = dm.get(this), - key, i, tmp, wrap - ; - if (info && (type in info)) { - tmp = info[type]; - i = indexOf.call(tmp.handler, handler); - if (-1 < i) { - key = getListenerKey(options); - wrap = tmp.wrap[i]; - if (key in wrap) { - rEL.call(this, type, wrap[key], wrap[key].capture); - delete wrap[key]; - // return if there are other wraps - for (key in wrap) return; - // otherwise remove all the things - tmp.handler.splice(i, 1); - tmp.wrap.splice(i, 1); - // if there are no other handlers - if (tmp.handler.length === 0) - // drop the info[type] entirely - delete info[type]; - } - } - } - } else { - rEL.call(this, type, handler, options); - } - }; - } - - augment = function (Constructor) { - if (!Constructor) return; - var proto = Constructor.prototype; - proto.addEventListener = createAEL(proto.addEventListener); - proto.removeEventListener = createREL(proto.removeEventListener); - }; - - if (global.EventTarget) { - augment(EventTarget); - } else { - augment(global.Text); - augment(global.Element || global.HTMLElement); - augment(global.HTMLDocument); - augment(global.Window || {prototype:global}); - augment(global.XMLHttpRequest); - } - - }()); - } - -}(window)); diff --git a/tgui/packages/tgui-polyfill/03-css-om.js b/tgui/packages/tgui-polyfill/03-css-om.js deleted file mode 100644 index 757d14f928583..0000000000000 --- a/tgui/packages/tgui-polyfill/03-css-om.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * CSS Object Model patches - * - * Adapted from: https://github.com/shawnbot/aight - * - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -/* eslint-disable */ -(function(Proto) { - 'use strict'; - - if (typeof Proto.setAttribute !== 'undefined') { - function toAttr(prop) { - return prop.replace(/-[a-z]/g, function (bit) { - return bit[1].toUpperCase(); - }); - } - - Proto.setProperty = function (prop, value) { - var attr = toAttr(prop); - if (!value) { - return this.removeAttribute(attr); - } - var str = String(value); - return this.setAttribute(attr, str); - }; - - Proto.getPropertyValue = function (prop) { - var attr = toAttr(prop); - return this.getAttribute(attr) || null; - }; - - Proto.removeProperty = function (prop) { - var attr = toAttr(prop); - var value = this.getAttribute(attr); - this.removeAttribute(attr); - return value; - }; - } - -})(CSSStyleDeclaration.prototype); diff --git a/tgui/packages/tgui-polyfill/1-misc.js b/tgui/packages/tgui-polyfill/1-misc.js new file mode 100644 index 0000000000000..bbb73e1040eb2 --- /dev/null +++ b/tgui/packages/tgui-polyfill/1-misc.js @@ -0,0 +1,51 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +/* eslint-disable */ +(function () { + 'use strict'; + + // Inferno needs Int32Array, and it is not covered by core-js. + if (!window.Int32Array) { + window.Int32Array = Array; + } + + // ie11 polyfills + !(function () { + // append + function t() { + var e = Array.prototype.slice.call(arguments), + n = document.createDocumentFragment(); + e.forEach(function (e) { + var t = e instanceof Node; + n.appendChild(t ? e : document.createTextNode(String(e))); + }), + this.appendChild(n); + } + // remove + function n() { + this.parentNode && this.parentNode.removeChild(this); + } + + // add to prototype + [Element.prototype, Document.prototype, DocumentFragment.prototype].forEach(function (e) { + e.hasOwnProperty('append') || + Object.defineProperty(e, 'append', { + configurable: !0, + enumerable: !0, + writable: !0, + value: t, + }); + e.hasOwnProperty('remove') || + Object.defineProperty(e, 'remove', { + configurable: !0, + enumerable: !0, + writable: !0, + value: n, + }); + }); + })(); +})(); diff --git a/tgui/packages/tgui-polyfill/10-misc.js b/tgui/packages/tgui-polyfill/10-misc.js deleted file mode 100644 index 5a1849e13558a..0000000000000 --- a/tgui/packages/tgui-polyfill/10-misc.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -/* eslint-disable */ -(function () { - 'use strict'; - - // Necessary polyfill to make Webpack code splitting work on IE8 - if (!Function.prototype.bind) (function () { - var slice = Array.prototype.slice; - Function.prototype.bind = function () { - var thatFunc = this, thatArg = arguments[0]; - var args = slice.call(arguments, 1); - if (typeof thatFunc !== 'function') { - // closest thing possible to the ECMAScript 5 - // internal IsCallable function - throw new TypeError('Function.prototype.bind - ' + - 'what is trying to be bound is not callable'); - } - return function () { - var funcArgs = args.concat(slice.call(arguments)) - return thatFunc.apply(thatArg, funcArgs); - }; - }; - })(); - - if (!Array.prototype['forEach']) { - Array.prototype.forEach = function (callback, thisArg) { - if (this == null) { - throw new TypeError('Array.prototype.forEach called on null or undefined'); - } - var T, k; - var O = Object(this); - var len = O.length >>> 0; - if (typeof callback !== "function") { - throw new TypeError(callback + ' is not a function'); - } - if (arguments.length > 1) { - T = thisArg; - } - k = 0; - while (k < len) { - var kValue; - if (k in O) { - kValue = O[k]; - callback.call(T, kValue, k, O); - } - k++; - } - }; - } - - // Inferno needs Int32Array, and it is not covered by core-js. - if (!window.Int32Array) { - window.Int32Array = Array; - } - -})(); diff --git a/tgui/packages/tgui-polyfill/package.json b/tgui/packages/tgui-polyfill/package.json index 6f0080b617d23..12241f48a683a 100644 --- a/tgui/packages/tgui-polyfill/package.json +++ b/tgui/packages/tgui-polyfill/package.json @@ -1,16 +1,16 @@ { "private": true, "name": "tgui-polyfill", - "version": "4.3.0", + "version": "4.4.0", "scripts": { - "tgui-polyfill:build": "terser 00-html5shiv.js 01-ie8.js 02-dom4.js 03-css-om.js 10-misc.js --ie8 -f ascii_only,comments=false -o ../../public/tgui-polyfill.min.js" + "tgui-polyfill:build": "terser 1-misc.js -f ascii_only,comments=false -o ../../public/tgui-polyfill.min.js" }, "dependencies": { - "core-js": "^3.29.0", - "regenerator-runtime": "^0.13.11", + "core-js": "^3.39.0", + "regenerator-runtime": "^0.14.1", "unfetch": "^5.0.0" }, "devDependencies": { - "terser": "^5.16.5" + "terser": "^5.37.0" } } diff --git a/tgui/packages/tgui-say/package.json b/tgui/packages/tgui-say/package.json index 4038f13527f1e..4d76c98927d3f 100644 --- a/tgui/packages/tgui-say/package.json +++ b/tgui/packages/tgui-say/package.json @@ -5,7 +5,7 @@ "dependencies": { "common": "workspace:*", "dompurify": "^3.0.1", - "inferno": "^8.2.1", + "inferno": "^8.2.3", "tgui": "workspace:*", "tgui-dev-server": "workspace:*", "tgui-polyfill": "workspace:*" diff --git a/tgui/packages/tgui/assets.ts b/tgui/packages/tgui/assets.ts index 7ae36cb126e9f..df9cf7a428c3c 100644 --- a/tgui/packages/tgui/assets.ts +++ b/tgui/packages/tgui/assets.ts @@ -4,44 +4,41 @@ * @license MIT */ +import { Action, AnyAction, Middleware } from '../common/redux'; + +import { Dispatch } from 'common/redux'; + const EXCLUDED_PATTERNS = [/v4shim/i]; -export const loadedMappings = {}; +export const loadedMappings: Record = {}; -export const resolveAsset = (name) => loadedMappings[name] || name; +export const resolveAsset = (name: string): string => loadedMappings[name] || name; -type Action = - | { - type: 'asset/stylesheet'; - payload: string; +export const assetMiddleware: Middleware = + (storeApi) => + (next: Dispatch) => + (action: ActionType) => { + const { type, payload } = action as AnyAction; + if (type === 'asset/stylesheet') { + Byond.loadCss(payload); + return; } - | { - type: 'asset/mappings'; - payload: Record; - }; - -export const assetMiddleware = (store) => (next) => (action: Action) => { - const { type, payload } = action; - if (type === 'asset/stylesheet') { - Byond.loadCss(payload); - return; - } - if (type === 'asset/mappings') { - for (let name of Object.keys(payload)) { - // Skip anything that matches excluded patterns - if (EXCLUDED_PATTERNS.some((regex) => regex.test(name))) { - continue; - } - const url = payload[name]; - const ext = name.split('.').pop(); - loadedMappings[name] = url; - if (ext === 'css') { - Byond.loadCss(url); - } - if (ext === 'js') { - Byond.loadJs(url); + if (type === 'asset/mappings') { + for (const name of Object.keys(payload)) { + // Skip anything that matches excluded patterns + if (EXCLUDED_PATTERNS.some((regex) => regex.test(name))) { + continue; + } + const url = payload[name]; + const ext = name.split('.').pop(); + loadedMappings[name] = url; + if (ext === 'css') { + Byond.loadCss(url); + } + if (ext === 'js') { + Byond.loadJs(url); + } } + return; } - return; - } - next(action); -}; + next(action); + }; diff --git a/tgui/packages/tgui/components/AnimatedNumber.js b/tgui/packages/tgui/components/AnimatedNumber.jsx similarity index 100% rename from tgui/packages/tgui/components/AnimatedNumber.js rename to tgui/packages/tgui/components/AnimatedNumber.jsx diff --git a/tgui/packages/tgui/components/Blink.js b/tgui/packages/tgui/components/Blink.jsx similarity index 100% rename from tgui/packages/tgui/components/Blink.js rename to tgui/packages/tgui/components/Blink.jsx diff --git a/tgui/packages/tgui/components/BlockQuote.js b/tgui/packages/tgui/components/BlockQuote.jsx similarity index 100% rename from tgui/packages/tgui/components/BlockQuote.js rename to tgui/packages/tgui/components/BlockQuote.jsx diff --git a/tgui/packages/tgui/components/BufferedTextArea.tsx b/tgui/packages/tgui/components/BufferedTextArea.tsx index d51c6686f1402..8061c85b2b21e 100644 --- a/tgui/packages/tgui/components/BufferedTextArea.tsx +++ b/tgui/packages/tgui/components/BufferedTextArea.tsx @@ -18,7 +18,7 @@ interface BufferedTextAreaPropsUnique { type BufferedTextAreaProps = BufferedTextAreaPropsUnique & Record; export class BufferedTextArea extends Component { - bufferTimer: NodeJS.Timer; + bufferTimer: NodeJS.Timeout; state = { bufferedText: this.props.value || '', textChanged: false, diff --git a/tgui/packages/tgui/components/Button.js b/tgui/packages/tgui/components/Button.jsx similarity index 100% rename from tgui/packages/tgui/components/Button.js rename to tgui/packages/tgui/components/Button.jsx diff --git a/tgui/packages/tgui/components/ByondUi.js b/tgui/packages/tgui/components/ByondUi.jsx similarity index 100% rename from tgui/packages/tgui/components/ByondUi.js rename to tgui/packages/tgui/components/ByondUi.jsx diff --git a/tgui/packages/tgui/components/Chart.js b/tgui/packages/tgui/components/Chart.jsx similarity index 100% rename from tgui/packages/tgui/components/Chart.js rename to tgui/packages/tgui/components/Chart.jsx diff --git a/tgui/packages/tgui/components/Collapsible.js b/tgui/packages/tgui/components/Collapsible.jsx similarity index 100% rename from tgui/packages/tgui/components/Collapsible.js rename to tgui/packages/tgui/components/Collapsible.jsx diff --git a/tgui/packages/tgui/components/CollapsibleSection.js b/tgui/packages/tgui/components/CollapsibleSection.jsx similarity index 100% rename from tgui/packages/tgui/components/CollapsibleSection.js rename to tgui/packages/tgui/components/CollapsibleSection.jsx diff --git a/tgui/packages/tgui/components/ColorBox.js b/tgui/packages/tgui/components/ColorBox.jsx similarity index 100% rename from tgui/packages/tgui/components/ColorBox.js rename to tgui/packages/tgui/components/ColorBox.jsx diff --git a/tgui/packages/tgui/components/Dimmer.js b/tgui/packages/tgui/components/Dimmer.jsx similarity index 100% rename from tgui/packages/tgui/components/Dimmer.js rename to tgui/packages/tgui/components/Dimmer.jsx diff --git a/tgui/packages/tgui/components/Divider.js b/tgui/packages/tgui/components/Divider.jsx similarity index 100% rename from tgui/packages/tgui/components/Divider.js rename to tgui/packages/tgui/components/Divider.jsx diff --git a/tgui/packages/tgui/components/DraggableClickableControl.js b/tgui/packages/tgui/components/DraggableClickableControl.jsx similarity index 100% rename from tgui/packages/tgui/components/DraggableClickableControl.js rename to tgui/packages/tgui/components/DraggableClickableControl.jsx diff --git a/tgui/packages/tgui/components/DraggableControl.js b/tgui/packages/tgui/components/DraggableControl.jsx similarity index 100% rename from tgui/packages/tgui/components/DraggableControl.js rename to tgui/packages/tgui/components/DraggableControl.jsx diff --git a/tgui/packages/tgui/components/Dropdown.tsx b/tgui/packages/tgui/components/Dropdown.tsx index 8c2be0402882e..e618295f55917 100644 --- a/tgui/packages/tgui/components/Dropdown.tsx +++ b/tgui/packages/tgui/components/Dropdown.tsx @@ -74,24 +74,16 @@ export class Dropdown extends Component { getBoundingClientRect: () => Dropdown.currentOpenMenu?.getBoundingClientRect() ?? NULL_RECT, }; menuContents: any; - handleClick: any; state: DropdownState = { open: false, + selected: this.props.selected, }; - constructor(props) { - super(props); - - this.state = { - selected: props.selected, - open: props.open, - }; - this.handleClick = () => { - if (this.state.open) { - this.setOpen(false); - } - }; - } + handleClick = () => { + if (this.state.open) { + this.setOpen(false); + } + }; getDOMNode() { return findDOMFromVNode(this.$LI, true); @@ -179,7 +171,7 @@ export class Dropdown extends Component { return (
{ this.setSelected(value); }}> @@ -256,27 +248,39 @@ export class Dropdown extends Component { } toPrevious(): void { - const selectedIndex = this.getSelectedIndex(); - - if (selectedIndex < 0) { + if (this.props.options.length < 1) { return; } + let selectedIndex = this.getSelectedIndex(); + const startIndex = 0; const endIndex = this.props.options.length - 1; - const previousIndex = selectedIndex === 0 ? endIndex : selectedIndex - 1; + + const hasSelected = selectedIndex >= 0; + if (!hasSelected) { + selectedIndex = startIndex; + } + + const previousIndex = selectedIndex === startIndex ? endIndex : selectedIndex - 1; this.setSelected(this.getOptionValue(this.props.options[previousIndex])); } toNext(): void { - const selectedIndex = this.getSelectedIndex(); - - if (selectedIndex < 0) { + if (this.props.options.length < 1) { return; } + let selectedIndex = this.getSelectedIndex(); + const startIndex = 0; const endIndex = this.props.options.length - 1; - const nextIndex = selectedIndex === endIndex ? 0 : selectedIndex + 1; + + const hasSelected = selectedIndex >= 0; + if (!hasSelected) { + selectedIndex = endIndex; + } + + const nextIndex = selectedIndex === endIndex ? startIndex : selectedIndex + 1; this.setSelected(this.getOptionValue(this.props.options[nextIndex])); } diff --git a/tgui/packages/tgui/components/Grid.js b/tgui/packages/tgui/components/Grid.jsx similarity index 100% rename from tgui/packages/tgui/components/Grid.js rename to tgui/packages/tgui/components/Grid.jsx diff --git a/tgui/packages/tgui/components/InfinitePlane.js b/tgui/packages/tgui/components/InfinitePlane.jsx similarity index 100% rename from tgui/packages/tgui/components/InfinitePlane.js rename to tgui/packages/tgui/components/InfinitePlane.jsx diff --git a/tgui/packages/tgui/components/Input.js b/tgui/packages/tgui/components/Input.jsx similarity index 100% rename from tgui/packages/tgui/components/Input.js rename to tgui/packages/tgui/components/Input.jsx diff --git a/tgui/packages/tgui/components/Knob.js b/tgui/packages/tgui/components/Knob.jsx similarity index 100% rename from tgui/packages/tgui/components/Knob.js rename to tgui/packages/tgui/components/Knob.jsx diff --git a/tgui/packages/tgui/components/LabeledControls.js b/tgui/packages/tgui/components/LabeledControls.jsx similarity index 100% rename from tgui/packages/tgui/components/LabeledControls.js rename to tgui/packages/tgui/components/LabeledControls.jsx diff --git a/tgui/packages/tgui/components/Modal.js b/tgui/packages/tgui/components/Modal.jsx similarity index 100% rename from tgui/packages/tgui/components/Modal.js rename to tgui/packages/tgui/components/Modal.jsx diff --git a/tgui/packages/tgui/components/NoticeBox.js b/tgui/packages/tgui/components/NoticeBox.jsx similarity index 100% rename from tgui/packages/tgui/components/NoticeBox.js rename to tgui/packages/tgui/components/NoticeBox.jsx diff --git a/tgui/packages/tgui/components/NumberInput.js b/tgui/packages/tgui/components/NumberInput.jsx similarity index 100% rename from tgui/packages/tgui/components/NumberInput.js rename to tgui/packages/tgui/components/NumberInput.jsx diff --git a/tgui/packages/tgui/components/OrbitalMapComponent.js b/tgui/packages/tgui/components/OrbitalMapComponent.jsx similarity index 100% rename from tgui/packages/tgui/components/OrbitalMapComponent.js rename to tgui/packages/tgui/components/OrbitalMapComponent.jsx diff --git a/tgui/packages/tgui/components/OrbitalMapSvg.js b/tgui/packages/tgui/components/OrbitalMapSvg.jsx similarity index 100% rename from tgui/packages/tgui/components/OrbitalMapSvg.js rename to tgui/packages/tgui/components/OrbitalMapSvg.jsx diff --git a/tgui/packages/tgui/components/ProgressBar.js b/tgui/packages/tgui/components/ProgressBar.js deleted file mode 100644 index 151516299b655..0000000000000 --- a/tgui/packages/tgui/components/ProgressBar.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -import { clamp01, scale, keyOfMatchingRange, toFixed } from 'common/math'; -import { classes, pureComponentHooks } from 'common/react'; -import { computeBoxClassName, computeBoxProps } from './Box'; - -export const ProgressBar = (props) => { - const { className, value, minValue = 0, maxValue = 1, color, ranges = {}, children, ...rest } = props; - const scaledValue = scale(value, minValue, maxValue); - const hasContent = children !== undefined; - const effectiveColor = color || keyOfMatchingRange(value, ranges) || 'default'; - return ( -
-
-
{hasContent ? children : toFixed(scaledValue * 100) + '%'}
-
- ); -}; - -ProgressBar.defaultHooks = pureComponentHooks; diff --git a/tgui/packages/tgui/components/ProgressBar.jsx b/tgui/packages/tgui/components/ProgressBar.jsx new file mode 100644 index 0000000000000..1f7658e28f159 --- /dev/null +++ b/tgui/packages/tgui/components/ProgressBar.jsx @@ -0,0 +1,42 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { clamp01, scale, keyOfMatchingRange, toFixed } from 'common/math'; +import { classes, pureComponentHooks } from 'common/react'; +import { computeBoxClassName, computeBoxProps } from './Box'; +import { CSS_COLORS } from '../constants'; + +export const ProgressBar = (props) => { + const { className, value, minValue = 0, maxValue = 1, color, ranges = {}, children, ...rest } = props; + const scaledValue = scale(value, minValue, maxValue); + const hasContent = children !== undefined; + const effectiveColor = color || keyOfMatchingRange(value, ranges) || 'default'; + + // We permit colors to be in hex format, rgb()/rgba() format, + // a name for a color- class, or a base CSS class. + const outerProps = computeBoxProps(rest); + const outerClasses = ['ProgressBar', className, computeBoxClassName(rest)]; + const fillStyles = { + 'width': clamp01(scaledValue) * 100 + '%', + }; + if (CSS_COLORS.includes(effectiveColor) || effectiveColor === 'default') { + // If the color is a color- class, just use that. + outerClasses.push('ProgressBar--color--' + effectiveColor); + } else { + // Otherwise, set styles directly. + outerProps.style = (outerProps.style || '') + `border-color: ${effectiveColor};`; + fillStyles['background-color'] = effectiveColor; + } + + return ( +
+
+
{hasContent ? children : toFixed(scaledValue * 100) + '%'}
+
+ ); +}; + +ProgressBar.defaultHooks = pureComponentHooks; diff --git a/tgui/packages/tgui/components/RestrictedInput.js b/tgui/packages/tgui/components/RestrictedInput.jsx similarity index 100% rename from tgui/packages/tgui/components/RestrictedInput.js rename to tgui/packages/tgui/components/RestrictedInput.jsx diff --git a/tgui/packages/tgui/components/ScrollableBox.js b/tgui/packages/tgui/components/ScrollableBox.jsx similarity index 100% rename from tgui/packages/tgui/components/ScrollableBox.js rename to tgui/packages/tgui/components/ScrollableBox.jsx diff --git a/tgui/packages/tgui/components/Section.tsx b/tgui/packages/tgui/components/Section.tsx index b3adbcbc756c6..13dcf2dadbb99 100644 --- a/tgui/packages/tgui/components/Section.tsx +++ b/tgui/packages/tgui/components/Section.tsx @@ -4,11 +4,11 @@ * @license MIT */ -import { canRender, classes } from 'common/react'; -import { Component, createRef, RefObject } from 'inferno'; -import { addScrollableNode, removeScrollableNode } from '../events'; import { BoxProps, computeBoxClassName, computeBoxProps } from './Box'; +import { Component, createRef, RefObject } from 'inferno'; import type { InfernoNode } from 'inferno'; +import { addScrollableNode, removeScrollableNode } from '../events'; +import { canRender, classes } from 'common/react'; interface SectionProps extends BoxProps { className?: string; @@ -18,9 +18,9 @@ interface SectionProps extends BoxProps { fitted?: boolean; scrollable?: boolean; /** @deprecated This property no longer works, please remove it. */ - level?: boolean; + level?: never; /** @deprecated Please use `scrollable` property */ - overflowY?: any; + overflowY?: never; /** @member Allows external control of scrolling. */ scrollableRef?: RefObject; /** @member Callback function for the `scroll` event */ @@ -41,7 +41,7 @@ export class Section extends Component { componentDidMount() { if (this.scrollable) { - addScrollableNode(this.scrollableRef.current); + addScrollableNode(this.scrollableRef.current as HTMLElement); if (this.onScroll && this.scrollableRef.current) { this.scrollableRef.current.onscroll = this.onScroll; } @@ -50,7 +50,7 @@ export class Section extends Component { componentWillUnmount() { if (this.scrollable) { - removeScrollableNode(this.scrollableRef.current); + removeScrollableNode(this.scrollableRef.current as HTMLElement); } } diff --git a/tgui/packages/tgui/components/Slider.js b/tgui/packages/tgui/components/Slider.jsx similarity index 100% rename from tgui/packages/tgui/components/Slider.js rename to tgui/packages/tgui/components/Slider.jsx diff --git a/tgui/packages/tgui/components/Table.js b/tgui/packages/tgui/components/Table.jsx similarity index 100% rename from tgui/packages/tgui/components/Table.js rename to tgui/packages/tgui/components/Table.jsx diff --git a/tgui/packages/tgui/components/Tabs.js b/tgui/packages/tgui/components/Tabs.jsx similarity index 100% rename from tgui/packages/tgui/components/Tabs.js rename to tgui/packages/tgui/components/Tabs.jsx diff --git a/tgui/packages/tgui/components/TextArea.js b/tgui/packages/tgui/components/TextArea.jsx similarity index 100% rename from tgui/packages/tgui/components/TextArea.js rename to tgui/packages/tgui/components/TextArea.jsx diff --git a/tgui/packages/tgui/components/TimeDisplay.js b/tgui/packages/tgui/components/TimeDisplay.jsx similarity index 100% rename from tgui/packages/tgui/components/TimeDisplay.js rename to tgui/packages/tgui/components/TimeDisplay.jsx diff --git a/tgui/packages/tgui/components/index.js b/tgui/packages/tgui/components/index.ts similarity index 100% rename from tgui/packages/tgui/components/index.js rename to tgui/packages/tgui/components/index.ts diff --git a/tgui/packages/tgui/constants.js b/tgui/packages/tgui/constants.ts similarity index 61% rename from tgui/packages/tgui/constants.js rename to tgui/packages/tgui/constants.ts index 182bcd0634c7a..0697a2395c5ec 100644 --- a/tgui/packages/tgui/constants.js +++ b/tgui/packages/tgui/constants.ts @@ -4,6 +4,14 @@ * @license MIT */ +type Gas = { + id: string; + path: string; + name: string; + label: string; + color: string; +}; + // UI states, which are mirrored from the BYOND code. export const UI_INTERACTIVE = 2; export const UI_UPDATE = 1; @@ -31,7 +39,12 @@ export const COLORS = { burn: '#e67e22', brute: '#e74c3c', }, -}; + // reagent / chemistry related colours + reagent: { + acidicbuffer: '#fbc314', + basicbuffer: '#3853a4', + }, +} as const; // Colors defined in CSS export const CSS_COLORS = [ @@ -126,7 +139,7 @@ export const RADIO_CHANNELS = [ freq: 1459, color: '#1ecc43', }, -]; +] as const; const GASES = [ { @@ -157,7 +170,7 @@ const GASES = [ 'id': 'water_vapor', 'name': 'Water Vapor', 'label': 'H₂O', - 'color': 'grey', + 'color': 'lightsteelblue', }, { 'id': 'nob', @@ -169,7 +182,7 @@ const GASES = [ 'id': 'n2o', 'name': 'Nitrous Oxide', 'label': 'N₂O', - 'color': 'red', + 'color': 'bisque', }, { 'id': 'no2', @@ -181,25 +194,25 @@ const GASES = [ 'id': 'tritium', 'name': 'Tritium', 'label': 'Tritium', - 'color': 'green', + 'color': 'limegreen', }, { 'id': 'bz', 'name': 'BZ', 'label': 'BZ', - 'color': 'purple', + 'color': 'mediumpurple', }, { 'id': 'stim', 'name': 'Stimulum', 'label': 'Stimulum', - 'color': 'purple', + 'color': 'darkviolet', }, { 'id': 'pluox', 'name': 'Pluoxium', 'label': 'Pluoxium', - 'color': 'blue', + 'color': 'mediumslateblue', }, { 'id': 'miasma', @@ -207,22 +220,88 @@ const GASES = [ 'label': 'Miasma', 'color': 'olive', }, + { + 'id': 'Freon', + 'name': 'Freon', + 'label': 'Freon', + 'color': 'paleturquoise', + }, { 'id': 'hydrogen', 'name': 'Hydrogen', 'label': 'H₂', 'color': 'white', }, -]; + // Bee doesn't have most of these \/ - but having them for future proofing is useful. Nothing iterates this list. + { + 'id': 'healium', + 'name': 'Healium', + 'label': 'Healium', + 'color': 'salmon', + }, + { + 'id': 'proto_nitrate', + 'name': 'Proto Nitrate', + 'label': 'Proto-Nitrate', + 'color': 'greenyellow', + }, + { + 'id': 'zauker', + 'name': 'Zauker', + 'label': 'Zauker', + 'color': 'darkgreen', + }, + { + 'id': 'halon', + 'name': 'Halon', + 'label': 'Halon', + 'color': 'purple', + }, + { + 'id': 'helium', + 'name': 'Helium', + 'label': 'He', + 'color': 'aliceblue', + }, + { + 'id': 'antinoblium', + 'name': 'Antinoblium', + 'label': 'Anti-Noblium', + 'color': 'maroon', + }, +] as const; -export const getGasLabel = (gasId, fallbackValue) => { - const gasSearchString = String(gasId).toLowerCase(); +// Returns gas label based on gasId +export const getGasLabel = (gasId: string, fallbackValue?: string) => { + const gasSearchString = gasId.toLowerCase(); const gas = GASES.find((gas) => gas.id === gasSearchString || gas.name.toLowerCase() === gasSearchString); - return (gas && gas.label) || fallbackValue || gasId; + return gas?.label || fallbackValue || gasId; }; -export const getGasColor = (gasId) => { - const gasSearchString = String(gasId).toLowerCase(); +// Returns gas color based on gasId +export const getGasColor = (gasId: string) => { + const gasSearchString = gasId.toLowerCase(); const gas = GASES.find((gas) => gas.id === gasSearchString || gas.name.toLowerCase() === gasSearchString); - return gas && gas.color; + return gas?.color; +}; + +/* +From https://github.com/tgstation/tgstation/pull/69240 + +PLEASE enable the tests in constants.test.ts if you port this + +// Returns gas object based on gasId +export const getGasFromId = (gasId: string): Gas | undefined => { + const gasSearchString = gasId.toLowerCase(); + const gas = GASES.find( + (gas) => + gas.id === gasSearchString || gas.name.toLowerCase() === gasSearchString + ); + return gas; +}; + +// Returns gas object based on gasPath +export const getGasFromPath = (gasPath: string): Gas | undefined => { + return GASES.find((gas) => gas.path === gasPath); }; +*/ diff --git a/tgui/packages/tgui/contants.test.ts b/tgui/packages/tgui/contants.test.ts new file mode 100644 index 0000000000000..51c863b3387e9 --- /dev/null +++ b/tgui/packages/tgui/contants.test.ts @@ -0,0 +1,72 @@ +import { getGasColor, getGasLabel } from './constants'; + +describe('gas helper functions', () => { + it('should get the proper gas label', () => { + const gasId = 'antinoblium'; + const gasLabel = getGasLabel(gasId); + expect(gasLabel).toBe('Anti-Noblium'); + }); + + it('should get the proper gas label with a fallback', () => { + const gasId = 'nonexistent'; + const gasLabel = getGasLabel(gasId, 'fallback'); + + expect(gasLabel).toBe('fallback'); + }); + + it('should return the id if no gas and no fallback is found', () => { + const gasId = 'nonexistent'; + const gasLabel = getGasLabel(gasId); + + expect(gasLabel).toBe('nonexistent'); + }); + + it('should get the proper gas color', () => { + const gasId = 'antinoblium'; + const gasColor = getGasColor(gasId); + + expect(gasColor).toBe('maroon'); + }); + + it('should return undefined if no gas is found', () => { + const gasId = 'nonexistent'; + const gasColor = getGasColor(gasId); + + expect(gasColor).toBeUndefined(); + }); + /* + https://github.com/tgstation/tgstation/pull/69240 + it('should return the gas object if found', () => { + const gasId = 'antinoblium'; + const gas = getGasFromId(gasId); + + expect(gas).toEqual({ + id: 'antinoblium', + path: '/datum/gas/antinoblium', + name: 'Antinoblium', + label: 'Anti-Noblium', + color: 'maroon', + }); + }); + + it('should return undefined if no gas is found', () => { + const gasId = 'nonexistent'; + const gas = getGasFromId(gasId); + + expect(gas).toBeUndefined(); + }); + + it('should return the gas using a path', () => { + const gasPath = '/datum/gas/antinoblium'; + const gas = getGasFromPath(gasPath); + + expect(gas).toEqual({ + id: 'antinoblium', + path: '/datum/gas/antinoblium', + name: 'Antinoblium', + label: 'Anti-Noblium', + color: 'maroon', + }); + }); + */ +}); diff --git a/tgui/packages/tgui/debug/KitchenSink.js b/tgui/packages/tgui/debug/KitchenSink.jsx similarity index 95% rename from tgui/packages/tgui/debug/KitchenSink.js rename to tgui/packages/tgui/debug/KitchenSink.jsx index 372074d215ad7..ab51025e1d91e 100644 --- a/tgui/packages/tgui/debug/KitchenSink.js +++ b/tgui/packages/tgui/debug/KitchenSink.jsx @@ -8,7 +8,7 @@ import { useLocalState } from '../backend'; import { Flex, Section, Tabs } from '../components'; import { Pane, Window } from '../layouts'; -const r = require.context('../stories', false, /\.stories\.js$/); +const r = require.context('../stories', false, /\.stories\.jsx$/); /** * @returns {{ diff --git a/tgui/packages/tgui/debug/index.js b/tgui/packages/tgui/debug/index.ts similarity index 100% rename from tgui/packages/tgui/debug/index.js rename to tgui/packages/tgui/debug/index.ts diff --git a/tgui/packages/tgui/drag.js b/tgui/packages/tgui/drag.ts similarity index 69% rename from tgui/packages/tgui/drag.js rename to tgui/packages/tgui/drag.ts index 73c47cce55fdc..048e1c770175c 100644 --- a/tgui/packages/tgui/drag.js +++ b/tgui/packages/tgui/drag.ts @@ -4,47 +4,54 @@ * @license MIT */ -import { storage } from 'common/storage'; -import { vecAdd, vecSubtract, vecMultiply, vecScale } from 'common/vector'; +import { vecAdd, vecMultiply, vecScale, vecSubtract } from 'common/vector'; + import { createLogger } from './logging'; +import { storage } from 'common/storage'; const logger = createLogger('drag'); const pixelRatio = window.devicePixelRatio ?? 1; - let windowKey = Byond.windowId; let dragging = false; let resizing = false; -let screenOffset = [0, 0]; -let screenOffsetPromise; -let dragPointOffset; -let resizeMatrix; -let initialSize; -let size; - -export const setWindowKey = (key) => { +let screenOffset: [number, number] = [0, 0]; +let screenOffsetPromise: Promise<[number, number]>; +let dragPointOffset: [number, number]; +let resizeMatrix: [number, number]; +let initialSize: [number, number]; +let size: [number, number]; + +// Set the window key +export const setWindowKey = (key: string): void => { windowKey = key; }; -const getWindowPosition = () => [window.screenLeft * pixelRatio, window.screenTop * pixelRatio]; +// Get window position +export const getWindowPosition = (): [number, number] => [window.screenLeft * pixelRatio, window.screenTop * pixelRatio]; -const getWindowSize = () => [window.innerWidth * pixelRatio, window.innerHeight * pixelRatio]; +// Get window size +export const getWindowSize = (): [number, number] => [window.innerWidth * pixelRatio, window.innerHeight * pixelRatio]; -const setWindowPosition = (vec) => { +// Set window position +const setWindowPosition = (vec: [number, number]) => { const byondPos = vecAdd(vec, screenOffset); return Byond.winset(Byond.windowId, { pos: byondPos[0] + ',' + byondPos[1], }); }; -const setWindowSize = (vec) => { +// Set window size +const setWindowSize = (vec: [number, number]) => { return Byond.winset(Byond.windowId, { size: vec[0] + 'x' + vec[1], }); }; -const getScreenPosition = () => [0 - screenOffset[0], 0 - screenOffset[1]]; +// Get screen position +const getScreenPosition = (): [number, number] => [0 - screenOffset[0], 0 - screenOffset[1]]; -const getScreenSize = () => [window.screen.availWidth * pixelRatio, window.screen.availHeight * pixelRatio]; +// Get screen size +const getScreenSize = (): [number, number] => [window.screen.availWidth * pixelRatio, window.screen.availHeight * pixelRatio]; /** * Moves an item to the top of the recents array, and keeps its length @@ -54,9 +61,9 @@ const getScreenSize = () => [window.screen.availWidth * pixelRatio, window.scree * * Returns new recents and an item which was trimmed. */ -const touchRecents = (recents, touchedItem, limit = 50) => { - const nextRecents = [touchedItem]; - let trimmedItem; +export const touchRecents = (recents: string[], touchedItem: string, limit = 50): [string[], string | undefined] => { + const nextRecents: string[] = [touchedItem]; + let trimmedItem: string | undefined; for (let i = 0; i < recents.length; i++) { const item = recents[i]; if (item === touchedItem) { @@ -71,6 +78,7 @@ const touchRecents = (recents, touchedItem, limit = 50) => { return [nextRecents, trimmedItem]; }; +// Store window geometry in local storage const storeWindowGeometry = async () => { logger.log('storing geometry'); const geometry = { @@ -86,8 +94,15 @@ const storeWindowGeometry = async () => { storage.set('geometries', geometries); }; -export const recallWindowGeometry = async (options = {}) => { - // Only recall geometry in fancy mode +// Recall window geometry from local storage and apply it +export const recallWindowGeometry = async ( + options: { + fancy?: boolean; + pos?: [number, number]; + size?: [number, number]; + locked?: boolean; + } = {} +) => { const geometry = options.fancy && (await storage.get(windowKey)); if (geometry) { logger.log('recalled geometry:', geometry); @@ -115,14 +130,14 @@ export const recallWindowGeometry = async (options = {}) => { pos = constraintPosition(pos, size)[1]; } setWindowPosition(pos); - } - // Set window position at the center of the screen. - else if (size) { + // Set window position at the center of the screen. + } else if (size) { pos = vecAdd(vecScale(areaAvailable, 0.5), vecScale(size, -0.5), vecScale(screenOffset, -1.0)); setWindowPosition(pos); } }; +// Setup draggable window export const setupDrag = async () => { // Calculate screen offset caused by the windows taskbar let windowPosition = getWindowPosition(); @@ -139,10 +154,10 @@ export const setupDrag = async () => { * Constraints window position to safe screen area, accounting for safe * margins which could be a system taskbar. */ -const constraintPosition = (pos, size) => { +const constraintPosition = (pos: [number, number], size: [number, number]): [boolean, [number, number]] => { const screenPos = getScreenPosition(); const screenSize = getScreenSize(); - const nextPos = [pos[0], pos[1]]; + const nextPos: [number, number] = [pos[0], pos[1]]; let relocated = false; for (let i = 0; i < 2; i++) { const leftBoundary = screenPos[i]; @@ -158,19 +173,21 @@ const constraintPosition = (pos, size) => { return [relocated, nextPos]; }; -export const dragStartHandler = (event) => { +// Start dragging the window +export const dragStartHandler = (event: MouseEvent) => { logger.log('drag start'); dragging = true; let windowPosition = getWindowPosition(); dragPointOffset = vecSubtract([event.screenX, event.screenY], getWindowPosition()); // Focus click target - event.target?.focus(); + (event.target as HTMLElement)?.focus(); document.addEventListener('mousemove', dragMoveHandler); document.addEventListener('mouseup', dragEndHandler); dragMoveHandler(event); }; -const dragEndHandler = (event) => { +// End dragging the window +const dragEndHandler = (event: MouseEvent) => { logger.log('drag end'); dragMoveHandler(event); document.removeEventListener('mousemove', dragMoveHandler); @@ -179,7 +196,8 @@ const dragEndHandler = (event) => { storeWindowGeometry(); }; -const dragMoveHandler = (event) => { +// Move the window while dragging +const dragMoveHandler = (event: MouseEvent) => { if (!dragging) { return; } @@ -187,20 +205,22 @@ const dragMoveHandler = (event) => { setWindowPosition(vecSubtract([event.screenX, event.screenY], dragPointOffset)); }; -export const resizeStartHandler = (x, y) => (event) => { +// Start resizing the window +export const resizeStartHandler = (x: number, y: number) => (event: MouseEvent) => { resizeMatrix = [x, y]; logger.log('resize start', resizeMatrix); resizing = true; dragPointOffset = vecSubtract([event.screenX, event.screenY], getWindowPosition()); initialSize = getWindowSize(); // Focus click target - event.target?.focus(); + (event.target as HTMLElement)?.focus(); document.addEventListener('mousemove', resizeMoveHandler); document.addEventListener('mouseup', resizeEndHandler); resizeMoveHandler(event); }; -const resizeEndHandler = (event) => { +// End resizing the window +const resizeEndHandler = (event: MouseEvent) => { logger.log('resize end', size); resizeMoveHandler(event); document.removeEventListener('mousemove', resizeMoveHandler); @@ -209,7 +229,8 @@ const resizeEndHandler = (event) => { storeWindowGeometry(); }; -const resizeMoveHandler = (event) => { +// Move the window while resizing +const resizeMoveHandler = (event: MouseEvent) => { if (!resizing) { return; } diff --git a/tgui/packages/tgui/events.test.ts b/tgui/packages/tgui/events.test.ts new file mode 100644 index 0000000000000..8f3e8e3109c3f --- /dev/null +++ b/tgui/packages/tgui/events.test.ts @@ -0,0 +1,60 @@ +import { KeyEvent, addScrollableNode, canStealFocus, removeScrollableNode, setupGlobalEvents } from './events'; + +describe('focusEvents', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('setupGlobalEvents sets the ignoreWindowFocus flag correctly', () => { + setupGlobalEvents({ ignoreWindowFocus: true }); + // Test other functionality that depends on the ignoreWindowFocus flag + }); + + it('canStealFocus returns true for input and textarea elements', () => { + const inputElement = document.createElement('input'); + const textareaElement = document.createElement('textarea'); + const divElement = document.createElement('div'); + + expect(canStealFocus(inputElement)).toBe(true); + expect(canStealFocus(textareaElement)).toBe(true); + expect(canStealFocus(divElement)).toBe(false); + }); + + it('addScrollableNode and removeScrollableNode manage the list of scrollable nodes correctly', () => { + const divElement1 = document.createElement('div'); + const divElement2 = document.createElement('div'); + + addScrollableNode(divElement1); + addScrollableNode(divElement2); + // Test other functionality that depends on the list of scrollable nodes + + removeScrollableNode(divElement1); + removeScrollableNode(divElement2); + // Test other functionality that depends on the list of scrollable nodes + }); + + it('KeyEvent class works correctly', () => { + const keyboardEvent = new KeyboardEvent('keydown', { + key: 'a', + keyCode: 65, + ctrlKey: true, + altKey: true, + shiftKey: true, + }); + + const keyEvent = new KeyEvent(keyboardEvent, 'keydown', false); + + expect(keyEvent.event).toBe(keyboardEvent); + expect(keyEvent.type).toBe('keydown'); + expect(keyEvent.code).toBe(65); + expect(keyEvent.ctrl).toBe(true); + expect(keyEvent.alt).toBe(true); + expect(keyEvent.shift).toBe(true); + expect(keyEvent.repeat).toBe(false); + expect(keyEvent.hasModifierKeys()).toBe(true); + expect(keyEvent.isModifierKey()).toBe(false); + expect(keyEvent.isDown()).toBe(true); + expect(keyEvent.isUp()).toBe(false); + expect(keyEvent.toString()).toBe('Ctrl+Alt+Shift+A'); + }); +}); diff --git a/tgui/packages/tgui/events.js b/tgui/packages/tgui/events.ts similarity index 76% rename from tgui/packages/tgui/events.js rename to tgui/packages/tgui/events.ts index bcd4bb3ec4c78..37db0c2e3ac60 100644 --- a/tgui/packages/tgui/events.js +++ b/tgui/packages/tgui/events.ts @@ -6,25 +6,24 @@ * @license MIT */ -import { EventEmitter } from 'common/events'; import { KEY_ALT, KEY_CTRL, KEY_F1, KEY_F12, KEY_SHIFT } from 'common/keycodes'; -import { logger } from './logging'; +import { EventEmitter } from 'common/events'; export const globalEvents = new EventEmitter(); let ignoreWindowFocus = false; -export const setupGlobalEvents = (options = {}) => { +export const setupGlobalEvents = (options: { ignoreWindowFocus?: boolean } = {}): void => { ignoreWindowFocus = !!options.ignoreWindowFocus; }; // Window focus // -------------------------------------------------------- -let windowFocusTimeout; +let windowFocusTimeout: ReturnType | null; let windowFocused = true; -const setWindowFocus = (value, delayed) => { - // Pretend to always be in focus. +// Pretend to always be in focus. +const setWindowFocus = (value: boolean, delayed?: boolean) => { if (ignoreWindowFocus) { windowFocused = true; return; @@ -47,14 +46,14 @@ const setWindowFocus = (value, delayed) => { // Focus stealing // -------------------------------------------------------- -let focusStolenBy = null; +let focusStolenBy: HTMLElement | null = null; -export const canStealFocus = (node) => { +export const canStealFocus = (node: HTMLElement) => { const tag = String(node.tagName).toLowerCase(); return tag === 'input' || tag === 'textarea'; }; -const stealFocus = (node) => { +const stealFocus = (node: HTMLElement) => { releaseStolenFocus(); focusStolenBy = node; focusStolenBy.addEventListener('blur', releaseStolenFocus); @@ -70,22 +69,22 @@ const releaseStolenFocus = () => { // Focus follows the mouse // -------------------------------------------------------- -let focusedNode = null; -let lastVisitedNode = null; -const trackedNodes = []; +let focusedNode: HTMLElement | null = null; +let lastVisitedNode: HTMLElement | null = null; +const trackedNodes: HTMLElement[] = []; -export const addScrollableNode = (node) => { +export const addScrollableNode = (node: HTMLElement) => { trackedNodes.push(node); }; -export const removeScrollableNode = (node) => { +export const removeScrollableNode = (node: HTMLElement) => { const index = trackedNodes.indexOf(node); if (index >= 0) { trackedNodes.splice(index, 1); } }; -const focusNearestTrackedParent = (node) => { +const focusNearestTrackedParent = (node: HTMLElement | null) => { if (focusStolenBy || !windowFocused) { return; } @@ -100,12 +99,12 @@ const focusNearestTrackedParent = (node) => { node.focus(); return; } - node = node.parentNode; + node = node.parentElement; } }; window.addEventListener('mousemove', (e) => { - const node = e.target; + const node = e.target as HTMLElement; if (node !== lastVisitedNode) { lastVisitedNode = node; focusNearestTrackedParent(node); @@ -117,10 +116,10 @@ window.addEventListener('mousemove', (e) => { window.addEventListener('focusin', (e) => { lastVisitedNode = null; - focusedNode = e.target; + focusedNode = e.target as HTMLElement; setWindowFocus(true); - if (canStealFocus(e.target)) { - stealFocus(e.target); + if (canStealFocus(e.target as HTMLElement)) { + stealFocus(e.target as HTMLElement); return; } }); @@ -142,13 +141,22 @@ window.addEventListener('beforeunload', (e) => { // Key events // -------------------------------------------------------- -const keyHeldByCode = {}; +const keyHeldByCode: Record = {}; export class KeyEvent { - constructor(e, type, repeat) { + event: KeyboardEvent; + type: 'keydown' | 'keyup'; + code: number; + ctrl: boolean; + shift: boolean; + alt: boolean; + repeat: boolean; + _str?: string; + + constructor(e: KeyboardEvent, type: 'keydown' | 'keyup', repeat?: boolean) { this.event = e; this.type = type; - this.code = window.event ? e.which : e.keyCode; + this.code = e.keyCode; this.ctrl = e.ctrlKey; this.shift = e.shiftKey; this.alt = e.altKey; @@ -198,7 +206,7 @@ export class KeyEvent { // IE8: Keydown event is only available on document. document.addEventListener('keydown', (e) => { - if (canStealFocus(e.target)) { + if (canStealFocus(e.target as HTMLElement)) { return; } const code = e.keyCode; @@ -209,7 +217,7 @@ document.addEventListener('keydown', (e) => { }); document.addEventListener('keyup', (e) => { - if (canStealFocus(e.target)) { + if (canStealFocus(e.target as HTMLElement)) { return; } const code = e.keyCode; diff --git a/tgui/packages/tgui/focus.js b/tgui/packages/tgui/focus.ts similarity index 100% rename from tgui/packages/tgui/focus.js rename to tgui/packages/tgui/focus.ts diff --git a/tgui/packages/tgui/format.js b/tgui/packages/tgui/format.js deleted file mode 100644 index c261a3dfdbd96..0000000000000 --- a/tgui/packages/tgui/format.js +++ /dev/null @@ -1,98 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -import { clamp, round, toFixed } from 'common/math'; - -const SI_SYMBOLS = [ - 'f', // femto - 'p', // pico - 'n', // nano - 'μ', // micro - 'm', // milli - // NOTE: This is a space for a reason. When we right align si numbers, - // in monospace mode, we want to units and numbers stay in their respective - // columns. If rendering in HTML mode, this space will collapse into - // a single space anyway. - ' ', - 'k', // kilo - 'M', // mega - 'G', // giga - 'T', // tera - 'P', // peta - 'E', // exa - 'Z', // zetta - 'Y', // yotta -]; - -const SI_BASE_INDEX = SI_SYMBOLS.indexOf(' '); - -/** - * Formats a number to a human readable form, by reducing it to SI units. - * TODO: This is quite a shit code and shit math, needs optimization. - */ -export const formatSiUnit = (value, minBase1000 = -SI_BASE_INDEX, unit = '') => { - if (typeof value !== 'number' || !Number.isFinite(value)) { - return value; - } - const realBase10 = Math.floor(Math.log10(value)); - const base10 = Math.floor(Math.max(minBase1000 * 3, realBase10)); - const realBase1000 = Math.floor(realBase10 / 3); - const base1000 = Math.floor(base10 / 3); - const symbolIndex = clamp(SI_BASE_INDEX + base1000, 0, SI_SYMBOLS.length); - const symbol = SI_SYMBOLS[symbolIndex]; - const scaledNumber = value / Math.pow(1000, base1000); - const scaledPrecision = realBase1000 > minBase1000 ? 2 + base1000 * 3 - base10 : 0; - // TODO: Make numbers bigger than precision value show - // up to 2 decimal numbers. - const finalString = toFixed(scaledNumber, scaledPrecision) + ' ' + symbol + unit; - return finalString.trim(); -}; - -export const formatPower = (value, minBase1000 = 0) => { - return formatSiUnit(value, minBase1000, 'W'); -}; - -export const formatMoney = (value, precision = 0) => { - if (!Number.isFinite(value)) { - return value; - } - // Round the number and make it fixed precision - let fixed = round(value, precision); - if (precision > 0) { - fixed = toFixed(value, precision); - } - fixed = String(fixed); - // Place thousand separators - const length = fixed.length; - let indexOfPoint = fixed.indexOf('.'); - if (indexOfPoint === -1) { - indexOfPoint = length; - } - let result = ''; - for (let i = 0; i < length; i++) { - if (i > 0 && i < indexOfPoint && (indexOfPoint - i) % 3 === 0) { - // Thin space - result += '\u2009'; - } - result += fixed.charAt(i); - } - return result; -}; - -/** - * Formats a floating point number as a number on the decibel scale. - */ -export const formatDb = (value) => { - const db = (20 * Math.log(value)) / Math.log(10); - const sign = db >= 0 ? '+' : '–'; - let formatted = Math.abs(db); - if (formatted === Infinity) { - formatted = 'Inf'; - } else { - formatted = toFixed(formatted, 2); - } - return sign + formatted + ' dB'; -}; diff --git a/tgui/packages/tgui/format.test.ts b/tgui/packages/tgui/format.test.ts new file mode 100644 index 0000000000000..011d9e9e6c07a --- /dev/null +++ b/tgui/packages/tgui/format.test.ts @@ -0,0 +1,112 @@ +import { formatDb, formatMoney, formatSiBaseTenUnit, formatSiUnit, formatTime } from './format'; + +describe('formatSiUnit', () => { + it('formats base values correctly', () => { + const value = 100; + const result = formatSiUnit(value); + expect(result).toBe('100'); + }); + + it('formats kilo values correctly', () => { + const value = 1500; + const result = formatSiUnit(value); + expect(result).toBe('1.50 k'); + }); + + it('formats micro values correctly', () => { + const value = 0.0001; + const result = formatSiUnit(value); + expect(result).toBe('100 μ'); + }); + + it('formats values with custom units correctly', () => { + const value = 0.5; + const result = formatSiUnit(value, 0, 'Hz'); + expect(result).toBe('0.50 Hz'); + }); + + it('handles non-finite values correctly', () => { + const value = Infinity; + const result = formatSiUnit(value); + expect(result).toBe('Infinity'); + }); +}); + +describe('formatMoney', () => { + it('formats integer values with default precision', () => { + const value = 1234567; + const result = formatMoney(value); + expect(result).toBe('1\u2009234\u2009567'); + }); + + it('formats float values with specified precision', () => { + const value = 1234567.89; + const result = formatMoney(value, 2); + expect(result).toBe('1\u2009234\u2009567.89'); + }); + + it('formats negative values correctly', () => { + const value = -1234567.89; + const result = formatMoney(value, 2); + expect(result).toBe('-1\u2009234\u2009567.89'); + }); + + it('returns non-finite values as is', () => { + const value = Infinity; + const result = formatMoney(value); + expect(result).toBe('Infinity'); + }); + + it('formats zero correctly', () => { + const value = 0; + const result = formatMoney(value); + expect(result).toBe('0'); + }); +}); + +describe('formatDb', () => { + it('formats positive values correctly', () => { + const value = 1; + const result = formatDb(value); + expect(result).toBe('+0.00 dB'); + }); + + it('formats negative values correctly', () => { + const value = 0.5; + const result = formatDb(value); + expect(result).toBe('-6.02 dB'); + }); + + it('formats Infinity correctly', () => { + const value = 0; + const result = formatDb(value); + expect(result).toBe('-Inf dB'); + }); + + it('formats very large values correctly', () => { + const value = 1e6; + const result = formatDb(value); + expect(result).toBe('+120.00 dB'); + }); + + it('formats very small values correctly', () => { + const value = 1e-6; + const result = formatDb(value); + expect(result).toBe('-120.00 dB'); + }); +}); + +describe('formatSiBaseTenUnit', () => { + it('formats SI base 10 units', () => { + expect(formatSiBaseTenUnit(1e9)).toBe('1.00 · 10⁹'); + expect(formatSiBaseTenUnit(1234567890, 0, 'm')).toBe('1.23 · 10⁹ m'); + }); +}); + +describe('formatTime', () => { + it('formats time values', () => { + expect(formatTime(36000)).toBe('01:00:00'); + expect(formatTime(36610)).toBe('01:01:01'); + expect(formatTime(36610, 'short')).toBe('1h1m1s'); + }); +}); diff --git a/tgui/packages/tgui/format.ts b/tgui/packages/tgui/format.ts new file mode 100644 index 0000000000000..c48a8baacfe42 --- /dev/null +++ b/tgui/packages/tgui/format.ts @@ -0,0 +1,157 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +const SI_SYMBOLS = [ + 'f', // femto + 'p', // pico + 'n', // nano + 'μ', // micro + 'm', // milli + // NOTE: This is a space for a reason. When we right align si numbers, + // in monospace mode, we want to units and numbers stay in their respective + // columns. If rendering in HTML mode, this space will collapse into + // a single space anyway. + ' ', // base + 'k', // kilo + 'M', // mega + 'G', // giga + 'T', // tera + 'P', // peta + 'E', // exa + 'Z', // zetta + 'Y', // yotta + 'R', // ronna + 'Q', // quecca + 'F', + 'N', + 'H', +] as const; + +const SI_BASE_INDEX = SI_SYMBOLS.indexOf(' '); + +// Formats a number to a human readable form, with a custom unit +export const formatSiUnit = (value: number, minBase1000 = -SI_BASE_INDEX, unit = ''): string => { + if (!isFinite(value)) { + return value.toString(); + } + + const realBase10 = Math.floor(Math.log10(Math.abs(value))); + const base10 = Math.max(minBase1000 * 3, realBase10); + const base1000 = Math.floor(base10 / 3); + const symbol = SI_SYMBOLS[Math.min(base1000 + SI_BASE_INDEX, SI_SYMBOLS.length - 1)]; + + const scaledValue = value / Math.pow(1000, base1000); + + let formattedValue = scaledValue.toFixed(2); + if (formattedValue.endsWith('.00')) { + formattedValue = formattedValue.slice(0, -3); + } else if (formattedValue.endsWith('.0')) { + formattedValue = formattedValue.slice(0, -2); + } + + return `${formattedValue} ${symbol.trim()}${unit}`.trim(); +}; + +// Formats a number to a human readable form, with power (W) as the unit +export const formatPower = (value: number, minBase1000 = 0) => { + return formatSiUnit(value, minBase1000, 'W'); +}; + +// Formats a number as a currency string +export const formatMoney = (value: number, precision = 0) => { + if (!Number.isFinite(value)) { + return String(value); + } + + // Round the number and make it fixed precision + const roundedValue = Number(value.toFixed(precision)); + + // Handle the negative sign + const isNegative = roundedValue < 0; + const absoluteValue = Math.abs(roundedValue); + + // Convert to string and place thousand separators + const parts = absoluteValue.toString().split('.'); + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '\u2009'); // Thin space + + const formattedValue = parts.join('.'); + + return isNegative ? `-${formattedValue}` : formattedValue; +}; + +// Formats a floating point number as a number on the decibel scale +export const formatDb = (value: number) => { + const db = 20 * Math.log10(value); + const sign = db >= 0 ? '+' : '-'; + let formatted: string | number = Math.abs(db); + + if (formatted === Infinity) { + formatted = 'Inf'; + } else { + formatted = formatted.toFixed(2); + } + + return `${sign}${formatted} dB`; +}; + +const SI_BASE_TEN_UNITS = [ + '', + '· 10³', // kilo + '· 10⁶', // mega + '· 10⁹', // giga + '· 10¹²', // tera + '· 10¹⁵', // peta + '· 10¹⁸', // exa + '· 10²¹', // zetta + '· 10²⁴', // yotta + '· 10²⁷', // ronna + '· 10³⁰', // quecca + '· 10³³', + '· 10³⁶', + '· 10³⁹', +] as const; + +// Converts a number to a string with SI base 10 units +export const formatSiBaseTenUnit = (value: number, minBase1000 = 0, unit = ''): string => { + if (!isFinite(value)) { + return 'NaN'; + } + + const realBase10 = Math.floor(Math.log10(value)); + const base10 = Math.max(minBase1000 * 3, realBase10); + const base1000 = Math.floor(base10 / 3); + const symbol = SI_BASE_TEN_UNITS[base1000]; + + const scaledValue = value / Math.pow(1000, base1000); + const precision = Math.max(0, 2 - (base10 % 3)); + const formattedValue = scaledValue.toFixed(precision); + + return `${formattedValue} ${symbol} ${unit}`.trim(); +}; + +/** + * Formats decisecond count into HH:MM:SS display by default + * "short" format does not pad and adds hms suffixes + */ +export const formatTime = (val: number, formatType: 'short' | 'default' = 'default'): string => { + const totalSeconds = Math.floor(val / 10); + const hours = Math.floor(totalSeconds / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const seconds = totalSeconds % 60; + + if (formatType === 'short') { + const hoursFormatted = hours > 0 ? `${hours}h` : ''; + const minutesFormatted = minutes > 0 ? `${minutes}m` : ''; + const secondsFormatted = seconds > 0 ? `${seconds}s` : ''; + return `${hoursFormatted}${minutesFormatted}${secondsFormatted}`; + } + + const hoursPadded = String(hours).padStart(2, '0'); + const minutesPadded = String(minutes).padStart(2, '0'); + const secondsPadded = String(seconds).padStart(2, '0'); + + return `${hoursPadded}:${minutesPadded}:${secondsPadded}`; +}; diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.tsx similarity index 98% rename from tgui/packages/tgui/index.js rename to tgui/packages/tgui/index.tsx index 7554cac0eccf3..2f0a7e9aaa796 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.tsx @@ -43,13 +43,14 @@ import './styles/themes/retro.scss'; import './styles/themes/syndicate.scss'; import './styles/themes/thinktronic-classic.scss'; -import { perf } from 'common/perf'; -import { setupHotReloading } from 'tgui-dev-server/link/client.cjs'; -import { setupHotKeys } from './hotkeys'; +import { StoreProvider, configureStore } from './store'; + import { captureExternalLinks } from './links'; import { createRenderer } from './renderer'; -import { configureStore, StoreProvider } from './store'; +import { perf } from 'common/perf'; import { setupGlobalEvents } from './events'; +import { setupHotKeys } from './hotkeys'; +import { setupHotReloading } from 'tgui-dev-server/link/client.cjs'; perf.mark('inception', window.performance?.timing?.navigationStart); perf.mark('init'); diff --git a/tgui/packages/tgui/interfaces/AbductorConsole.js b/tgui/packages/tgui/interfaces/AbductorConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AbductorConsole.js rename to tgui/packages/tgui/interfaces/AbductorConsole.jsx diff --git a/tgui/packages/tgui/interfaces/Achievements.js b/tgui/packages/tgui/interfaces/Achievements.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Achievements.js rename to tgui/packages/tgui/interfaces/Achievements.jsx diff --git a/tgui/packages/tgui/interfaces/AdminFax.js b/tgui/packages/tgui/interfaces/AdminFax.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AdminFax.js rename to tgui/packages/tgui/interfaces/AdminFax.jsx diff --git a/tgui/packages/tgui/interfaces/AdminSecretsPanel.js b/tgui/packages/tgui/interfaces/AdminSecretsPanel.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AdminSecretsPanel.js rename to tgui/packages/tgui/interfaces/AdminSecretsPanel.jsx diff --git a/tgui/packages/tgui/interfaces/AdvancedAirlockController.js b/tgui/packages/tgui/interfaces/AdvancedAirlockController.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AdvancedAirlockController.js rename to tgui/packages/tgui/interfaces/AdvancedAirlockController.jsx diff --git a/tgui/packages/tgui/interfaces/AiAirlock.js b/tgui/packages/tgui/interfaces/AiAirlock.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AiAirlock.js rename to tgui/packages/tgui/interfaces/AiAirlock.jsx diff --git a/tgui/packages/tgui/interfaces/AiRestorer.js b/tgui/packages/tgui/interfaces/AiRestorer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AiRestorer.js rename to tgui/packages/tgui/interfaces/AiRestorer.jsx diff --git a/tgui/packages/tgui/interfaces/AirAlarm.js b/tgui/packages/tgui/interfaces/AirAlarm.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AirAlarm.js rename to tgui/packages/tgui/interfaces/AirAlarm.jsx diff --git a/tgui/packages/tgui/interfaces/AirlockElectronics.js b/tgui/packages/tgui/interfaces/AirlockElectronics.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AirlockElectronics.js rename to tgui/packages/tgui/interfaces/AirlockElectronics.jsx diff --git a/tgui/packages/tgui/interfaces/Apc.js b/tgui/packages/tgui/interfaces/Apc.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Apc.js rename to tgui/packages/tgui/interfaces/Apc.jsx diff --git a/tgui/packages/tgui/interfaces/Aquarium.js b/tgui/packages/tgui/interfaces/Aquarium.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Aquarium.js rename to tgui/packages/tgui/interfaces/Aquarium.jsx diff --git a/tgui/packages/tgui/interfaces/AtmosAlertConsole.js b/tgui/packages/tgui/interfaces/AtmosAlertConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AtmosAlertConsole.js rename to tgui/packages/tgui/interfaces/AtmosAlertConsole.jsx diff --git a/tgui/packages/tgui/interfaces/AtmosControlConsole.js b/tgui/packages/tgui/interfaces/AtmosControlConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AtmosControlConsole.js rename to tgui/packages/tgui/interfaces/AtmosControlConsole.jsx diff --git a/tgui/packages/tgui/interfaces/AtmosFilter.js b/tgui/packages/tgui/interfaces/AtmosFilter.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AtmosFilter.js rename to tgui/packages/tgui/interfaces/AtmosFilter.jsx index 323e90f200432..cf08df84bd5be 100644 --- a/tgui/packages/tgui/interfaces/AtmosFilter.js +++ b/tgui/packages/tgui/interfaces/AtmosFilter.jsx @@ -1,7 +1,7 @@ import { useBackend } from '../backend'; import { Button, LabeledList, NumberInput, Section } from '../components'; -import { getGasLabel } from '../constants'; import { Window } from '../layouts'; +import { getGasLabel } from '../constants'; export const AtmosFilter = (props, context) => { const { act, data } = useBackend(context); diff --git a/tgui/packages/tgui/interfaces/AtmosMixer.js b/tgui/packages/tgui/interfaces/AtmosMixer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AtmosMixer.js rename to tgui/packages/tgui/interfaces/AtmosMixer.jsx diff --git a/tgui/packages/tgui/interfaces/AtmosPump.js b/tgui/packages/tgui/interfaces/AtmosPump.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AtmosPump.js rename to tgui/packages/tgui/interfaces/AtmosPump.jsx diff --git a/tgui/packages/tgui/interfaces/AtmosTempGate.js b/tgui/packages/tgui/interfaces/AtmosTempGate.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AtmosTempGate.js rename to tgui/packages/tgui/interfaces/AtmosTempGate.jsx diff --git a/tgui/packages/tgui/interfaces/AtmosTempPump.js b/tgui/packages/tgui/interfaces/AtmosTempPump.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AtmosTempPump.js rename to tgui/packages/tgui/interfaces/AtmosTempPump.jsx diff --git a/tgui/packages/tgui/interfaces/AutomatedAnnouncement.js b/tgui/packages/tgui/interfaces/AutomatedAnnouncement.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/AutomatedAnnouncement.js rename to tgui/packages/tgui/interfaces/AutomatedAnnouncement.jsx diff --git a/tgui/packages/tgui/interfaces/BankMachine.js b/tgui/packages/tgui/interfaces/BankMachine.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/BankMachine.js rename to tgui/packages/tgui/interfaces/BankMachine.jsx diff --git a/tgui/packages/tgui/interfaces/BanningPanel.js b/tgui/packages/tgui/interfaces/BanningPanel.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/BanningPanel.js rename to tgui/packages/tgui/interfaces/BanningPanel.jsx diff --git a/tgui/packages/tgui/interfaces/Biogenerator.js b/tgui/packages/tgui/interfaces/Biogenerator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Biogenerator.js rename to tgui/packages/tgui/interfaces/Biogenerator.jsx diff --git a/tgui/packages/tgui/interfaces/BluespaceArtillery.js b/tgui/packages/tgui/interfaces/BluespaceArtillery.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/BluespaceArtillery.js rename to tgui/packages/tgui/interfaces/BluespaceArtillery.jsx diff --git a/tgui/packages/tgui/interfaces/BluespaceLocator.js b/tgui/packages/tgui/interfaces/BluespaceLocator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/BluespaceLocator.js rename to tgui/packages/tgui/interfaces/BluespaceLocator.jsx diff --git a/tgui/packages/tgui/interfaces/BluespaceTap.js b/tgui/packages/tgui/interfaces/BluespaceTap.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/BluespaceTap.js rename to tgui/packages/tgui/interfaces/BluespaceTap.jsx diff --git a/tgui/packages/tgui/interfaces/BorgPanel.js b/tgui/packages/tgui/interfaces/BorgPanel.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/BorgPanel.js rename to tgui/packages/tgui/interfaces/BorgPanel.jsx diff --git a/tgui/packages/tgui/interfaces/BountyBoard.js b/tgui/packages/tgui/interfaces/BountyBoard.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/BountyBoard.js rename to tgui/packages/tgui/interfaces/BountyBoard.jsx diff --git a/tgui/packages/tgui/interfaces/BrigTimer.js b/tgui/packages/tgui/interfaces/BrigTimer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/BrigTimer.js rename to tgui/packages/tgui/interfaces/BrigTimer.jsx diff --git a/tgui/packages/tgui/interfaces/CameraConsole.js b/tgui/packages/tgui/interfaces/CameraConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/CameraConsole.js rename to tgui/packages/tgui/interfaces/CameraConsole.jsx diff --git a/tgui/packages/tgui/interfaces/Canister.js b/tgui/packages/tgui/interfaces/Canister.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Canister.js rename to tgui/packages/tgui/interfaces/Canister.jsx diff --git a/tgui/packages/tgui/interfaces/Canvas.js b/tgui/packages/tgui/interfaces/Canvas.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Canvas.js rename to tgui/packages/tgui/interfaces/Canvas.jsx diff --git a/tgui/packages/tgui/interfaces/Cargo.js b/tgui/packages/tgui/interfaces/Cargo.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Cargo.js rename to tgui/packages/tgui/interfaces/Cargo.jsx diff --git a/tgui/packages/tgui/interfaces/CargoBountyConsole.js b/tgui/packages/tgui/interfaces/CargoBountyConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/CargoBountyConsole.js rename to tgui/packages/tgui/interfaces/CargoBountyConsole.jsx diff --git a/tgui/packages/tgui/interfaces/CargoExpress.js b/tgui/packages/tgui/interfaces/CargoExpress.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/CargoExpress.js rename to tgui/packages/tgui/interfaces/CargoExpress.jsx diff --git a/tgui/packages/tgui/interfaces/CargoHoldTerminal.js b/tgui/packages/tgui/interfaces/CargoHoldTerminal.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/CargoHoldTerminal.js rename to tgui/packages/tgui/interfaces/CargoHoldTerminal.jsx diff --git a/tgui/packages/tgui/interfaces/CellularEmporium.js b/tgui/packages/tgui/interfaces/CellularEmporium.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/CellularEmporium.js rename to tgui/packages/tgui/interfaces/CellularEmporium.jsx diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher.js b/tgui/packages/tgui/interfaces/CentcomPodLauncher.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/CentcomPodLauncher.js rename to tgui/packages/tgui/interfaces/CentcomPodLauncher.jsx diff --git a/tgui/packages/tgui/interfaces/ChemAcclimator.js b/tgui/packages/tgui/interfaces/ChemAcclimator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ChemAcclimator.js rename to tgui/packages/tgui/interfaces/ChemAcclimator.jsx diff --git a/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.js b/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ChemDebugSynthesizer.js rename to tgui/packages/tgui/interfaces/ChemDebugSynthesizer.jsx diff --git a/tgui/packages/tgui/interfaces/ChemDispenser.js b/tgui/packages/tgui/interfaces/ChemDispenser.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ChemDispenser.js rename to tgui/packages/tgui/interfaces/ChemDispenser.jsx diff --git a/tgui/packages/tgui/interfaces/ChemFilter.js b/tgui/packages/tgui/interfaces/ChemFilter.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ChemFilter.js rename to tgui/packages/tgui/interfaces/ChemFilter.jsx diff --git a/tgui/packages/tgui/interfaces/ChemHeater.js b/tgui/packages/tgui/interfaces/ChemHeater.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ChemHeater.js rename to tgui/packages/tgui/interfaces/ChemHeater.jsx diff --git a/tgui/packages/tgui/interfaces/ChemMaster.js b/tgui/packages/tgui/interfaces/ChemMaster.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ChemMaster.js rename to tgui/packages/tgui/interfaces/ChemMaster.jsx diff --git a/tgui/packages/tgui/interfaces/ChemReactionChamber.js b/tgui/packages/tgui/interfaces/ChemReactionChamber.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ChemReactionChamber.js rename to tgui/packages/tgui/interfaces/ChemReactionChamber.jsx diff --git a/tgui/packages/tgui/interfaces/ChemSplitter.js b/tgui/packages/tgui/interfaces/ChemSplitter.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ChemSplitter.js rename to tgui/packages/tgui/interfaces/ChemSplitter.jsx diff --git a/tgui/packages/tgui/interfaces/ChemSynthesizer.js b/tgui/packages/tgui/interfaces/ChemSynthesizer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ChemSynthesizer.js rename to tgui/packages/tgui/interfaces/ChemSynthesizer.jsx diff --git a/tgui/packages/tgui/interfaces/CircuitModule.js b/tgui/packages/tgui/interfaces/CircuitModule.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/CircuitModule.js rename to tgui/packages/tgui/interfaces/CircuitModule.jsx diff --git a/tgui/packages/tgui/interfaces/Clipboard.js b/tgui/packages/tgui/interfaces/Clipboard.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Clipboard.js rename to tgui/packages/tgui/interfaces/Clipboard.jsx diff --git a/tgui/packages/tgui/interfaces/ClockworkSlab.js b/tgui/packages/tgui/interfaces/ClockworkSlab.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ClockworkSlab.js rename to tgui/packages/tgui/interfaces/ClockworkSlab.jsx diff --git a/tgui/packages/tgui/interfaces/CloningConsole.js b/tgui/packages/tgui/interfaces/CloningConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/CloningConsole.js rename to tgui/packages/tgui/interfaces/CloningConsole.jsx diff --git a/tgui/packages/tgui/interfaces/CodexGigas.js b/tgui/packages/tgui/interfaces/CodexGigas.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/CodexGigas.js rename to tgui/packages/tgui/interfaces/CodexGigas.jsx diff --git a/tgui/packages/tgui/interfaces/ColorMatrixEditor.js b/tgui/packages/tgui/interfaces/ColorMatrixEditor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ColorMatrixEditor.js rename to tgui/packages/tgui/interfaces/ColorMatrixEditor.jsx diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole.js b/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/CommunicationsConsole.js rename to tgui/packages/tgui/interfaces/CommunicationsConsole.jsx diff --git a/tgui/packages/tgui/interfaces/ComputerFabricator.js b/tgui/packages/tgui/interfaces/ComputerFabricator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ComputerFabricator.js rename to tgui/packages/tgui/interfaces/ComputerFabricator.jsx diff --git a/tgui/packages/tgui/interfaces/Crayon.js b/tgui/packages/tgui/interfaces/Crayon.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Crayon.js rename to tgui/packages/tgui/interfaces/Crayon.jsx diff --git a/tgui/packages/tgui/interfaces/Cryo.js b/tgui/packages/tgui/interfaces/Cryo.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Cryo.js rename to tgui/packages/tgui/interfaces/Cryo.jsx diff --git a/tgui/packages/tgui/interfaces/DisposalUnit.js b/tgui/packages/tgui/interfaces/DisposalUnit.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/DisposalUnit.js rename to tgui/packages/tgui/interfaces/DisposalUnit.jsx diff --git a/tgui/packages/tgui/interfaces/DnaConsole.js b/tgui/packages/tgui/interfaces/DnaConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/DnaConsole.js rename to tgui/packages/tgui/interfaces/DnaConsole.jsx diff --git a/tgui/packages/tgui/interfaces/DnaVault.js b/tgui/packages/tgui/interfaces/DnaVault.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/DnaVault.js rename to tgui/packages/tgui/interfaces/DnaVault.jsx diff --git a/tgui/packages/tgui/interfaces/EightBallVote.js b/tgui/packages/tgui/interfaces/EightBallVote.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/EightBallVote.js rename to tgui/packages/tgui/interfaces/EightBallVote.jsx diff --git a/tgui/packages/tgui/interfaces/Electropack.js b/tgui/packages/tgui/interfaces/Electropack.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Electropack.js rename to tgui/packages/tgui/interfaces/Electropack.jsx diff --git a/tgui/packages/tgui/interfaces/Elevator.js b/tgui/packages/tgui/interfaces/Elevator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Elevator.js rename to tgui/packages/tgui/interfaces/Elevator.jsx diff --git a/tgui/packages/tgui/interfaces/EmagConsole.js b/tgui/packages/tgui/interfaces/EmagConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/EmagConsole.js rename to tgui/packages/tgui/interfaces/EmagConsole.jsx diff --git a/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.js b/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/EmergencyShuttleConsole.js rename to tgui/packages/tgui/interfaces/EmergencyShuttleConsole.jsx diff --git a/tgui/packages/tgui/interfaces/EmojiInputModal.js b/tgui/packages/tgui/interfaces/EmojiInputModal.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/EmojiInputModal.js rename to tgui/packages/tgui/interfaces/EmojiInputModal.jsx diff --git a/tgui/packages/tgui/interfaces/EngravedMessage.js b/tgui/packages/tgui/interfaces/EngravedMessage.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/EngravedMessage.js rename to tgui/packages/tgui/interfaces/EngravedMessage.jsx diff --git a/tgui/packages/tgui/interfaces/ExosuitControlConsole.js b/tgui/packages/tgui/interfaces/ExosuitControlConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ExosuitControlConsole.js rename to tgui/packages/tgui/interfaces/ExosuitControlConsole.jsx diff --git a/tgui/packages/tgui/interfaces/Filteriffic.js b/tgui/packages/tgui/interfaces/Filteriffic.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Filteriffic.js rename to tgui/packages/tgui/interfaces/Filteriffic.jsx diff --git a/tgui/packages/tgui/interfaces/FishCatalog.js b/tgui/packages/tgui/interfaces/FishCatalog.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/FishCatalog.js rename to tgui/packages/tgui/interfaces/FishCatalog.jsx diff --git a/tgui/packages/tgui/interfaces/Folder.js b/tgui/packages/tgui/interfaces/Folder.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Folder.js rename to tgui/packages/tgui/interfaces/Folder.jsx diff --git a/tgui/packages/tgui/interfaces/ForbiddenLore.js b/tgui/packages/tgui/interfaces/ForbiddenLore.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ForbiddenLore.js rename to tgui/packages/tgui/interfaces/ForbiddenLore.jsx diff --git a/tgui/packages/tgui/interfaces/FugitiveCaptureConsole.js b/tgui/packages/tgui/interfaces/FugitiveCaptureConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/FugitiveCaptureConsole.js rename to tgui/packages/tgui/interfaces/FugitiveCaptureConsole.jsx diff --git a/tgui/packages/tgui/interfaces/GenPop.js b/tgui/packages/tgui/interfaces/GenPop.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/GenPop.js rename to tgui/packages/tgui/interfaces/GenPop.jsx diff --git a/tgui/packages/tgui/interfaces/GhostPoolProtection.js b/tgui/packages/tgui/interfaces/GhostPoolProtection.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/GhostPoolProtection.js rename to tgui/packages/tgui/interfaces/GhostPoolProtection.jsx diff --git a/tgui/packages/tgui/interfaces/GlandDispenser.js b/tgui/packages/tgui/interfaces/GlandDispenser.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/GlandDispenser.js rename to tgui/packages/tgui/interfaces/GlandDispenser.jsx diff --git a/tgui/packages/tgui/interfaces/Gps.js b/tgui/packages/tgui/interfaces/Gps.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Gps.js rename to tgui/packages/tgui/interfaces/Gps.jsx diff --git a/tgui/packages/tgui/interfaces/GravityGenerator.js b/tgui/packages/tgui/interfaces/GravityGenerator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/GravityGenerator.js rename to tgui/packages/tgui/interfaces/GravityGenerator.jsx diff --git a/tgui/packages/tgui/interfaces/GulagItemReclaimer.js b/tgui/packages/tgui/interfaces/GulagItemReclaimer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/GulagItemReclaimer.js rename to tgui/packages/tgui/interfaces/GulagItemReclaimer.jsx diff --git a/tgui/packages/tgui/interfaces/GulagTeleporterConsole.js b/tgui/packages/tgui/interfaces/GulagTeleporterConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/GulagTeleporterConsole.js rename to tgui/packages/tgui/interfaces/GulagTeleporterConsole.jsx diff --git a/tgui/packages/tgui/interfaces/Holodeck.js b/tgui/packages/tgui/interfaces/Holodeck.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Holodeck.js rename to tgui/packages/tgui/interfaces/Holodeck.jsx diff --git a/tgui/packages/tgui/interfaces/HypnoChair.js b/tgui/packages/tgui/interfaces/HypnoChair.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/HypnoChair.js rename to tgui/packages/tgui/interfaces/HypnoChair.jsx diff --git a/tgui/packages/tgui/interfaces/ImplantChair.js b/tgui/packages/tgui/interfaces/ImplantChair.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ImplantChair.js rename to tgui/packages/tgui/interfaces/ImplantChair.jsx diff --git a/tgui/packages/tgui/interfaces/InfraredEmitter.js b/tgui/packages/tgui/interfaces/InfraredEmitter.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/InfraredEmitter.js rename to tgui/packages/tgui/interfaces/InfraredEmitter.jsx diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/BasicInput.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/BasicInput.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/IntegratedCircuit/BasicInput.js rename to tgui/packages/tgui/interfaces/IntegratedCircuit/BasicInput.jsx diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.js rename to tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.jsx diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/Connections.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/Connections.jsx similarity index 96% rename from tgui/packages/tgui/interfaces/IntegratedCircuit/Connections.js rename to tgui/packages/tgui/interfaces/IntegratedCircuit/Connections.jsx index 02cd6f4fc9094..6a59708121341 100644 --- a/tgui/packages/tgui/interfaces/IntegratedCircuit/Connections.js +++ b/tgui/packages/tgui/interfaces/IntegratedCircuit/Connections.jsx @@ -1,6 +1,6 @@ import { CSS_COLORS } from '../../constants'; import { SVG_CURVE_INTENSITY } from './constants'; -import { classes } from '../../../common/react'; +import { classes } from 'common/react'; export const Connections = (props, context) => { const { connections } = props; diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayName.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayName.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayName.js rename to tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayName.jsx diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.js rename to tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.jsx diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.jsx similarity index 97% rename from tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.js rename to tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.jsx index 4c435a0bcbed9..39f7b25184dbf 100644 --- a/tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.js +++ b/tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.jsx @@ -1,7 +1,7 @@ import { useBackend } from '../../backend'; -import { Box, Stack, Button, Dropdown } from '../../components'; +import { Box, Stack, Button } from '../../components'; import { Component } from 'inferno'; -import { shallowDiffers } from '../../../common/react'; +import { shallowDiffers } from 'common/react'; import { ABSOLUTE_Y_OFFSET } from './constants'; import { Port } from './Port'; diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/Port.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/Port.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/IntegratedCircuit/Port.js rename to tgui/packages/tgui/interfaces/IntegratedCircuit/Port.jsx diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.js rename to tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.jsx diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/constants.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/constants.ts similarity index 100% rename from tgui/packages/tgui/interfaces/IntegratedCircuit/constants.js rename to tgui/packages/tgui/interfaces/IntegratedCircuit/constants.ts diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/index.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/index.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/IntegratedCircuit/index.js rename to tgui/packages/tgui/interfaces/IntegratedCircuit/index.jsx diff --git a/tgui/packages/tgui/interfaces/Intellicard.js b/tgui/packages/tgui/interfaces/Intellicard.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Intellicard.js rename to tgui/packages/tgui/interfaces/Intellicard.jsx diff --git a/tgui/packages/tgui/interfaces/Interview.js b/tgui/packages/tgui/interfaces/Interview.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Interview.js rename to tgui/packages/tgui/interfaces/Interview.jsx diff --git a/tgui/packages/tgui/interfaces/InterviewManager.js b/tgui/packages/tgui/interfaces/InterviewManager.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/InterviewManager.js rename to tgui/packages/tgui/interfaces/InterviewManager.jsx diff --git a/tgui/packages/tgui/interfaces/Jukebox.js b/tgui/packages/tgui/interfaces/Jukebox.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Jukebox.js rename to tgui/packages/tgui/interfaces/Jukebox.jsx diff --git a/tgui/packages/tgui/interfaces/KeycardAuth.js b/tgui/packages/tgui/interfaces/KeycardAuth.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/KeycardAuth.js rename to tgui/packages/tgui/interfaces/KeycardAuth.jsx diff --git a/tgui/packages/tgui/interfaces/LaborClaimConsole.js b/tgui/packages/tgui/interfaces/LaborClaimConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/LaborClaimConsole.js rename to tgui/packages/tgui/interfaces/LaborClaimConsole.jsx diff --git a/tgui/packages/tgui/interfaces/LanguageMenu.js b/tgui/packages/tgui/interfaces/LanguageMenu.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/LanguageMenu.js rename to tgui/packages/tgui/interfaces/LanguageMenu.jsx diff --git a/tgui/packages/tgui/interfaces/LaunchpadConsole.js b/tgui/packages/tgui/interfaces/LaunchpadConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/LaunchpadConsole.js rename to tgui/packages/tgui/interfaces/LaunchpadConsole.jsx diff --git a/tgui/packages/tgui/interfaces/LaunchpadRemote.js b/tgui/packages/tgui/interfaces/LaunchpadRemote.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/LaunchpadRemote.js rename to tgui/packages/tgui/interfaces/LaunchpadRemote.jsx diff --git a/tgui/packages/tgui/interfaces/ListInputModal.tsx b/tgui/packages/tgui/interfaces/ListInputModal.tsx index 77666ec984e48..052aa8ee717f7 100644 --- a/tgui/packages/tgui/interfaces/ListInputModal.tsx +++ b/tgui/packages/tgui/interfaces/ListInputModal.tsx @@ -2,8 +2,8 @@ import { Loader } from './common/Loader'; import { InputButtons } from './common/InputButtons'; import { Button, Input, Section, Stack } from '../components'; import { useBackend, useLocalState } from '../backend'; -import { decodeHtmlEntities } from '../../common/string'; -import { KEY_A, KEY_DOWN, KEY_ESCAPE, KEY_ENTER, KEY_UP, KEY_Z } from '../../common/keycodes'; +import { capitalizeFirst, decodeHtmlEntities } from 'common/string'; +import { KEY_A, KEY_DOWN, KEY_ESCAPE, KEY_ENTER, KEY_UP, KEY_Z } from 'common/keycodes'; import { Window } from '../layouts'; type ListInputData = { @@ -187,7 +187,7 @@ const ListDisplay = (props, context) => { 'animation': 'none', 'transition': 'none', }}> - {item.replace(/^\w/, (c) => c.toUpperCase())} + {capitalizeFirst(item)} ); })} diff --git a/tgui/packages/tgui/interfaces/MechBayPowerConsole.js b/tgui/packages/tgui/interfaces/MechBayPowerConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/MechBayPowerConsole.js rename to tgui/packages/tgui/interfaces/MechBayPowerConsole.jsx diff --git a/tgui/packages/tgui/interfaces/MessageMonitor.js b/tgui/packages/tgui/interfaces/MessageMonitor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/MessageMonitor.js rename to tgui/packages/tgui/interfaces/MessageMonitor.jsx diff --git a/tgui/packages/tgui/interfaces/MiningVendor.js b/tgui/packages/tgui/interfaces/MiningVendor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/MiningVendor.js rename to tgui/packages/tgui/interfaces/MiningVendor.jsx diff --git a/tgui/packages/tgui/interfaces/Mint.js b/tgui/packages/tgui/interfaces/Mint.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Mint.js rename to tgui/packages/tgui/interfaces/Mint.jsx diff --git a/tgui/packages/tgui/interfaces/ModularFabricator.js b/tgui/packages/tgui/interfaces/ModularFabricator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ModularFabricator.js rename to tgui/packages/tgui/interfaces/ModularFabricator.jsx diff --git a/tgui/packages/tgui/interfaces/Morph.js b/tgui/packages/tgui/interfaces/Morph.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Morph.js rename to tgui/packages/tgui/interfaces/Morph.jsx diff --git a/tgui/packages/tgui/interfaces/Mule.js b/tgui/packages/tgui/interfaces/Mule.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Mule.js rename to tgui/packages/tgui/interfaces/Mule.jsx diff --git a/tgui/packages/tgui/interfaces/NaniteChamberControl.js b/tgui/packages/tgui/interfaces/NaniteChamberControl.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NaniteChamberControl.js rename to tgui/packages/tgui/interfaces/NaniteChamberControl.jsx diff --git a/tgui/packages/tgui/interfaces/NaniteCloudControl.js b/tgui/packages/tgui/interfaces/NaniteCloudControl.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NaniteCloudControl.js rename to tgui/packages/tgui/interfaces/NaniteCloudControl.jsx diff --git a/tgui/packages/tgui/interfaces/NaniteProgramHub.js b/tgui/packages/tgui/interfaces/NaniteProgramHub.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NaniteProgramHub.js rename to tgui/packages/tgui/interfaces/NaniteProgramHub.jsx diff --git a/tgui/packages/tgui/interfaces/NaniteProgrammer.js b/tgui/packages/tgui/interfaces/NaniteProgrammer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NaniteProgrammer.js rename to tgui/packages/tgui/interfaces/NaniteProgrammer.jsx diff --git a/tgui/packages/tgui/interfaces/NaniteRemote.js b/tgui/packages/tgui/interfaces/NaniteRemote.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NaniteRemote.js rename to tgui/packages/tgui/interfaces/NaniteRemote.jsx diff --git a/tgui/packages/tgui/interfaces/Newscaster.js b/tgui/packages/tgui/interfaces/Newscaster.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Newscaster.js rename to tgui/packages/tgui/interfaces/Newscaster.jsx diff --git a/tgui/packages/tgui/interfaces/NoticeBoard.js b/tgui/packages/tgui/interfaces/NoticeBoard.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NoticeBoard.js rename to tgui/packages/tgui/interfaces/NoticeBoard.jsx diff --git a/tgui/packages/tgui/interfaces/NtnetRelay.js b/tgui/packages/tgui/interfaces/NtnetRelay.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtnetRelay.js rename to tgui/packages/tgui/interfaces/NtnetRelay.jsx diff --git a/tgui/packages/tgui/interfaces/NtosAiRestorer.js b/tgui/packages/tgui/interfaces/NtosAiRestorer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosAiRestorer.js rename to tgui/packages/tgui/interfaces/NtosAiRestorer.jsx diff --git a/tgui/packages/tgui/interfaces/NtosAirlockControl.js b/tgui/packages/tgui/interfaces/NtosAirlockControl.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosAirlockControl.js rename to tgui/packages/tgui/interfaces/NtosAirlockControl.jsx diff --git a/tgui/packages/tgui/interfaces/NtosArcade.js b/tgui/packages/tgui/interfaces/NtosArcade.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosArcade.js rename to tgui/packages/tgui/interfaces/NtosArcade.jsx diff --git a/tgui/packages/tgui/interfaces/NtosAtmos.js b/tgui/packages/tgui/interfaces/NtosAtmos.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosAtmos.js rename to tgui/packages/tgui/interfaces/NtosAtmos.jsx diff --git a/tgui/packages/tgui/interfaces/NtosBountyBoard.js b/tgui/packages/tgui/interfaces/NtosBountyBoard.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosBountyBoard.js rename to tgui/packages/tgui/interfaces/NtosBountyBoard.jsx diff --git a/tgui/packages/tgui/interfaces/NtosBountyConsole.js b/tgui/packages/tgui/interfaces/NtosBountyConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosBountyConsole.js rename to tgui/packages/tgui/interfaces/NtosBountyConsole.jsx diff --git a/tgui/packages/tgui/interfaces/NtosCard.js b/tgui/packages/tgui/interfaces/NtosCard.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosCard.js rename to tgui/packages/tgui/interfaces/NtosCard.jsx diff --git a/tgui/packages/tgui/interfaces/NtosCargo.js b/tgui/packages/tgui/interfaces/NtosCargo.jsx similarity index 85% rename from tgui/packages/tgui/interfaces/NtosCargo.js rename to tgui/packages/tgui/interfaces/NtosCargo.jsx index b5009c0aeaf50..b478e42e5aecd 100644 --- a/tgui/packages/tgui/interfaces/NtosCargo.js +++ b/tgui/packages/tgui/interfaces/NtosCargo.jsx @@ -1,4 +1,4 @@ -import { CargoContent } from './Cargo.js'; +import { CargoContent } from './Cargo'; import { NtosWindow } from '../layouts'; export const NtosCargo = (props, context) => { diff --git a/tgui/packages/tgui/interfaces/NtosConfiguration.js b/tgui/packages/tgui/interfaces/NtosConfiguration.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosConfiguration.js rename to tgui/packages/tgui/interfaces/NtosConfiguration.jsx diff --git a/tgui/packages/tgui/interfaces/NtosCrewManifest.js b/tgui/packages/tgui/interfaces/NtosCrewManifest.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosCrewManifest.js rename to tgui/packages/tgui/interfaces/NtosCrewManifest.jsx diff --git a/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js b/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js rename to tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.jsx diff --git a/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitorSyndicate.js b/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitorSyndicate.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitorSyndicate.js rename to tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitorSyndicate.jsx diff --git a/tgui/packages/tgui/interfaces/NtosCyborgSelfMonitor.js b/tgui/packages/tgui/interfaces/NtosCyborgSelfMonitor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosCyborgSelfMonitor.js rename to tgui/packages/tgui/interfaces/NtosCyborgSelfMonitor.jsx diff --git a/tgui/packages/tgui/interfaces/NtosEmagConsole.js b/tgui/packages/tgui/interfaces/NtosEmagConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosEmagConsole.js rename to tgui/packages/tgui/interfaces/NtosEmagConsole.jsx diff --git a/tgui/packages/tgui/interfaces/NtosFileManager.js b/tgui/packages/tgui/interfaces/NtosFileManager.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosFileManager.js rename to tgui/packages/tgui/interfaces/NtosFileManager.jsx diff --git a/tgui/packages/tgui/interfaces/NtosGhostRbmkStats.js b/tgui/packages/tgui/interfaces/NtosGhostRbmkStats.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosGhostRbmkStats.js rename to tgui/packages/tgui/interfaces/NtosGhostRbmkStats.jsx diff --git a/tgui/packages/tgui/interfaces/NtosJobManager.js b/tgui/packages/tgui/interfaces/NtosJobManager.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosJobManager.js rename to tgui/packages/tgui/interfaces/NtosJobManager.jsx diff --git a/tgui/packages/tgui/interfaces/NtosLogViewer.js b/tgui/packages/tgui/interfaces/NtosLogViewer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosLogViewer.js rename to tgui/packages/tgui/interfaces/NtosLogViewer.jsx diff --git a/tgui/packages/tgui/interfaces/NtosMain.js b/tgui/packages/tgui/interfaces/NtosMain.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosMain.js rename to tgui/packages/tgui/interfaces/NtosMain.jsx diff --git a/tgui/packages/tgui/interfaces/NtosMessenger.js b/tgui/packages/tgui/interfaces/NtosMessenger.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosMessenger.js rename to tgui/packages/tgui/interfaces/NtosMessenger.jsx diff --git a/tgui/packages/tgui/interfaces/NtosNetChat.js b/tgui/packages/tgui/interfaces/NtosNetChat.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosNetChat.js rename to tgui/packages/tgui/interfaces/NtosNetChat.jsx diff --git a/tgui/packages/tgui/interfaces/NtosNetDos.js b/tgui/packages/tgui/interfaces/NtosNetDos.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosNetDos.js rename to tgui/packages/tgui/interfaces/NtosNetDos.jsx diff --git a/tgui/packages/tgui/interfaces/NtosNetDownloader.js b/tgui/packages/tgui/interfaces/NtosNetDownloader.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosNetDownloader.js rename to tgui/packages/tgui/interfaces/NtosNetDownloader.jsx diff --git a/tgui/packages/tgui/interfaces/NtosNetMonitor.js b/tgui/packages/tgui/interfaces/NtosNetMonitor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosNetMonitor.js rename to tgui/packages/tgui/interfaces/NtosNetMonitor.jsx diff --git a/tgui/packages/tgui/interfaces/NtosNewscaster.js b/tgui/packages/tgui/interfaces/NtosNewscaster.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosNewscaster.js rename to tgui/packages/tgui/interfaces/NtosNewscaster.jsx diff --git a/tgui/packages/tgui/interfaces/NtosNotepad.js b/tgui/packages/tgui/interfaces/NtosNotepad.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosNotepad.js rename to tgui/packages/tgui/interfaces/NtosNotepad.jsx diff --git a/tgui/packages/tgui/interfaces/NtosPhysScanner.js b/tgui/packages/tgui/interfaces/NtosPhysScanner.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosPhysScanner.js rename to tgui/packages/tgui/interfaces/NtosPhysScanner.jsx diff --git a/tgui/packages/tgui/interfaces/NtosPortraitPrinter.js b/tgui/packages/tgui/interfaces/NtosPortraitPrinter.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosPortraitPrinter.js rename to tgui/packages/tgui/interfaces/NtosPortraitPrinter.jsx diff --git a/tgui/packages/tgui/interfaces/NtosPowerMonitor.js b/tgui/packages/tgui/interfaces/NtosPowerMonitor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosPowerMonitor.js rename to tgui/packages/tgui/interfaces/NtosPowerMonitor.jsx diff --git a/tgui/packages/tgui/interfaces/NtosRadar.js b/tgui/packages/tgui/interfaces/NtosRadar.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosRadar.js rename to tgui/packages/tgui/interfaces/NtosRadar.jsx diff --git a/tgui/packages/tgui/interfaces/NtosRadarSyndicate.js b/tgui/packages/tgui/interfaces/NtosRadarSyndicate.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosRadarSyndicate.js rename to tgui/packages/tgui/interfaces/NtosRadarSyndicate.jsx diff --git a/tgui/packages/tgui/interfaces/NtosRbmkStats.js b/tgui/packages/tgui/interfaces/NtosRbmkStats.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosRbmkStats.js rename to tgui/packages/tgui/interfaces/NtosRbmkStats.jsx diff --git a/tgui/packages/tgui/interfaces/NtosRecords.js b/tgui/packages/tgui/interfaces/NtosRecords.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosRecords.js rename to tgui/packages/tgui/interfaces/NtosRecords.jsx diff --git a/tgui/packages/tgui/interfaces/NtosRevelation.js b/tgui/packages/tgui/interfaces/NtosRevelation.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosRevelation.js rename to tgui/packages/tgui/interfaces/NtosRevelation.jsx diff --git a/tgui/packages/tgui/interfaces/NtosRoboControl.js b/tgui/packages/tgui/interfaces/NtosRoboControl.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosRoboControl.js rename to tgui/packages/tgui/interfaces/NtosRoboControl.jsx diff --git a/tgui/packages/tgui/interfaces/NtosSecurEye.js b/tgui/packages/tgui/interfaces/NtosSecurEye.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosSecurEye.js rename to tgui/packages/tgui/interfaces/NtosSecurEye.jsx diff --git a/tgui/packages/tgui/interfaces/NtosSignaller.js b/tgui/packages/tgui/interfaces/NtosSignaller.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosSignaller.js rename to tgui/packages/tgui/interfaces/NtosSignaller.jsx diff --git a/tgui/packages/tgui/interfaces/NtosStationAlertConsole.js b/tgui/packages/tgui/interfaces/NtosStationAlertConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosStationAlertConsole.js rename to tgui/packages/tgui/interfaces/NtosStationAlertConsole.jsx diff --git a/tgui/packages/tgui/interfaces/NtosSupermatterMonitor.js b/tgui/packages/tgui/interfaces/NtosSupermatterMonitor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NtosSupermatterMonitor.js rename to tgui/packages/tgui/interfaces/NtosSupermatterMonitor.jsx diff --git a/tgui/packages/tgui/interfaces/NuclearBomb.js b/tgui/packages/tgui/interfaces/NuclearBomb.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/NuclearBomb.js rename to tgui/packages/tgui/interfaces/NuclearBomb.jsx diff --git a/tgui/packages/tgui/interfaces/Objective.js b/tgui/packages/tgui/interfaces/Objective.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Objective.js rename to tgui/packages/tgui/interfaces/Objective.jsx diff --git a/tgui/packages/tgui/interfaces/OperatingComputer.js b/tgui/packages/tgui/interfaces/OperatingComputer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/OperatingComputer.js rename to tgui/packages/tgui/interfaces/OperatingComputer.jsx diff --git a/tgui/packages/tgui/interfaces/Orbit.js b/tgui/packages/tgui/interfaces/Orbit.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Orbit.js rename to tgui/packages/tgui/interfaces/Orbit.jsx diff --git a/tgui/packages/tgui/interfaces/OrbitalMap.js b/tgui/packages/tgui/interfaces/OrbitalMap.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/OrbitalMap.js rename to tgui/packages/tgui/interfaces/OrbitalMap.jsx diff --git a/tgui/packages/tgui/interfaces/OreBox.js b/tgui/packages/tgui/interfaces/OreBox.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/OreBox.js rename to tgui/packages/tgui/interfaces/OreBox.jsx diff --git a/tgui/packages/tgui/interfaces/OreRedemptionMachine.js b/tgui/packages/tgui/interfaces/OreRedemptionMachine.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/OreRedemptionMachine.js rename to tgui/packages/tgui/interfaces/OreRedemptionMachine.jsx diff --git a/tgui/packages/tgui/interfaces/OutfitEditor.js b/tgui/packages/tgui/interfaces/OutfitEditor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/OutfitEditor.js rename to tgui/packages/tgui/interfaces/OutfitEditor.jsx diff --git a/tgui/packages/tgui/interfaces/OutfitManager.js b/tgui/packages/tgui/interfaces/OutfitManager.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/OutfitManager.js rename to tgui/packages/tgui/interfaces/OutfitManager.jsx diff --git a/tgui/packages/tgui/interfaces/PDAInputModal.js b/tgui/packages/tgui/interfaces/PDAInputModal.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PDAInputModal.js rename to tgui/packages/tgui/interfaces/PDAInputModal.jsx diff --git a/tgui/packages/tgui/interfaces/PaiInterface.tsx b/tgui/packages/tgui/interfaces/PaiInterface.tsx index b4d3acb86e140..09153e2f8c245 100644 --- a/tgui/packages/tgui/interfaces/PaiInterface.tsx +++ b/tgui/packages/tgui/interfaces/PaiInterface.tsx @@ -1,4 +1,5 @@ import { BooleanLike } from 'common/react'; +import { capitalizeAll } from 'common/string'; import { useBackend, useSharedState } from '../backend'; import { Box, Button, LabeledList, Icon, NoticeBox, ProgressBar, Section, Stack, Table, Tabs, Tooltip } from '../components'; import { Window } from '../layouts'; @@ -290,7 +291,7 @@ const InstalledSoftware = (props, context) => { installed.map((software) => { return ( ); }) @@ -310,18 +311,7 @@ const InstalledInfo = (props) => { return ; } else { return ( -
letter.toUpperCase() - // eslint-disable-next-line react/jsx-indent - ) - }> +
{software && ( {SOFTWARE_DESC[software] || ''} @@ -516,7 +506,7 @@ const AvailableRow = (props, context) => { return ( - {software.name.replace(/^\w/, (c) => c.toUpperCase())} + {capitalizeAll(software.name)} diff --git a/tgui/packages/tgui/interfaces/Pandemic.js b/tgui/packages/tgui/interfaces/Pandemic.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Pandemic.js rename to tgui/packages/tgui/interfaces/Pandemic.jsx diff --git a/tgui/packages/tgui/interfaces/ParticleAccelerator.js b/tgui/packages/tgui/interfaces/ParticleAccelerator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ParticleAccelerator.js rename to tgui/packages/tgui/interfaces/ParticleAccelerator.jsx diff --git a/tgui/packages/tgui/interfaces/Particool.js b/tgui/packages/tgui/interfaces/Particool.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Particool.js rename to tgui/packages/tgui/interfaces/Particool.jsx diff --git a/tgui/packages/tgui/interfaces/PersonalCrafting.js b/tgui/packages/tgui/interfaces/PersonalCrafting.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PersonalCrafting.js rename to tgui/packages/tgui/interfaces/PersonalCrafting.jsx diff --git a/tgui/packages/tgui/interfaces/Photocopier.js b/tgui/packages/tgui/interfaces/Photocopier.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Photocopier.js rename to tgui/packages/tgui/interfaces/Photocopier.jsx diff --git a/tgui/packages/tgui/interfaces/PhysicalNewscaster.js b/tgui/packages/tgui/interfaces/PhysicalNewscaster.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PhysicalNewscaster.js rename to tgui/packages/tgui/interfaces/PhysicalNewscaster.jsx diff --git a/tgui/packages/tgui/interfaces/PictureSelectModal.js b/tgui/packages/tgui/interfaces/PictureSelectModal.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PictureSelectModal.js rename to tgui/packages/tgui/interfaces/PictureSelectModal.jsx diff --git a/tgui/packages/tgui/interfaces/PlantDNAManipulator.js b/tgui/packages/tgui/interfaces/PlantDNAManipulator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PlantDNAManipulator.js rename to tgui/packages/tgui/interfaces/PlantDNAManipulator.jsx diff --git a/tgui/packages/tgui/interfaces/PlayerPanel.js b/tgui/packages/tgui/interfaces/PlayerPanel.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PlayerPanel.js rename to tgui/packages/tgui/interfaces/PlayerPanel.jsx diff --git a/tgui/packages/tgui/interfaces/PortableGenerator.js b/tgui/packages/tgui/interfaces/PortableGenerator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PortableGenerator.js rename to tgui/packages/tgui/interfaces/PortableGenerator.jsx diff --git a/tgui/packages/tgui/interfaces/PortablePump.js b/tgui/packages/tgui/interfaces/PortablePump.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PortablePump.js rename to tgui/packages/tgui/interfaces/PortablePump.jsx diff --git a/tgui/packages/tgui/interfaces/PortableScrubber.js b/tgui/packages/tgui/interfaces/PortableScrubber.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PortableScrubber.js rename to tgui/packages/tgui/interfaces/PortableScrubber.jsx diff --git a/tgui/packages/tgui/interfaces/PortableThermomachine.js b/tgui/packages/tgui/interfaces/PortableThermomachine.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PortableThermomachine.js rename to tgui/packages/tgui/interfaces/PortableThermomachine.jsx diff --git a/tgui/packages/tgui/interfaces/PortraitPicker.js b/tgui/packages/tgui/interfaces/PortraitPicker.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PortraitPicker.js rename to tgui/packages/tgui/interfaces/PortraitPicker.jsx diff --git a/tgui/packages/tgui/interfaces/PowerMonitor.js b/tgui/packages/tgui/interfaces/PowerMonitor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PowerMonitor.js rename to tgui/packages/tgui/interfaces/PowerMonitor.jsx diff --git a/tgui/packages/tgui/interfaces/ProbingConsole.js b/tgui/packages/tgui/interfaces/ProbingConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ProbingConsole.js rename to tgui/packages/tgui/interfaces/ProbingConsole.jsx diff --git a/tgui/packages/tgui/interfaces/ProximitySensor.js b/tgui/packages/tgui/interfaces/ProximitySensor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ProximitySensor.js rename to tgui/packages/tgui/interfaces/ProximitySensor.jsx diff --git a/tgui/packages/tgui/interfaces/PsychicPlane.js b/tgui/packages/tgui/interfaces/PsychicPlane.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/PsychicPlane.js rename to tgui/packages/tgui/interfaces/PsychicPlane.jsx diff --git a/tgui/packages/tgui/interfaces/RDConsole.js b/tgui/packages/tgui/interfaces/RDConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/RDConsole.js rename to tgui/packages/tgui/interfaces/RDConsole.jsx diff --git a/tgui/packages/tgui/interfaces/Radio.js b/tgui/packages/tgui/interfaces/Radio.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Radio.js rename to tgui/packages/tgui/interfaces/Radio.jsx diff --git a/tgui/packages/tgui/interfaces/RadioactiveMicrolaser.js b/tgui/packages/tgui/interfaces/RadioactiveMicrolaser.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/RadioactiveMicrolaser.js rename to tgui/packages/tgui/interfaces/RadioactiveMicrolaser.jsx diff --git a/tgui/packages/tgui/interfaces/RapidPipeDispenser.js b/tgui/packages/tgui/interfaces/RapidPipeDispenser.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/RapidPipeDispenser.js rename to tgui/packages/tgui/interfaces/RapidPipeDispenser.jsx diff --git a/tgui/packages/tgui/interfaces/RbmkControlRods.js b/tgui/packages/tgui/interfaces/RbmkControlRods.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/RbmkControlRods.js rename to tgui/packages/tgui/interfaces/RbmkControlRods.jsx diff --git a/tgui/packages/tgui/interfaces/ReligiousTool.js b/tgui/packages/tgui/interfaces/ReligiousTool.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ReligiousTool.js rename to tgui/packages/tgui/interfaces/ReligiousTool.jsx diff --git a/tgui/packages/tgui/interfaces/RemoteRobotControl.js b/tgui/packages/tgui/interfaces/RemoteRobotControl.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/RemoteRobotControl.js rename to tgui/packages/tgui/interfaces/RemoteRobotControl.jsx diff --git a/tgui/packages/tgui/interfaces/RequestManager.js b/tgui/packages/tgui/interfaces/RequestManager.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/RequestManager.js rename to tgui/packages/tgui/interfaces/RequestManager.jsx diff --git a/tgui/packages/tgui/interfaces/RoboticsControlConsole.js b/tgui/packages/tgui/interfaces/RoboticsControlConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/RoboticsControlConsole.js rename to tgui/packages/tgui/interfaces/RoboticsControlConsole.jsx diff --git a/tgui/packages/tgui/interfaces/SatelliteControl.js b/tgui/packages/tgui/interfaces/SatelliteControl.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/SatelliteControl.js rename to tgui/packages/tgui/interfaces/SatelliteControl.jsx diff --git a/tgui/packages/tgui/interfaces/ScannerGate.js b/tgui/packages/tgui/interfaces/ScannerGate.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ScannerGate.js rename to tgui/packages/tgui/interfaces/ScannerGate.jsx diff --git a/tgui/packages/tgui/interfaces/SeedExtractor.js b/tgui/packages/tgui/interfaces/SeedExtractor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/SeedExtractor.js rename to tgui/packages/tgui/interfaces/SeedExtractor.jsx diff --git a/tgui/packages/tgui/interfaces/SelectEquipment.js b/tgui/packages/tgui/interfaces/SelectEquipment.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/SelectEquipment.js rename to tgui/packages/tgui/interfaces/SelectEquipment.jsx diff --git a/tgui/packages/tgui/interfaces/ShuttleDesignator.js b/tgui/packages/tgui/interfaces/ShuttleDesignator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ShuttleDesignator.js rename to tgui/packages/tgui/interfaces/ShuttleDesignator.jsx diff --git a/tgui/packages/tgui/interfaces/ShuttleManipulator.js b/tgui/packages/tgui/interfaces/ShuttleManipulator.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ShuttleManipulator.js rename to tgui/packages/tgui/interfaces/ShuttleManipulator.jsx diff --git a/tgui/packages/tgui/interfaces/Signaler.js b/tgui/packages/tgui/interfaces/Signaler.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Signaler.js rename to tgui/packages/tgui/interfaces/Signaler.jsx diff --git a/tgui/packages/tgui/interfaces/SimpleBot.tsx b/tgui/packages/tgui/interfaces/SimpleBot.tsx index 5022addced540..67a1cbc7d7252 100644 --- a/tgui/packages/tgui/interfaces/SimpleBot.tsx +++ b/tgui/packages/tgui/interfaces/SimpleBot.tsx @@ -1,8 +1,8 @@ -import { multiline } from '../../common/string'; -import { useBackend } from '../backend'; -import { Button, Icon, LabeledControls, NoticeBox, Section, Slider, Stack, Tooltip, Flex } from '../components'; -import { Window } from '../layouts'; -import { getGasLabel } from '../constants'; +import { capitalizeAll, multiline } from 'common/string'; +import { useBackend } from 'tgui/backend'; +import { Button, Icon, LabeledControls, NoticeBox, Section, Slider, Stack, Tooltip, Flex } from 'tgui/components'; +import { Window } from 'tgui/layouts'; +import { getGasLabel } from 'tgui/constants'; type SimpleBotContext = { can_hack: number; @@ -169,10 +169,7 @@ const ControlsDisplay = (_, context) => { return ; } return ( - letter.toUpperCase())}> + ); diff --git a/tgui/packages/tgui/interfaces/Sleeper.js b/tgui/packages/tgui/interfaces/Sleeper.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Sleeper.js rename to tgui/packages/tgui/interfaces/Sleeper.jsx diff --git a/tgui/packages/tgui/interfaces/SlimeBodySwapper.js b/tgui/packages/tgui/interfaces/SlimeBodySwapper.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/SlimeBodySwapper.js rename to tgui/packages/tgui/interfaces/SlimeBodySwapper.jsx diff --git a/tgui/packages/tgui/interfaces/SmartVend.js b/tgui/packages/tgui/interfaces/SmartVend.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/SmartVend.js rename to tgui/packages/tgui/interfaces/SmartVend.jsx diff --git a/tgui/packages/tgui/interfaces/Smelter.js b/tgui/packages/tgui/interfaces/Smelter.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Smelter.js rename to tgui/packages/tgui/interfaces/Smelter.jsx diff --git a/tgui/packages/tgui/interfaces/Smes.js b/tgui/packages/tgui/interfaces/Smes.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Smes.js rename to tgui/packages/tgui/interfaces/Smes.jsx diff --git a/tgui/packages/tgui/interfaces/SmokeMachine.js b/tgui/packages/tgui/interfaces/SmokeMachine.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/SmokeMachine.js rename to tgui/packages/tgui/interfaces/SmokeMachine.jsx diff --git a/tgui/packages/tgui/interfaces/SolarControl.js b/tgui/packages/tgui/interfaces/SolarControl.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/SolarControl.js rename to tgui/packages/tgui/interfaces/SolarControl.jsx diff --git a/tgui/packages/tgui/interfaces/SpawnersMenu.js b/tgui/packages/tgui/interfaces/SpawnersMenu.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/SpawnersMenu.js rename to tgui/packages/tgui/interfaces/SpawnersMenu.jsx diff --git a/tgui/packages/tgui/interfaces/Stack.js b/tgui/packages/tgui/interfaces/Stack.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Stack.js rename to tgui/packages/tgui/interfaces/Stack.jsx diff --git a/tgui/packages/tgui/interfaces/StationAlertConsole.js b/tgui/packages/tgui/interfaces/StationAlertConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/StationAlertConsole.js rename to tgui/packages/tgui/interfaces/StationAlertConsole.jsx diff --git a/tgui/packages/tgui/interfaces/SupermatterMonitor.js b/tgui/packages/tgui/interfaces/SupermatterMonitor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/SupermatterMonitor.js rename to tgui/packages/tgui/interfaces/SupermatterMonitor.jsx diff --git a/tgui/packages/tgui/interfaces/SyndContractor.js b/tgui/packages/tgui/interfaces/SyndContractor.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/SyndContractor.js rename to tgui/packages/tgui/interfaces/SyndContractor.jsx diff --git a/tgui/packages/tgui/interfaces/TachyonArray.js b/tgui/packages/tgui/interfaces/TachyonArray.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/TachyonArray.js rename to tgui/packages/tgui/interfaces/TachyonArray.jsx diff --git a/tgui/packages/tgui/interfaces/Tank.js b/tgui/packages/tgui/interfaces/Tank.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Tank.js rename to tgui/packages/tgui/interfaces/Tank.jsx diff --git a/tgui/packages/tgui/interfaces/TankDispenser.js b/tgui/packages/tgui/interfaces/TankDispenser.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/TankDispenser.js rename to tgui/packages/tgui/interfaces/TankDispenser.jsx diff --git a/tgui/packages/tgui/interfaces/TechFab.js b/tgui/packages/tgui/interfaces/TechFab.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/TechFab.js rename to tgui/packages/tgui/interfaces/TechFab.jsx diff --git a/tgui/packages/tgui/interfaces/Techweb.js b/tgui/packages/tgui/interfaces/Techweb.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Techweb.js rename to tgui/packages/tgui/interfaces/Techweb.jsx diff --git a/tgui/packages/tgui/interfaces/Telecomms.js b/tgui/packages/tgui/interfaces/Telecomms.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Telecomms.js rename to tgui/packages/tgui/interfaces/Telecomms.jsx diff --git a/tgui/packages/tgui/interfaces/Teleporter.js b/tgui/packages/tgui/interfaces/Teleporter.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Teleporter.js rename to tgui/packages/tgui/interfaces/Teleporter.jsx diff --git a/tgui/packages/tgui/interfaces/ThermoMachine.js b/tgui/packages/tgui/interfaces/ThermoMachine.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/ThermoMachine.js rename to tgui/packages/tgui/interfaces/ThermoMachine.jsx diff --git a/tgui/packages/tgui/interfaces/TicketBrowser.js b/tgui/packages/tgui/interfaces/TicketBrowser.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/TicketBrowser.js rename to tgui/packages/tgui/interfaces/TicketBrowser.jsx diff --git a/tgui/packages/tgui/interfaces/TicketMessenger.js b/tgui/packages/tgui/interfaces/TicketMessenger.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/TicketMessenger.js rename to tgui/packages/tgui/interfaces/TicketMessenger.jsx diff --git a/tgui/packages/tgui/interfaces/Timer.js b/tgui/packages/tgui/interfaces/Timer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Timer.js rename to tgui/packages/tgui/interfaces/Timer.jsx diff --git a/tgui/packages/tgui/interfaces/TrackedPlaytime.js b/tgui/packages/tgui/interfaces/TrackedPlaytime.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/TrackedPlaytime.js rename to tgui/packages/tgui/interfaces/TrackedPlaytime.jsx diff --git a/tgui/packages/tgui/interfaces/TraitorBackstoryMenu.js b/tgui/packages/tgui/interfaces/TraitorBackstoryMenu.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/TraitorBackstoryMenu.js rename to tgui/packages/tgui/interfaces/TraitorBackstoryMenu.jsx diff --git a/tgui/packages/tgui/interfaces/TransferValve.js b/tgui/packages/tgui/interfaces/TransferValve.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/TransferValve.js rename to tgui/packages/tgui/interfaces/TransferValve.jsx diff --git a/tgui/packages/tgui/interfaces/TurbineComputer.js b/tgui/packages/tgui/interfaces/TurbineComputer.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/TurbineComputer.js rename to tgui/packages/tgui/interfaces/TurbineComputer.jsx diff --git a/tgui/packages/tgui/interfaces/TurretControl.js b/tgui/packages/tgui/interfaces/TurretControl.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/TurretControl.js rename to tgui/packages/tgui/interfaces/TurretControl.jsx diff --git a/tgui/packages/tgui/interfaces/Uplink.js b/tgui/packages/tgui/interfaces/Uplink.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Uplink.js rename to tgui/packages/tgui/interfaces/Uplink.jsx diff --git a/tgui/packages/tgui/interfaces/Vendatray.js b/tgui/packages/tgui/interfaces/Vendatray.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Vendatray.js rename to tgui/packages/tgui/interfaces/Vendatray.jsx diff --git a/tgui/packages/tgui/interfaces/Vending.tsx b/tgui/packages/tgui/interfaces/Vending.tsx index 271d4acda2f2b..c8a066a131aea 100644 --- a/tgui/packages/tgui/interfaces/Vending.tsx +++ b/tgui/packages/tgui/interfaces/Vending.tsx @@ -1,4 +1,5 @@ import { classes } from 'common/react'; +import { capitalizeAll } from 'common/string'; import { useBackend, useLocalState } from 'tgui/backend'; import { Box, Button, Icon, LabeledList, NoticeBox, Section, Stack, Table } from 'tgui/components'; import { Window } from 'tgui/layouts'; @@ -213,7 +214,7 @@ const VendingRow = (props, context) => { - {product.name.replace(/^\w/, (c) => c.toUpperCase())} + {capitalizeAll(product.name)} {!!productStock?.colorable && } diff --git a/tgui/packages/tgui/interfaces/Vote.js b/tgui/packages/tgui/interfaces/Vote.jsx similarity index 91% rename from tgui/packages/tgui/interfaces/Vote.js rename to tgui/packages/tgui/interfaces/Vote.jsx index a6578901e31b8..eda5f107c3ca5 100644 --- a/tgui/packages/tgui/interfaces/Vote.js +++ b/tgui/packages/tgui/interfaces/Vote.jsx @@ -1,22 +1,14 @@ -import { useBackend } from '../backend'; -import { Box, Icon, Flex, Button, Section, Collapsible } from '../components'; -import { Window } from '../layouts'; -import { logger } from '../logging'; +import { capitalizeFirst } from 'common/string'; +import { useBackend } from 'tgui/backend'; +import { Box, Icon, Flex, Button, Section, Collapsible } from 'tgui/components'; +import { Window } from 'tgui/layouts'; export const Vote = (props, context) => { const { data } = useBackend(context); const { mode, question, lower_admin } = data; return ( - c.toUpperCase()) : mode.replace(/^\w/, (c) => c.toUpperCase())}` - : '' - }`} - width={400} - height={500}> + {!!lower_admin && } @@ -147,7 +139,7 @@ const DisplayChoices = (props, context) => { }); }} disabled={choice === props.choices[selectedChoice - props.startIndex - 1]}> - {choice.name?.replace(/^\w/, (c) => c.toUpperCase())} + {capitalizeFirst(choice.name)} {choice === props.choices[selectedChoice - props.startIndex - 1] && } diff --git a/tgui/packages/tgui/interfaces/Wires.js b/tgui/packages/tgui/interfaces/Wires.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Wires.js rename to tgui/packages/tgui/interfaces/Wires.jsx diff --git a/tgui/packages/tgui/interfaces/Workshop.js b/tgui/packages/tgui/interfaces/Workshop.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/Workshop.js rename to tgui/packages/tgui/interfaces/Workshop.jsx diff --git a/tgui/packages/tgui/interfaces/XenoartifactConsole.js b/tgui/packages/tgui/interfaces/XenoartifactConsole.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/XenoartifactConsole.js rename to tgui/packages/tgui/interfaces/XenoartifactConsole.jsx diff --git a/tgui/packages/tgui/interfaces/XenoartifactLabeler.js b/tgui/packages/tgui/interfaces/XenoartifactLabeler.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/XenoartifactLabeler.js rename to tgui/packages/tgui/interfaces/XenoartifactLabeler.jsx diff --git a/tgui/packages/tgui/interfaces/common/AccessList.js b/tgui/packages/tgui/interfaces/common/AccessList.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/common/AccessList.js rename to tgui/packages/tgui/interfaces/common/AccessList.jsx diff --git a/tgui/packages/tgui/interfaces/common/AtmosControls.js b/tgui/packages/tgui/interfaces/common/AtmosControls.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/common/AtmosControls.js rename to tgui/packages/tgui/interfaces/common/AtmosControls.jsx diff --git a/tgui/packages/tgui/interfaces/common/BeakerContents.js b/tgui/packages/tgui/interfaces/common/BeakerContents.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/common/BeakerContents.js rename to tgui/packages/tgui/interfaces/common/BeakerContents.jsx diff --git a/tgui/packages/tgui/interfaces/common/InterfaceLockNoticeBox.js b/tgui/packages/tgui/interfaces/common/InterfaceLockNoticeBox.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/common/InterfaceLockNoticeBox.js rename to tgui/packages/tgui/interfaces/common/InterfaceLockNoticeBox.jsx diff --git a/tgui/packages/tgui/interfaces/common/PortableAtmos.js b/tgui/packages/tgui/interfaces/common/PortableAtmos.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/common/PortableAtmos.js rename to tgui/packages/tgui/interfaces/common/PortableAtmos.jsx diff --git a/tgui/packages/tgui/interfaces/manually-routed/KitchenSink.js b/tgui/packages/tgui/interfaces/manually-routed/KitchenSink.jsx similarity index 100% rename from tgui/packages/tgui/interfaces/manually-routed/KitchenSink.js rename to tgui/packages/tgui/interfaces/manually-routed/KitchenSink.jsx diff --git a/tgui/packages/tgui/layouts/Layout.js b/tgui/packages/tgui/layouts/Layout.jsx similarity index 100% rename from tgui/packages/tgui/layouts/Layout.js rename to tgui/packages/tgui/layouts/Layout.jsx diff --git a/tgui/packages/tgui/layouts/NtosWindow.js b/tgui/packages/tgui/layouts/NtosWindow.jsx similarity index 100% rename from tgui/packages/tgui/layouts/NtosWindow.js rename to tgui/packages/tgui/layouts/NtosWindow.jsx diff --git a/tgui/packages/tgui/layouts/Pane.js b/tgui/packages/tgui/layouts/Pane.jsx similarity index 100% rename from tgui/packages/tgui/layouts/Pane.js rename to tgui/packages/tgui/layouts/Pane.jsx diff --git a/tgui/packages/tgui/layouts/Window.js b/tgui/packages/tgui/layouts/Window.jsx similarity index 100% rename from tgui/packages/tgui/layouts/Window.js rename to tgui/packages/tgui/layouts/Window.jsx diff --git a/tgui/packages/tgui/layouts/index.js b/tgui/packages/tgui/layouts/index.ts similarity index 100% rename from tgui/packages/tgui/layouts/index.js rename to tgui/packages/tgui/layouts/index.ts diff --git a/tgui/packages/tgui/links.js b/tgui/packages/tgui/links.js deleted file mode 100644 index 0aaca2a1d7eec..0000000000000 --- a/tgui/packages/tgui/links.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -/** - * Prevents baby jailing the user when he clicks an external link. - */ -export const captureExternalLinks = () => { - // Click handler - const listenerFn = (e) => { - const tagName = String(e.target.tagName).toLowerCase(); - const href = String(e.target.href); - // Must be a link - if (tagName !== 'a') { - return; - } - // Leave BYOND links alone - const isByondLink = href.charAt(0) === '?' || href.startsWith(location.origin) || href.startsWith('byond://'); - if (isByondLink) { - return; - } - // Prevent default action - e.preventDefault(); - // Open the link - Byond.sendMessage({ - type: 'openLink', - url: href, - }); - }; - // Subscribe to all document clicks - document.addEventListener('click', listenerFn); -}; diff --git a/tgui/packages/tgui/links.test.ts b/tgui/packages/tgui/links.test.ts new file mode 100644 index 0000000000000..96b010a653931 --- /dev/null +++ b/tgui/packages/tgui/links.test.ts @@ -0,0 +1,76 @@ +import { captureExternalLinks } from './links'; + +describe('captureExternalLinks', () => { + let addEventListenerSpy; + let clickHandler; + + beforeEach(() => { + addEventListenerSpy = jest.spyOn(document, 'addEventListener'); + captureExternalLinks(); + clickHandler = addEventListenerSpy.mock.calls[0][1]; + }); + + afterEach(() => { + addEventListenerSpy.mockRestore(); + }); + + it('should subscribe to document clicks', () => { + expect(addEventListenerSpy).toHaveBeenCalledWith('click', expect.any(Function)); + }); + + it('should preventDefault and send a message when a non-BYOND external link is clicked', () => { + const externalLink = { + tagName: 'A', + getAttribute: () => 'https://example.com', + parentElement: document.body, + }; + const byond = { sendMessage: jest.fn() }; + // @ts-ignore + global.Byond = byond; + + const evt = { target: externalLink, preventDefault: jest.fn() }; + clickHandler(evt); + + expect(evt.preventDefault).toHaveBeenCalled(); + expect(byond.sendMessage).toHaveBeenCalledWith({ + type: 'openLink', + url: 'https://example.com', + }); + }); + + it('should not preventDefault or send a message when a BYOND link is clicked', () => { + const byondLink = { + tagName: 'A', + getAttribute: () => 'byond://server-address', + parentElement: document.body, + }; + const byond = { sendMessage: jest.fn() }; + // @ts-ignore + global.Byond = byond; + + const evt = { target: byondLink, preventDefault: jest.fn() }; + clickHandler(evt); + + expect(evt.preventDefault).not.toHaveBeenCalled(); + expect(byond.sendMessage).not.toHaveBeenCalled(); + }); + + it('should add https:// to www links', () => { + const wwwLink = { + tagName: 'A', + getAttribute: () => 'www.example.com', + parentElement: document.body, + }; + const byond = { sendMessage: jest.fn() }; + // @ts-ignore + global.Byond = byond; + + const evt = { target: wwwLink, preventDefault: jest.fn() }; + clickHandler(evt); + + expect(byond.sendMessage).toHaveBeenCalledWith({ + type: 'openLink', + url: 'https://www.example.com', + }); + }); +}); diff --git a/tgui/packages/tgui/links.ts b/tgui/packages/tgui/links.ts new file mode 100644 index 0000000000000..7c3036b922352 --- /dev/null +++ b/tgui/packages/tgui/links.ts @@ -0,0 +1,45 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +/** + * Prevents baby jailing the user when he clicks an external link. + */ +export const captureExternalLinks = () => { + // Subscribe to all document clicks + document.addEventListener('click', (evt: MouseEvent) => { + let target = evt.target as HTMLElement; + // Recurse down the tree to find a valid link + while (true) { + // Reached the end, bail. + if (!target || target === document.body) { + return; + } + const tagName = String(target.tagName).toLowerCase(); + if (tagName === 'a') { + break; + } + target = target.parentElement as HTMLElement; + } + const hrefAttr = target.getAttribute('href') || ''; + // Leave BYOND links alone + const isByondLink = hrefAttr.charAt(0) === '?' || hrefAttr.startsWith('byond://'); + if (isByondLink) { + return; + } + // Prevent default action + evt.preventDefault(); + // Normalize the URL + let url = hrefAttr; + if (url.toLowerCase().startsWith('www')) { + url = 'https://' + url; + } + // Open the link + Byond.sendMessage({ + type: 'openLink', + url, + }); + }); +}; diff --git a/tgui/packages/tgui/logging.js b/tgui/packages/tgui/logging.ts similarity index 56% rename from tgui/packages/tgui/logging.js rename to tgui/packages/tgui/logging.ts index e2b2404d649eb..cc2e9d2f01874 100644 --- a/tgui/packages/tgui/logging.js +++ b/tgui/packages/tgui/logging.ts @@ -12,15 +12,23 @@ const LEVEL_INFO = 2; const LEVEL_WARN = 3; const LEVEL_ERROR = 4; -const log = (level, ns, ...args) => { +interface Logger { + debug: (...args: any[]) => void; + log: (...args: any[]) => void; + info: (...args: any[]) => void; + warn: (...args: any[]) => void; + error: (...args: any[]) => void; +} + +const log = (level: number, namespace = 'Generic', ...args: any[]): void => { // Send logs to a remote log collector if (process.env.NODE_ENV !== 'production') { - sendLogEntry(level, ns, ...args); + sendLogEntry(level, namespace, ...args); } // Send important logs to the backend if (level >= LEVEL_INFO) { const logEntry = - [ns, ...args] + [namespace, ...args] .map((value) => { if (typeof value === 'string') { return value; @@ -36,18 +44,19 @@ const log = (level, ns, ...args) => { navigator.userAgent; Byond.sendMessage({ type: 'log', + ns: namespace, message: logEntry, }); } }; -export const createLogger = (ns) => { +export const createLogger = (namespace?: string): Logger => { return { - debug: (...args) => log(LEVEL_DEBUG, ns, ...args), - log: (...args) => log(LEVEL_LOG, ns, ...args), - info: (...args) => log(LEVEL_INFO, ns, ...args), - warn: (...args) => log(LEVEL_WARN, ns, ...args), - error: (...args) => log(LEVEL_ERROR, ns, ...args), + debug: (...args) => log(LEVEL_DEBUG, namespace, ...args), + log: (...args) => log(LEVEL_LOG, namespace, ...args), + info: (...args) => log(LEVEL_INFO, namespace, ...args), + warn: (...args) => log(LEVEL_WARN, namespace, ...args), + error: (...args) => log(LEVEL_ERROR, namespace, ...args), }; }; @@ -56,4 +65,4 @@ export const createLogger = (ns) => { * * Does not have a namespace associated with it. */ -export const logger = createLogger(); +export const logger: Logger = createLogger(); diff --git a/tgui/packages/tgui/package.json b/tgui/packages/tgui/package.json index 310a8247848e3..05c3efb1069fd 100644 --- a/tgui/packages/tgui/package.json +++ b/tgui/packages/tgui/package.json @@ -1,16 +1,16 @@ { "private": true, "name": "tgui", - "version": "4.3.0", + "version": "4.4.0", "dependencies": { - "@popperjs/core": "^2.9.3", + "@popperjs/core": "^2.11.8", "@types/marked": "^4.0.8", "common": "workspace:*", - "csstype": "^3.1.1", + "csstype": "^3.1.3", "dateformat": "^5.0.3", "dompurify": "^3.0.1", - "inferno": "^8.2.1", - "inferno-vnode-flags": "^8.2.1", + "inferno": "^8.2.3", + "inferno-vnode-flags": "^8.2.3", "js-yaml": "^4.1.0", "marked": "^4.2.12", "tgui-dev-server": "workspace:*", diff --git a/tgui/packages/tgui/routes.js b/tgui/packages/tgui/routes.tsx similarity index 79% rename from tgui/packages/tgui/routes.js rename to tgui/packages/tgui/routes.tsx index 7a19cbd679596..6fd4cd850a872 100644 --- a/tgui/packages/tgui/routes.js +++ b/tgui/packages/tgui/routes.tsx @@ -4,14 +4,16 @@ * @license MIT */ -import { selectBackend } from './backend'; import { Icon, Section, Stack } from './components'; -import { selectDebug } from './debug/selectors'; + +import { Store } from 'common/redux'; import { Window } from './layouts'; +import { selectBackend } from './backend'; +import { selectDebug } from './debug/selectors'; const requireInterface = require.context('./interfaces'); -const routingError = (type, name) => () => { +const routingError = (type: 'notFound' | 'missingExport', name: string) => () => { return ( @@ -30,6 +32,7 @@ const routingError = (type, name) => () => { ); }; +// Displays an empty Window with scrollable content const SuspendedWindow = () => { return ( @@ -38,6 +41,7 @@ const SuspendedWindow = () => { ); }; +// Displays a loading screen with a spinning icon const RefreshingWindow = () => { return ( @@ -55,7 +59,8 @@ const RefreshingWindow = () => { ); }; -export const getRoutedComponent = (store) => { +// Get the component for the current route +export const getRoutedComponent = (store: Store) => { const state = store.getState(); const { suspended, config } = selectBackend(state); if (suspended) { @@ -73,14 +78,14 @@ export const getRoutedComponent = (store) => { } const name = config?.interface; const interfacePathBuilders = [ - (name) => `./${name}.tsx`, - (name) => `./${name}.js`, - (name) => `./${name}/index.tsx`, - (name) => `./${name}/index.js`, + (name: string) => `./${name}.tsx`, + (name: string) => `./${name}.jsx`, + (name: string) => `./${name}/index.tsx`, + (name: string) => `./${name}/index.jsx`, ]; let esModule; while (!esModule && interfacePathBuilders.length > 0) { - const interfacePathBuilder = interfacePathBuilders.shift(); + const interfacePathBuilder = interfacePathBuilders.shift()!; const interfacePath = interfacePathBuilder(name); try { esModule = requireInterface(interfacePath); diff --git a/tgui/packages/tgui/sanitize.test.ts b/tgui/packages/tgui/sanitize.test.ts new file mode 100644 index 0000000000000..38416e9ef1aa5 --- /dev/null +++ b/tgui/packages/tgui/sanitize.test.ts @@ -0,0 +1,33 @@ +import { sanitizeText } from './sanitize'; + +describe('sanitizeText', () => { + it('should sanitize basic HTML input', () => { + const input = 'Hello, world!'; + const expected = 'Hello, world!'; + const result = sanitizeText(input); + expect(result).toBe(expected); + }); + + it('should sanitize advanced HTML input when advHtml flag is true', () => { + const input = 'Hello, world!'; + const expected = 'Hello, world!'; + const result = sanitizeText(input, true); + expect(result).toBe(expected); + }); + + it('should allow specific HTML tags when tags array is provided', () => { + const input = 'Hello, world!Goodbye, world!'; + const tags = ['b']; + const expected = 'Hello, world!Goodbye, world!'; + const result = sanitizeText(input, false, tags); + expect(result).toBe(expected); + }); + + it('should allow advanced HTML tags when advTags array is provided and advHtml flag is true', () => { + const input = 'Hello, world!'; + const advTags = ['iframe']; + const expected = 'Hello, world!'; + const result = sanitizeText(input, true, undefined, undefined, advTags); + expect(result).toBe(expected); + }); +}); diff --git a/tgui/packages/tgui/sanitize.js b/tgui/packages/tgui/sanitize.ts similarity index 68% rename from tgui/packages/tgui/sanitize.js rename to tgui/packages/tgui/sanitize.ts index 8077efa9b5037..f2473df894b83 100644 --- a/tgui/packages/tgui/sanitize.js +++ b/tgui/packages/tgui/sanitize.ts @@ -54,17 +54,17 @@ let defAttr = ['class', 'style']; /** * Feed it a string and it should spit out a sanitized version. * - * @param {string} input - * @param {boolean} advHtml - * @param {array} tags - * @param {array} forbidAttr - * @param {array} advTags + * @param input - Input HTML string to sanitize + * @param advHtml - Flag to enable/disable advanced HTML + * @param tags - List of allowed HTML tags + * @param forbidAttr - List of forbidden HTML attributes + * @param advTags - List of advanced HTML tags allowed for trusted sources */ -export const sanitizeText = (input, advHtml, tags = defTag, forbidAttr = defAttr, advTags = advTag) => { +export const sanitizeText = (input: string, advHtml = false, tags = defTag, forbidAttr = defAttr, advTags = advTag) => { // This is VERY important to think first if you NEED // the tag you put in here. We are pushing all this // though dangerouslySetInnerHTML and even though - // the default DOMPurify kills javascript, it dosn't + // the default DOMPurify kills javascript, it doesn't // kill href links or such if (advHtml) { tags = tags.concat(advTags); diff --git a/tgui/packages/tgui/store.js b/tgui/packages/tgui/store.js deleted file mode 100644 index c89db0f5941de..0000000000000 --- a/tgui/packages/tgui/store.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -import { flow } from 'common/fp'; -import { applyMiddleware, combineReducers, createStore } from 'common/redux'; -import { Component } from 'inferno'; -import { assetMiddleware } from './assets'; -import { backendMiddleware, backendReducer } from './backend'; -import { debugMiddleware, debugReducer, relayMiddleware } from './debug'; -import { createLogger } from './logging'; - -const logger = createLogger('store'); - -export const configureStore = (options = {}) => { - const { sideEffects = true } = options; - const reducer = flow([ - combineReducers({ - debug: debugReducer, - backend: backendReducer, - }), - options.reducer, - ]); - const middleware = !sideEffects - ? [] - : [...(options.middleware?.pre || []), assetMiddleware, backendMiddleware, ...(options.middleware?.post || [])]; - if (process.env.NODE_ENV !== 'production') { - // We are using two if statements because Webpack is capable of - // removing this specific block as dead code. - if (sideEffects) { - middleware.unshift(loggingMiddleware, debugMiddleware, relayMiddleware); - } - } - const enhancer = applyMiddleware(...middleware); - const store = createStore(reducer, enhancer); - // Globals - window.__store__ = store; - window.__augmentStack__ = createStackAugmentor(store); - return store; -}; - -const loggingMiddleware = (store) => (next) => (action) => { - const { type, payload } = action; - if (type === 'update' || type === 'backend/update') { - logger.debug('action', { type }); - } else { - logger.debug('action', action); - } - return next(action); -}; - -/** - * Creates a function, which can be assigned to window.__augmentStack__ - * to augment reported stack traces with useful data for debugging. - */ -const createStackAugmentor = (store) => (stack, error) => { - if (!error) { - error = new Error(stack.split('\n')[0]); - error.stack = stack; - } else if (typeof error === 'object' && !error.stack) { - error.stack = stack; - } - logger.log('FatalError:', error); - const state = store.getState(); - const config = state?.backend?.config; - let augmentedStack = stack; - augmentedStack += '\nUser Agent: ' + navigator.userAgent; - augmentedStack += - '\nState: ' + - JSON.stringify({ - ckey: config?.client?.ckey, - interface: config?.interface, - window: config?.window, - }); - return augmentedStack; -}; - -/** - * Store provider for Inferno apps. - */ -export class StoreProvider extends Component { - getChildContext() { - const { store } = this.props; - return { store }; - } - - render() { - return this.props.children; - } -} diff --git a/tgui/packages/tgui/store.ts b/tgui/packages/tgui/store.ts new file mode 100644 index 0000000000000..b18e611bcd414 --- /dev/null +++ b/tgui/packages/tgui/store.ts @@ -0,0 +1,111 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { Middleware, Reducer, Store, applyMiddleware, combineReducers, createStore } from 'common/redux'; +import { backendMiddleware, backendReducer } from './backend'; +import { debugMiddleware, debugReducer, relayMiddleware } from './debug'; + +import { Component } from 'inferno'; +import { assetMiddleware } from './assets'; +import { createLogger } from './logging'; +import { flow } from 'common/fp'; + +type ConfigureStoreOptions = { + sideEffects?: boolean; + reducer?: Reducer; + middleware?: { + pre?: Middleware[]; + post?: Middleware[]; + }; +}; + +type StackAugmentor = (stack: string, error?: Error) => string; + +type StoreProviderProps = { + store: Store; + children: any; +}; + +const logger = createLogger('store'); + +export const configureStore = (options: ConfigureStoreOptions = {}): Store => { + const { sideEffects = true, reducer, middleware } = options; + const rootReducer: Reducer = flow([ + combineReducers({ + debug: debugReducer, + backend: backendReducer, + }), + reducer, + ]); + + const middlewares: Middleware[] = !sideEffects + ? [] + : [...(middleware?.pre || []), assetMiddleware, backendMiddleware, ...(middleware?.post || [])]; + + if (process.env.NODE_ENV !== 'production') { + // We are using two if statements because Webpack is capable of + // removing this specific block as dead code. + if (sideEffects) { + middlewares.unshift(loggingMiddleware, debugMiddleware, relayMiddleware); + } + } + + const enhancer = applyMiddleware(...middlewares); + const store = createStore(rootReducer, enhancer); + + // Globals + window.__store__ = store; + window.__augmentStack__ = createStackAugmentor(store); + + return store; +}; + +const loggingMiddleware: Middleware = (store) => (next) => (action) => { + const { type } = action; + logger.debug('action', type === 'update' || type === 'backend/update' ? { type } : action); + return next(action); +}; + +/** + * Creates a function, which can be assigned to window.__augmentStack__ + * to augment reported stack traces with useful data for debugging. + */ +const createStackAugmentor = + (store: Store): StackAugmentor => + (stack, error) => { + error = error || new Error(stack.split('\n')[0]); + error.stack = error.stack || stack; + + logger.log('FatalError:', error); + const state = store.getState(); + const config = state?.backend?.config; + + return ( + stack + + '\nUser Agent: ' + + navigator.userAgent + + '\nState: ' + + JSON.stringify({ + ckey: config?.client?.ckey, + interface: config?.interface, + window: config?.window, + }) + ); + }; + +/** + * Store provider for Inferno apps. + */ +export class StoreProvider extends Component { + getChildContext() { + const { store } = this.props; + return { store }; + } + + render() { + return this.props.children; + } +} diff --git a/tgui/packages/tgui/stories/Blink.stories.js b/tgui/packages/tgui/stories/Blink.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Blink.stories.js rename to tgui/packages/tgui/stories/Blink.stories.jsx diff --git a/tgui/packages/tgui/stories/BlockQuote.stories.js b/tgui/packages/tgui/stories/BlockQuote.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/BlockQuote.stories.js rename to tgui/packages/tgui/stories/BlockQuote.stories.jsx diff --git a/tgui/packages/tgui/stories/Box.stories.js b/tgui/packages/tgui/stories/Box.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Box.stories.js rename to tgui/packages/tgui/stories/Box.stories.jsx diff --git a/tgui/packages/tgui/stories/Button.stories.js b/tgui/packages/tgui/stories/Button.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Button.stories.js rename to tgui/packages/tgui/stories/Button.stories.jsx diff --git a/tgui/packages/tgui/stories/ByondUi.stories.js b/tgui/packages/tgui/stories/ByondUi.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/ByondUi.stories.js rename to tgui/packages/tgui/stories/ByondUi.stories.jsx diff --git a/tgui/packages/tgui/stories/Collapsible.stories.js b/tgui/packages/tgui/stories/Collapsible.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Collapsible.stories.js rename to tgui/packages/tgui/stories/Collapsible.stories.jsx diff --git a/tgui/packages/tgui/stories/Flex.stories.js b/tgui/packages/tgui/stories/Flex.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Flex.stories.js rename to tgui/packages/tgui/stories/Flex.stories.jsx diff --git a/tgui/packages/tgui/stories/Input.stories.js b/tgui/packages/tgui/stories/Input.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Input.stories.js rename to tgui/packages/tgui/stories/Input.stories.jsx diff --git a/tgui/packages/tgui/stories/Popper.stories.js b/tgui/packages/tgui/stories/Popper.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Popper.stories.js rename to tgui/packages/tgui/stories/Popper.stories.jsx diff --git a/tgui/packages/tgui/stories/ProgressBar.stories.js b/tgui/packages/tgui/stories/ProgressBar.stories.js deleted file mode 100644 index bd2ce2455f655..0000000000000 --- a/tgui/packages/tgui/stories/ProgressBar.stories.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @file - * @copyright 2021 Aleksej Komarov - * @license MIT - */ - -import { useLocalState } from '../backend'; -import { Box, Button, ProgressBar, Section } from '../components'; - -export const meta = { - title: 'ProgressBar', - render: () => , -}; - -const Story = (props, context) => { - const [progress, setProgress] = useLocalState(context, 'progress', 0.5); - return ( -
- - Value: {Number(progress).toFixed(1)} - - -
- ); -}; diff --git a/tgui/packages/tgui/stories/ProgressBar.stories.jsx b/tgui/packages/tgui/stories/ProgressBar.stories.jsx new file mode 100644 index 0000000000000..dfee21bcdef26 --- /dev/null +++ b/tgui/packages/tgui/stories/ProgressBar.stories.jsx @@ -0,0 +1,47 @@ +/** + * @file + * @copyright 2021 Aleksej Komarov + * @license MIT + */ + +import { useLocalState } from '../backend'; +import { Box, Button, Input, LabeledList, ProgressBar, Section } from '../components'; + +export const meta = { + title: 'ProgressBar', + render: () => , +}; + +const Story = (props, context) => { + const [progress, setProgress] = useLocalState(context, 'progress', 0.5); + const [color, setColor] = useLocalState(context, 'color', ''); + + const color_data = color + ? { color: color } + : { + ranges: { + good: [0.5, Infinity], + bad: [-Infinity, 0.1], + average: [0, 0.5], + }, + }; + + return ( +
+ + Value: {Number(progress).toFixed(1)} + + + + +
+ ); +}; diff --git a/tgui/packages/tgui/stories/Stack.stories.js b/tgui/packages/tgui/stories/Stack.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Stack.stories.js rename to tgui/packages/tgui/stories/Stack.stories.jsx diff --git a/tgui/packages/tgui/stories/Storage.stories.js b/tgui/packages/tgui/stories/Storage.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Storage.stories.js rename to tgui/packages/tgui/stories/Storage.stories.jsx diff --git a/tgui/packages/tgui/stories/Tabs.stories.js b/tgui/packages/tgui/stories/Tabs.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Tabs.stories.js rename to tgui/packages/tgui/stories/Tabs.stories.jsx diff --git a/tgui/packages/tgui/stories/Themes.stories.js b/tgui/packages/tgui/stories/Themes.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Themes.stories.js rename to tgui/packages/tgui/stories/Themes.stories.jsx diff --git a/tgui/packages/tgui/stories/Tooltip.stories.js b/tgui/packages/tgui/stories/Tooltip.stories.jsx similarity index 100% rename from tgui/packages/tgui/stories/Tooltip.stories.js rename to tgui/packages/tgui/stories/Tooltip.stories.jsx diff --git a/tgui/packages/tgui/stories/common.js b/tgui/packages/tgui/stories/common.jsx similarity index 100% rename from tgui/packages/tgui/stories/common.js rename to tgui/packages/tgui/stories/common.jsx diff --git a/tgui/packages/tgui/styles/components/Dropdown.scss b/tgui/packages/tgui/styles/components/Dropdown.scss index bd670681e2be3..c8b4610b6b076 100644 --- a/tgui/packages/tgui/styles/components/Dropdown.scss +++ b/tgui/packages/tgui/styles/components/Dropdown.scss @@ -51,6 +51,11 @@ line-height: base.em(17px); transition: background-color 100ms ease-out; + &.selected { + background-color: rgba(255, 255, 255, 0.5) !important; + transition: background-color 0ms; + } + &:hover { background-color: rgba(255, 255, 255, 0.2); transition: background-color 0ms; diff --git a/tgui/packages/tgui/styles/components/ProgressBar.scss b/tgui/packages/tgui/styles/components/ProgressBar.scss index 611c65a7cc835..a544ee2e930e7 100644 --- a/tgui/packages/tgui/styles/components/ProgressBar.scss +++ b/tgui/packages/tgui/styles/components/ProgressBar.scss @@ -17,6 +17,8 @@ $bg-map: colors.$bg-map !default; position: relative; width: 100%; padding: 0 0.5em; + border-width: base.em(1px) !important; + border-style: solid !important; border-radius: $border-radius; background-color: $background-color; transition: border-color 900ms ease-out; @@ -50,7 +52,7 @@ $bg-map: colors.$bg-map !default; @each $color-name, $color-value in $bg-map { .ProgressBar--color--#{$color-name} { - border: base.em(1px) solid $color-value !important; + border-color: $color-value !important; .ProgressBar__fill { background-color: $color-value; diff --git a/tgui/public/tgui-polyfill.min.js b/tgui/public/tgui-polyfill.min.js index 40061479ec948..68c21147e6d93 100644 --- a/tgui/public/tgui-polyfill.min.js +++ b/tgui/public/tgui-polyfill.min.js @@ -1 +1 @@ -(function(window,document){var version="3.7.3";var options=window.html5||{};var reSkip=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;var saveClones=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;var supportsHtml5Styles;var expando="_html5shiv";var expanID=0;var expandoData={};var supportsUnknownElements;(function(){try{var a=document.createElement("a");a.innerHTML="";supportsHtml5Styles="hidden"in a;supportsUnknownElements=a.childNodes.length==1||function(){document.createElement("a");var frag=document.createDocumentFragment();return typeof frag.cloneNode=="undefined"||typeof frag.createDocumentFragment=="undefined"||typeof frag.createElement=="undefined"}()}catch(e){supportsHtml5Styles=true;supportsUnknownElements=true}})();function addStyleSheet(ownerDocument,cssText){var p=ownerDocument.createElement("p"),parent=ownerDocument.getElementsByTagName("head")[0]||ownerDocument.documentElement;p.innerHTML="x";return parent.insertBefore(p.lastChild,parent.firstChild)}function getElements(){var elements=html5.elements;return typeof elements=="string"?elements.split(" "):elements}function addElements(newElements,ownerDocument){var elements=html5.elements;if(typeof elements!="string"){elements=elements.join(" ")}if(typeof newElements!="string"){newElements=newElements.join(" ")}html5.elements=elements+" "+newElements;shivDocument(ownerDocument)}function getExpandoData(ownerDocument){var data=expandoData[ownerDocument[expando]];if(!data){data={};expanID++;ownerDocument[expando]=expanID;expandoData[expanID]=data}return data}function createElement(nodeName,ownerDocument,data){if(!ownerDocument){ownerDocument=document}if(supportsUnknownElements){return ownerDocument.createElement(nodeName)}if(!data){data=getExpandoData(ownerDocument)}var node;if(data.cache[nodeName]){node=data.cache[nodeName].cloneNode()}else if(saveClones.test(nodeName)){node=(data.cache[nodeName]=data.createElem(nodeName)).cloneNode()}else{node=data.createElem(nodeName)}return node.canHaveChildren&&!reSkip.test(nodeName)&&!node.tagUrn?data.frag.appendChild(node):node}function createDocumentFragment(ownerDocument,data){if(!ownerDocument){ownerDocument=document}if(supportsUnknownElements){return ownerDocument.createDocumentFragment()}data=data||getExpandoData(ownerDocument);var clone=data.frag.cloneNode(),i=0,elems=getElements(),l=elems.length;for(;i3?getModifier(init):null,key=String(init.key),chr=String(init.char),location=init.location,keyCode=init.keyCode||(init.keyCode=key)&&key.charCodeAt(0)||0,charCode=init.charCode||(init.charCode=chr)&&chr.charCodeAt(0)||0,bubbles=init.bubbles,cancelable=init.cancelable,repeat=init.repeat,locale=init.locale,view=init.view||window,args;if(!init.which)init.which=init.keyCode;if("initKeyEvent"in out){out.initKeyEvent(type,bubbles,cancelable,view,ctrlKey,altKey,shiftKey,metaKey,keyCode,charCode)}else if(0>>0);var proto=Element.prototype;var querySelector=proto.querySelector;var querySelectorAll=proto.querySelectorAll;proto.querySelector=function qS(css){return find(this,querySelector,css)};proto.querySelectorAll=function qSA(css){return find(this,querySelectorAll,css)};function find(node,method,css){node.setAttribute(dataScope,null);var result=method.call(node,String(css).replace(/(^|,\s*)(:scope([ >]|$))/g,(function($0,$1,$2,$3){return $1+"["+dataScope+"]"+($3||" ")})));node.removeAttribute(dataScope);return result}})()}})(window);(function(global){"use strict";var DOMMap=global.WeakMap||function(){var counter=0,dispatched=false,drop=false,value;function dispatch(key,ce,shouldDrop){drop=shouldDrop;dispatched=false;value=undefined;key.dispatchEvent(ce)}function Handler(value){this.value=value}Handler.prototype.handleEvent=function handleEvent(e){dispatched=true;if(drop){e.currentTarget.removeEventListener(e.type,this,false)}else{value=this.value}};function DOMMap(){counter++;this.__ce__=new Event("@DOMMap:"+counter+Math.random())}DOMMap.prototype={constructor:DOMMap,"delete":function del(key){return dispatch(key,this.__ce__,true),dispatched},get:function get(key){dispatch(key,this.__ce__,false);var v=value;value=undefined;return v},has:function has(key){return dispatch(key,this.__ce__,false),dispatched},set:function set(key,value){dispatch(key,this.__ce__,true);key.addEventListener(this.__ce__.type,new Handler(value),false);return this}};return DOMMap}();function Dict(){}Dict.prototype=(Object.create||Object)(null);function createEventListener(type,callback,options){function eventListener(e){if(eventListener.once){e.currentTarget.removeEventListener(e.type,callback,eventListener);eventListener.removed=true}if(eventListener.passive){e.preventDefault=createEventListener.preventDefault}if(typeof eventListener.callback==="function"){eventListener.callback.call(this,e)}else if(eventListener.callback){eventListener.callback.handleEvent(e)}if(eventListener.passive){delete e.preventDefault}}eventListener.type=type;eventListener.callback=callback;eventListener.capture=!!options.capture;eventListener.passive=!!options.passive;eventListener.once=!!options.once;eventListener.removed=false;return eventListener}createEventListener.preventDefault=function preventDefault(){};var Event=global.CustomEvent,dE=global.dispatchEvent,aEL=global.addEventListener,rEL=global.removeEventListener,counter=0,increment=function(){counter++},indexOf=[].indexOf||function indexOf(value){var length=this.length;while(length--){if(this[length]===value){break}}return length},getListenerKey=function(options){return"".concat(options.capture?"1":"0",options.passive?"1":"0",options.once?"1":"0")},augment;try{aEL("_",increment,{once:true});dE(new Event("_"));dE(new Event("_"));rEL("_",increment,{once:true})}catch(o_O){}if(counter!==1){(function(){var dm=new DOMMap;function createAEL(aEL){return function addEventListener(type,handler,options){if(options&&typeof options!=="boolean"){var info=dm.get(this),key=getListenerKey(options),i,tmp,wrap;if(!info)dm.set(this,info=new Dict);if(!(type in info))info[type]={handler:[],wrap:[]};tmp=info[type];i=indexOf.call(tmp.handler,handler);if(i<0){i=tmp.handler.push(handler)-1;tmp.wrap[i]=wrap=new Dict}else{wrap=tmp.wrap[i]}if(!(key in wrap)){wrap[key]=createEventListener(type,handler,options);aEL.call(this,type,wrap[key],wrap[key].capture)}}else{aEL.call(this,type,handler,options)}}}function createREL(rEL){return function removeEventListener(type,handler,options){if(options&&typeof options!=="boolean"){var info=dm.get(this),key,i,tmp,wrap;if(info&&type in info){tmp=info[type];i=indexOf.call(tmp.handler,handler);if(-1>>0;if(typeof callback!=="function"){throw new TypeError(callback+" is not a function")}if(arguments.length>1){T=thisArg}k=0;while(k