From 588d08df5aa682d3bd7f9fc0b8af38d22c3b36f8 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Tue, 19 Sep 2023 08:46:59 +0200 Subject: [PATCH] [MIRROR] Typescript camera console [MDB IGNORE] (#23795) * Typescript camera console (#78412) ## About The Pull Request Refactors the camera console into typescript, including the secureye, removing the need for its custom css file ![KaBYZCTqG1](https://github.com/tgstation/tgstation/assets/42397676/4de52792-943c-4b2d-94e5-8ff10b678a91) ![Screenshot 2023-09-17 120550](https://github.com/tgstation/tgstation/assets/42397676/a5370678-ffc0-44b4-94b7-f3de95d8286e) ## Why It's Good For The Game Spruces up the UI ever so slightly and possibly fixes an issue #78009 Less code is better code ## Changelog :cl: refactor: Refactors the camera console UI. /:cl: * Typescript camera console --------- Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com> --- .../packages/tgui/interfaces/CameraConsole.js | 137 ------------ .../tgui/interfaces/CameraConsole.tsx | 199 ++++++++++++++++++ .../packages/tgui/interfaces/NtosSecurEye.tsx | 74 +------ .../tgui/styles/interfaces/CameraConsole.scss | 53 ----- tgui/packages/tgui/styles/main.scss | 1 - 5 files changed, 201 insertions(+), 263 deletions(-) delete mode 100644 tgui/packages/tgui/interfaces/CameraConsole.js create mode 100644 tgui/packages/tgui/interfaces/CameraConsole.tsx delete mode 100644 tgui/packages/tgui/styles/interfaces/CameraConsole.scss diff --git a/tgui/packages/tgui/interfaces/CameraConsole.js b/tgui/packages/tgui/interfaces/CameraConsole.js deleted file mode 100644 index 57cc34e77e1..00000000000 --- a/tgui/packages/tgui/interfaces/CameraConsole.js +++ /dev/null @@ -1,137 +0,0 @@ -import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; -import { classes } from 'common/react'; -import { createSearch } from 'common/string'; -import { useBackend, useLocalState } from '../backend'; -import { Button, ByondUi, Flex, Input, Section } from '../components'; -import { Window } from '../layouts'; - -/** - * Returns previous and next camera names relative to the currently - * active camera. - */ -export const prevNextCamera = (cameras, activeCamera) => { - if (!activeCamera) { - return []; - } - const index = cameras.findIndex( - (camera) => camera.name === activeCamera.name - ); - return [cameras[index - 1]?.name, cameras[index + 1]?.name]; -}; - -/** - * Camera selector. - * - * Filters cameras, applies search terms and sorts the alphabetically. - */ -export const selectCameras = (cameras, searchText = '') => { - const testSearch = createSearch(searchText, (camera) => camera.name); - return flow([ - // Null camera filter - filter((camera) => camera?.name), - // Optional search term - searchText && filter(testSearch), - // Slightly expensive, but way better than sorting in BYOND - sortBy((camera) => camera.name), - ])(cameras); -}; - -export const CameraConsole = (props, context) => { - const { act, data } = useBackend(context); - const { mapRef, activeCamera } = data; - const cameras = selectCameras(data.cameras); - const [prevCameraName, nextCameraName] = prevNextCamera( - cameras, - activeCamera - ); - return ( - -
- - - -
-
-
- Camera: - {(activeCamera && activeCamera.name) || '—'} -
-
-
- -
-
- ); -}; - -export const CameraConsoleContent = (props, context) => { - const { act, data } = useBackend(context); - const [searchText, setSearchText] = useLocalState(context, 'searchText', ''); - const { activeCamera } = data; - const cameras = selectCameras(data.cameras, searchText); - return ( - - - setSearchText(value)} - /> - - -
- {cameras.map((camera) => ( - // We're not using the component here because performance - // would be absolutely abysmal (50+ ms for each re-render). -
- act('switch_camera', { - name: camera.name, - }) - }> - {camera.name} -
- ))} -
-
-
- ); -}; diff --git a/tgui/packages/tgui/interfaces/CameraConsole.tsx b/tgui/packages/tgui/interfaces/CameraConsole.tsx new file mode 100644 index 00000000000..b1077f6bdcb --- /dev/null +++ b/tgui/packages/tgui/interfaces/CameraConsole.tsx @@ -0,0 +1,199 @@ +import { filter, sortBy } from 'common/collections'; +import { flow } from 'common/fp'; +import { BooleanLike, classes } from 'common/react'; +import { createSearch } from 'common/string'; +import { useBackend, useLocalState } from '../backend'; +import { Button, ByondUi, Input, NoticeBox, Section, Stack } from '../components'; +import { Window } from '../layouts'; + +type Data = { + can_spy: BooleanLike; + mapRef: string; + cameras: Camera[]; + activeCamera: Camera & { status: BooleanLike }; + network: string[]; +}; + +type Camera = { + name: string; +}; + +/** + * Returns previous and next camera names relative to the currently + * active camera. + */ +const prevNextCamera = ( + cameras: Camera[], + activeCamera: Camera & { status: BooleanLike } +) => { + if (!activeCamera) { + return []; + } + const index = cameras.findIndex( + (camera) => camera?.name === activeCamera.name + ); + return [cameras[index - 1]?.name, cameras[index + 1]?.name]; +}; + +/** + * Camera selector. + * + * Filters cameras, applies search terms and sorts the alphabetically. + */ +const selectCameras = (cameras: Camera[], searchText = ''): Camera[] => { + const testSearch = createSearch(searchText, (camera: Camera) => camera.name); + + return flow([ + // Null camera filter + filter((camera: Camera) => !!camera?.name), + // Optional search term + searchText && filter(testSearch), + // Slightly expensive, but way better than sorting in BYOND + sortBy((camera: Camera) => camera.name), + ])(cameras); +}; + +export const CameraConsole = (props, context) => { + return ( + + + + + + ); +}; + +export const CameraContent = (props, context) => { + return ( + + + + + + + + + ); +}; + +const CameraSelector = (props, context) => { + const { act, data } = useBackend(context); + const [searchText, setSearchText] = useLocalState(context, 'searchText', ''); + const { activeCamera } = data; + const cameras = selectCameras(data.cameras, searchText); + + return ( + + + setSearchText(value)} + /> + + +
+ {cameras.map((camera) => ( + // We're not using the component here because performance + // would be absolutely abysmal (50+ ms for each re-render). +
+ act('switch_camera', { + name: camera.name, + }) + }> + {camera.name} +
+ ))} +
+
+
+ ); +}; + +const CameraControls = (props, context) => { + const { act, data } = useBackend(context); + const { activeCamera, can_spy, mapRef } = data; + const cameras = selectCameras(data.cameras); + + const [prevCameraName, nextCameraName] = prevNextCamera( + cameras, + activeCamera + ); + + return ( +
+ + + + + {activeCamera?.name ? ( + {activeCamera.name} + ) : ( + No input signal + )} + + + + {!!can_spy && ( +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/NtosSecurEye.tsx b/tgui/packages/tgui/interfaces/NtosSecurEye.tsx index 84e3be0dd6c..85d41767f6e 100644 --- a/tgui/packages/tgui/interfaces/NtosSecurEye.tsx +++ b/tgui/packages/tgui/interfaces/NtosSecurEye.tsx @@ -1,82 +1,12 @@ -import { useBackend } from '../backend'; -import { Button, ByondUi } from '../components'; import { NtosWindow } from '../layouts'; -import { prevNextCamera, selectCameras, CameraConsoleContent } from './CameraConsole'; - -type Data = { - mapRef: string; - can_spy: boolean; - activeCamera: Camera; - cameras: Camera[]; -}; - -type Camera = { - name: string; -}; +import { CameraContent } from './CameraConsole'; export const NtosSecurEye = (props, context) => { return ( -
- -
- +
); }; - -/** Displays info and controls for the current camera */ -const CameraControls = (props, context) => { - const { act, data } = useBackend(context); - const { can_spy, activeCamera, mapRef } = data; - const cameras = selectCameras(data.cameras); - const [prevCameraName, nextCameraName] = prevNextCamera( - cameras, - activeCamera - ); - - return ( -
-
- Camera: - {(activeCamera && activeCamera.name) || '—'} -
-
- {can_spy && ( -
- -
- ); -}; diff --git a/tgui/packages/tgui/styles/interfaces/CameraConsole.scss b/tgui/packages/tgui/styles/interfaces/CameraConsole.scss deleted file mode 100644 index 7e8f99fdcce..00000000000 --- a/tgui/packages/tgui/styles/interfaces/CameraConsole.scss +++ /dev/null @@ -1,53 +0,0 @@ -@use '../base.scss'; - -$background-color: rgba(0, 0, 0, 0.33) !default; - -.CameraConsole__left { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: base.em(220px); -} - -.CameraConsole__right { - position: absolute; - top: 0; - bottom: 0; - left: base.em(220px); - right: 0; - background-color: $background-color; -} - -.CameraConsole__toolbar { - position: absolute; - top: 0; - left: 0; - right: 0; - height: 2em; - line-height: 2em; - margin: 0.25em 1em 0; -} - -.CameraConsole__toolbarRight { - position: absolute; - top: 0; - right: 0; - height: 2em; - line-height: 2em; - margin: 0.33em 0.5em 0; -} - -.CameraConsole__map { - position: absolute; - top: base.em(26px); - bottom: 0; - left: 0; - right: 0; - margin: 0.5em; - text-align: center; - - .NoticeBox { - margin-top: calc(50% - 2em); - } -} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index b6c5ef4486d..25443ae606c 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -48,7 +48,6 @@ // Interfaces @include meta.load-css('./interfaces/AlertModal.scss'); -@include meta.load-css('./interfaces/CameraConsole.scss'); @include meta.load-css('./interfaces/Changelog.scss'); @include meta.load-css('./interfaces/CrewManifest.scss'); @include meta.load-css('./interfaces/Emojipedia.scss');