[MIRROR] TGUI 5.0 Patch 1 (#7701)

Co-authored-by: Selis <sirlionfur@hotmail.de>
This commit is contained in:
CHOMPStation2
2024-02-08 07:31:06 -07:00
committed by GitHub
parent 103514bd40
commit eebf92d66f
121 changed files with 1546 additions and 2676 deletions

View File

@@ -4,5 +4,17 @@
/**/*.chunk.* /**/*.chunk.*
/**/*.hot-update.* /**/*.hot-update.*
**.lock
**.log
**.json
**.svg
**.scss
**.md
**.css
**.txt
**.woff2
**.eot
**.ttf
# CHOMPEdit - Until removed # CHOMPEdit - Until removed
/packages/tgui_ch/** /packages/tgui_ch/**

View File

@@ -1,43 +0,0 @@
rules:
## Enforce a maximum cyclomatic complexity allowed in a program
# complexity: [warn, { max: 25 }]
## Enforce consistent brace style for blocks
# brace-style: [warn, stroustrup, { allowSingleLine: false }]
## Enforce the consistent use of either backticks, double, or single quotes
# quotes: [warn, single, {
# avoidEscape: true,
# allowTemplateLiterals: true,
# }]
# react/jsx-closing-bracket-location: [warn, {
# selfClosing: after-props,
# nonEmpty: after-props,
# }]
# react/display-name: warn
## Radar
## ------------------------------------------------------
# radar/cognitive-complexity: warn
radar/max-switch-cases: warn
radar/no-all-duplicated-branches: warn
radar/no-collapsible-if: warn
radar/no-collection-size-mischeck: warn
radar/no-duplicate-string: warn
radar/no-duplicated-branches: warn
radar/no-element-overwrite: warn
radar/no-extra-arguments: warn
radar/no-identical-conditions: warn
radar/no-identical-expressions: warn
radar/no-identical-functions: warn
radar/no-inverted-boolean-check: warn
radar/no-one-iteration-loop: warn
radar/no-redundant-boolean: warn
radar/no-redundant-jump: warn
radar/no-same-line-conditional: warn
radar/no-small-switch: warn
radar/no-unused-collection: warn
radar/no-use-of-empty-return-value: warn
radar/no-useless-catch: warn
radar/prefer-immediate-return: warn
radar/prefer-object-literal: warn
radar/prefer-single-boolean-return: warn
radar/prefer-while: warn

1
tgui/.eslintrc-sonar.yml Normal file
View File

@@ -0,0 +1 @@
extends: 'plugin:sonarjs/recommended'

View File

@@ -11,14 +11,14 @@ env:
browser: true browser: true
node: true node: true
plugins: plugins:
- radar - sonarjs
- react - react
- unused-imports - unused-imports
- simple-import-sort
settings: settings:
react: react:
version: '16.10' version: '18.2'
rules: rules:
## Possible Errors ## Possible Errors
## ---------------------------------------- ## ----------------------------------------
## Enforce “for” loop update clause moving the counter in the right ## Enforce “for” loop update clause moving the counter in the right
@@ -309,13 +309,16 @@ rules:
## Enforce or disallow capitalization of the first letter of a comment ## Enforce or disallow capitalization of the first letter of a comment
# capitalized-comments: error # capitalized-comments: error
## Require or disallow trailing commas ## Require or disallow trailing commas
comma-dangle: [error, { comma-dangle: [
arrays: always-multiline, error,
objects: always-multiline, {
imports: always-multiline, arrays: always-multiline,
exports: always-multiline, objects: always-multiline,
functions: only-multiline, ## Optional on functions imports: always-multiline,
}] exports: always-multiline,
functions: only-multiline, ## Optional on functions
},
]
## Enforce consistent spacing before and after commas ## Enforce consistent spacing before and after commas
comma-spacing: [error, { before: false, after: true }] comma-spacing: [error, { before: false, after: true }]
## Enforce consistent comma style ## Enforce consistent comma style
@@ -350,15 +353,15 @@ rules:
## Enforce the location of arrow function bodies ## Enforce the location of arrow function bodies
# implicit-arrow-linebreak: error # implicit-arrow-linebreak: error
## Enforce consistent indentation ## Enforce consistent indentation
# indent: [warn, 2, { SwitchCase: 1 }] # indent: [error, 2, { SwitchCase: 1 }]
## Enforce the consistent use of either double or single quotes in JSX ## Enforce the consistent use of either double or single quotes in JSX
## attributes ## attributes
# jsx-quotes: [warn, prefer-double] # jsx-quotes: [error, prefer-double]
## Enforce consistent spacing between keys and values in object literal ## Enforce consistent spacing between keys and values in object literal
## properties ## properties
# key-spacing: [warn, { beforeColon: false, afterColon: true }] # key-spacing: [error, { beforeColon: false, afterColon: true }]
## Enforce consistent spacing before and after keywords ## Enforce consistent spacing before and after keywords
# keyword-spacing: [warn, { before: true, after: true }] # keyword-spacing: [error, { before: true, after: true }]
## Enforce position of line comments ## Enforce position of line comments
# line-comment-position: error # line-comment-position: error
## Enforce consistent linebreak style ## Enforce consistent linebreak style
@@ -370,8 +373,8 @@ rules:
## Enforce a maximum depth that blocks can be nested ## Enforce a maximum depth that blocks can be nested
# max-depth: error # max-depth: error
## Enforce a maximum line length ## Enforce a maximum line length
# max-len: [warn, { # max-len: [error, {
# code: 120, # code: 80,
# ## Ignore imports # ## Ignore imports
# ignorePattern: '^(import\s.+\sfrom\s|.*require\()', # ignorePattern: '^(import\s.+\sfrom\s|.*require\()',
# ignoreUrls: true, # ignoreUrls: true,
@@ -415,7 +418,7 @@ rules:
## Disallow mixed binary operators ## Disallow mixed binary operators
# no-mixed-operators: error # no-mixed-operators: error
## Disallow mixed spaces and tabs for indentation ## Disallow mixed spaces and tabs for indentation
no-mixed-spaces-and-tabs: warn # no-mixed-spaces-and-tabs: error
## Disallow use of chained assignment expressions ## Disallow use of chained assignment expressions
# no-multi-assign: error # no-multi-assign: error
## Disallow multiple empty lines ## Disallow multiple empty lines
@@ -441,7 +444,7 @@ rules:
## Disallow ternary operators when simpler alternatives exist ## Disallow ternary operators when simpler alternatives exist
# no-unneeded-ternary: error # no-unneeded-ternary: error
## Disallow whitespace before properties ## Disallow whitespace before properties
# no-whitespace-before-property: warn # no-whitespace-before-property: error
## Enforce the location of single-line statements ## Enforce the location of single-line statements
# nonblock-statement-body-position: error # nonblock-statement-body-position: error
## Enforce consistent line breaks inside braces ## Enforce consistent line breaks inside braces
@@ -458,7 +461,7 @@ rules:
## Require or disallow assignment operator shorthand where possible ## Require or disallow assignment operator shorthand where possible
# operator-assignment: error # operator-assignment: error
## Enforce consistent linebreak style for operators ## Enforce consistent linebreak style for operators
# operator-linebreak: [warn, before] # operator-linebreak: [error, before]
## Require or disallow padding within blocks ## Require or disallow padding within blocks
# padded-blocks: error # padded-blocks: error
## Require or disallow padding lines between statements ## Require or disallow padding lines between statements
@@ -483,7 +486,7 @@ rules:
## Enforce consistent spacing before blocks ## Enforce consistent spacing before blocks
space-before-blocks: [error, always] space-before-blocks: [error, always]
## Enforce consistent spacing before function definition opening parenthesis ## Enforce consistent spacing before function definition opening parenthesis
# space-before-function-paren: [warn, { # space-before-function-paren: [error, {
# anonymous: always, # anonymous: always,
# named: never, # named: never,
# asyncArrow: always, # asyncArrow: always,
@@ -696,7 +699,7 @@ rules:
react/jsx-closing-tag-location: error react/jsx-closing-tag-location: error
## Enforce or disallow newlines inside of curly braces in JSX attributes and ## Enforce or disallow newlines inside of curly braces in JSX attributes and
## expressions (fixable) ## expressions (fixable)
# react/jsx-curly-newline: warn # react/jsx-curly-newline: error
## Enforce or disallow spaces inside of curly braces in JSX attributes and ## Enforce or disallow spaces inside of curly braces in JSX attributes and
## expressions (fixable) ## expressions (fixable)
react/jsx-curly-spacing: error react/jsx-curly-spacing: error
@@ -709,13 +712,13 @@ rules:
## Enforce event handler naming conventions in JSX ## Enforce event handler naming conventions in JSX
react/jsx-handler-names: error react/jsx-handler-names: error
## Validate JSX indentation (fixable) ## Validate JSX indentation (fixable)
# react/jsx-indent: [warn, 2, { # react/jsx-indent: [error, 2, {
# checkAttributes: true, # checkAttributes: true,
# }] # }]
## Validate props indentation in JSX (fixable) ## Validate props indentation in JSX (fixable)
# react/jsx-indent-props: [warn, 2] # react/jsx-indent-props: [error, 2]
## Validate JSX has key prop when in array or iterator ## Validate JSX has key prop when in array or iterator
# react/jsx-key: warn react/jsx-key: error
## Validate JSX maximum depth ## Validate JSX maximum depth
react/jsx-max-depth: [error, { max: 10 }] ## Generous react/jsx-max-depth: [error, { max: 10 }] ## Generous
## Limit maximum of props on a single line in JSX (fixable) ## Limit maximum of props on a single line in JSX (fixable)
@@ -762,3 +765,6 @@ rules:
## Prevents the use of unused imports. ## Prevents the use of unused imports.
## This could be done by enabling no-unused-vars, but we're doing this for now ## This could be done by enabling no-unused-vars, but we're doing this for now
unused-imports/no-unused-imports: error unused-imports/no-unused-imports: error
## https://github.com/lydell/eslint-plugin-simple-import-sort/
simple-import-sort/imports: error
simple-import-sort/exports: error

1
tgui/.gitignore vendored
View File

@@ -17,6 +17,7 @@ package-lock.json
/public/*.map /public/*.map
/public/tgui-bench.bundle.js /public/tgui-bench.bundle.js
/public/tgui-bench.bundle.css /public/tgui-bench.bundle.css
/coverage
## Previously ignored locations that are kept to avoid confusing git ## Previously ignored locations that are kept to avoid confusing git
## while transitioning to a new project structure. ## while transitioning to a new project structure.

15
tgui/.swcrc Normal file
View File

@@ -0,0 +1,15 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"jsc": {
"loose": true,
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"react": {
"runtime": "automatic"
}
}
}
}

View File

@@ -740,9 +740,10 @@ Popper lets you position elements so that they don't go out of the bounds of the
**Props:** **Props:**
- `popperContent: ReactNode` - The content that will be put inside the popper. - `content: ReactNode` - The content that will be put inside the popper.
- `options?: { ... }` - An object of options to pass to `createPopper`. See [https://popper.js.org/docs/v2/constructors/#options], but the one you want most is `placement`. Valid placements are "bottom", "top", "left", and "right". You can affix "-start" and "-end" to achieve something like top left or top right respectively. You can also use "auto" (with an optional "-start" or "-end"), where a best fit will be chosen. - `isOpen: boolean` - Whether or not the popper is open.
- `additionalStyles: { ... }` - A map of CSS styles to add to the element that will contain the popper. - `onClickOutside?: (e) => void` - A function that will be called when the user clicks outside of the popper.
- `placement?: string` - The placement of the popper. See [https://popper.js.org/docs/v2/constructors/#placement]
### `ProgressBar` ### `ProgressBar`

View File

@@ -8,7 +8,7 @@ module.exports = {
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
testRunner: require.resolve('jest-circus/runner'), testRunner: require.resolve('jest-circus/runner'),
transform: { transform: {
'^.+\\.(js|cjs|ts|tsx)$': require.resolve('babel-jest'), '^.+\\.(js|cjs|ts|tsx)$': require.resolve('@swc/jest'),
}, },
moduleFileExtensions: ['js', 'cjs', 'ts', 'tsx', 'json'], moduleFileExtensions: ['js', 'cjs', 'ts', 'tsx', 'json'],
resetMocks: true, resetMocks: true,

View File

@@ -9,41 +9,33 @@
"scripts": { "scripts": {
"tgui:analyze": "webpack --analyze", "tgui:analyze": "webpack --analyze",
"tgui:bench": "webpack --env TGUI_BENCH=1 && node packages/tgui-bench/index.js", "tgui:bench": "webpack --env TGUI_BENCH=1 && node packages/tgui-bench/index.js",
"tgui:build": "webpack", "tgui:build": "BROWSERSLIST_IGNORE_OLD_DATA=true webpack",
"tgui:dev": "node --experimental-modules packages/tgui-dev-server/index.js", "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,.ts,.tsx",
"tgui:prettier": "prettierx --check .", "tgui:prettier": "prettierx --check .",
"tgui:sonar": "eslint packages --ext .js,.cjs,.ts,.tsx -c .eslintrc-harder.yml", "tgui:sonar": "eslint packages -c .eslintrc-sonar.yml",
"tgui:test": "jest --watch", "tgui:test": "jest --watch",
"tgui:test-simple": "CI=true jest --color", "tgui:test-simple": "CI=true jest --color",
"tgui:test-ci": "CI=true jest --color --collect-coverage", "tgui:test-ci": "CI=true jest --color --collect-coverage",
"tgui:tsc": "tsc" "tgui:tsc": "tsc"
}, },
"dependencies": { "dependencies": {
"@babel/core": "^7.23.3", "@swc/core": "^1.3.100",
"@babel/eslint-parser": "^7.23.3", "@swc/jest": "^0.2.29",
"@babel/plugin-transform-class-properties": "^7.23.3",
"@babel/plugin-transform-jscript": "^7.23.3",
"@babel/preset-env": "^7.23.3",
"@babel/preset-react": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@types/jest": "^29.5.10", "@types/jest": "^29.5.10",
"@types/jsdom": "^21.1.6", "@types/jsdom": "^21.1.6",
"@types/node": "^14.x", "@types/node": "^14.x",
"@types/webpack": "^5.28.5", "@types/webpack": "^5.28.5",
"@types/webpack-env": "^1.18.4", "@types/webpack-env": "^1.18.4",
"@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/parser": "^6.14.0",
"babel-jest": "^29.7.0",
"babel-loader": "^8.3.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"common": "workspace:*",
"css-loader": "^6.8.1", "css-loader": "^6.8.1",
"esbuild-loader": "^4.0.2", "esbuild-loader": "^4.0.2",
"eslint": "^7.32.0", "eslint": "^8.56.0",
"eslint-config-prettier": "^9.0.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-radar": "^0.2.1",
"eslint-plugin-react": "^7.33.2", "eslint-plugin-react": "^7.33.2",
"eslint-plugin-unused-imports": "^1.1.5", "eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-sonarjs": "^0.23.0",
"eslint-plugin-unused-imports": "^3.0.0",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-circus": "^29.7.0", "jest-circus": "^29.7.0",
@@ -54,6 +46,7 @@
"sass": "^1.69.5", "sass": "^1.69.5",
"sass-loader": "^13.3.2", "sass-loader": "^13.3.2",
"style-loader": "^3.3.3", "style-loader": "^3.3.3",
"swc-loader": "^0.2.3",
"typescript": "^4.9.4", "typescript": "^4.9.4",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"webpack": "^5.89.0", "webpack": "^5.89.0",

View File

@@ -1,4 +1,4 @@
import { Action, Reducer, applyMiddleware, combineReducers, createAction, createStore } from './redux'; import { Action, applyMiddleware, combineReducers, createAction, createStore, Reducer } from './redux';
// Dummy Reducer // Dummy Reducer
const counterReducer: Reducer<number, Action<string>> = (state = 0, action) => { const counterReducer: Reducer<number, Action<string>> = (state = 0, action) => {

View File

@@ -4,8 +4,10 @@
* @license MIT * @license MIT
*/ */
import { setupGlobalEvents } from 'tgui/events';
import 'tgui/styles/main.scss'; import 'tgui/styles/main.scss';
import { setupGlobalEvents } from 'tgui/events';
import Benchmark from './lib/benchmark'; import Benchmark from './lib/benchmark';
const sendMessage = (obj: any) => { const sendMessage = (obj: any) => {

View File

@@ -1,8 +1,7 @@
import { configureStore } from 'tgui/store';
import { DisposalBin } from 'tgui/interfaces/DisposalBin';
import { backendUpdate, setGlobalStore } from 'tgui/backend'; import { backendUpdate, setGlobalStore } from 'tgui/backend';
import { DisposalBin } from 'tgui/interfaces/DisposalBin';
import { createRenderer } from 'tgui/renderer'; import { createRenderer } from 'tgui/renderer';
import { configureStore } from 'tgui/store';
const store = configureStore({ sideEffects: false }); const store = configureStore({ sideEffects: false });

View File

@@ -6,8 +6,9 @@
import { exec } from 'child_process'; import { exec } from 'child_process';
import { promisify } from 'util'; import { promisify } from 'util';
import { createLogger } from './logging';
import { require } from './require'; import { createLogger } from './logging.js';
import { require } from './require.js';
const axios = require('axios'); const axios = require('axios');
const logger = createLogger('dreamseeker'); const logger = createLogger('dreamseeker');

View File

@@ -4,8 +4,8 @@
* @license MIT * @license MIT
*/ */
import { createCompiler } from './webpack'; import { reloadByondCache } from './reloader.js';
import { reloadByondCache } from './reloader'; import { createCompiler } from './webpack.js';
const noHot = process.argv.includes('--no-hot'); const noHot = process.argv.includes('--no-hot');
const noTmp = process.argv.includes('--no-tmp'); const noTmp = process.argv.includes('--no-tmp');

View File

@@ -31,11 +31,9 @@ const ensureConnection = () => {
}; };
} }
} }
};
if (process.env.NODE_ENV !== 'production') {
window.onunload = () => socket && socket.close(); window.onunload = () => socket && socket.close();
} };
const subscribe = (fn) => subscribers.push(fn); const subscribe = (fn) => subscribers.push(fn);
@@ -136,38 +134,38 @@ const sendLogEntry = (level, ns, ...args) => {
const setupHotReloading = () => { const setupHotReloading = () => {
if ( if (
// prettier-ignore process.env.NODE_ENV === 'production' ||
process.env.NODE_ENV !== 'production' !process.env.WEBPACK_HMR_ENABLED ||
&& process.env.WEBPACK_HMR_ENABLED !window.WebSocket
&& window.WebSocket
) { ) {
if (module.hot) { return;
ensureConnection(); }
sendLogEntry(0, null, 'setting up hot reloading'); if (module.hot) {
subscribe((msg) => { ensureConnection();
const { type } = msg; sendLogEntry(0, null, 'setting up hot reloading');
sendLogEntry(0, null, 'received', type); subscribe((msg) => {
if (type === 'hotUpdate') { const { type } = msg;
const status = module.hot.status(); sendLogEntry(0, null, 'received', type);
if (status !== 'idle') { if (type === 'hotUpdate') {
sendLogEntry(0, null, 'hot reload status:', status); const status = module.hot.status();
return; if (status !== 'idle') {
} sendLogEntry(0, null, 'hot reload status:', status);
module.hot return;
.check({
ignoreUnaccepted: true,
ignoreDeclined: true,
ignoreErrored: true,
})
.then((modules) => {
sendLogEntry(0, null, 'outdated modules', modules);
})
.catch((err) => {
sendLogEntry(0, null, 'reload error', err);
});
} }
}); module.hot
} .check({
ignoreUnaccepted: true,
ignoreDeclined: true,
ignoreErrored: true,
})
.then((modules) => {
sendLogEntry(0, null, 'outdated modules', modules);
})
.catch((err) => {
sendLogEntry(0, null, 'reload error', err);
});
}
});
} }
}; };

View File

@@ -6,9 +6,10 @@
import fs from 'fs'; import fs from 'fs';
import { basename } from 'path'; import { basename } from 'path';
import { createLogger } from '../logging';
import { require } from '../require'; import { createLogger } from '../logging.js';
import { resolveGlob } from '../util'; import { require } from '../require.js';
import { resolveGlob } from '../util.js';
const SourceMap = require('source-map'); const SourceMap = require('source-map');
const { parse: parseStackTrace } = require('stacktrace-parser'); const { parse: parseStackTrace } = require('stacktrace-parser');

View File

@@ -6,9 +6,10 @@
import http from 'http'; import http from 'http';
import { inspect } from 'util'; import { inspect } from 'util';
import { createLogger, directLog } from '../logging';
import { require } from '../require'; import { createLogger, directLog } from '../logging.js';
import { loadSourceMaps, retrace } from './retrace'; import { require } from '../require.js';
import { loadSourceMaps, retrace } from './retrace.js';
const WebSocket = require('ws'); const WebSocket = require('ws');

View File

@@ -7,10 +7,11 @@
import fs from 'fs'; import fs from 'fs';
import os from 'os'; import os from 'os';
import { basename } from 'path'; import { basename } from 'path';
import { DreamSeeker } from './dreamseeker';
import { createLogger } from './logging'; import { DreamSeeker } from './dreamseeker.js';
import { resolveGlob, resolvePath } from './util'; import { createLogger } from './logging.js';
import { regQuery } from './winreg'; import { resolveGlob, resolvePath } from './util.js';
import { regQuery } from './winreg.js';
const logger = createLogger('reloader'); const logger = createLogger('reloader');

View File

@@ -6,7 +6,8 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { require } from './require';
import { require } from './require.js';
const globPkg = require('glob'); const globPkg = require('glob');

View File

@@ -7,10 +7,11 @@
import fs from 'fs'; import fs from 'fs';
import { createRequire } from 'module'; import { createRequire } from 'module';
import { dirname } from 'path'; import { dirname } from 'path';
import { loadSourceMaps, setupLink } from './link/server';
import { createLogger } from './logging'; import { loadSourceMaps, setupLink } from './link/server.js';
import { reloadByondCache } from './reloader'; import { createLogger } from './logging.js';
import { resolveGlob } from './util'; import { reloadByondCache } from './reloader.js';
import { resolveGlob } from './util.js';
const logger = createLogger('webpack'); const logger = createLogger('webpack');

View File

@@ -8,7 +8,8 @@
import { exec } from 'child_process'; import { exec } from 'child_process';
import { promisify } from 'util'; import { promisify } from 'util';
import { createLogger } from './logging';
import { createLogger } from './logging.js';
const logger = createLogger('winreg'); const logger = createLogger('winreg');
@@ -35,8 +36,8 @@ export const regQuery = async (path, key) => {
logger.error('could not find the start of the key value'); logger.error('could not find the start of the key value');
return null; return null;
} }
const value = stdout.substring(indexOfValue + 4, indexOfEol);
return value; return stdout.substring(indexOfValue + 4, indexOfEol);
} catch (err) { } catch (err) {
logger.error(err); logger.error(err);
return null; return null;

View File

@@ -6,6 +6,7 @@
import { Button, Section, Stack } from 'tgui/components'; import { Button, Section, Stack } from 'tgui/components';
import { Pane } from 'tgui/layouts'; import { Pane } from 'tgui/layouts';
import { NowPlayingWidget, useAudio } from './audio'; import { NowPlayingWidget, useAudio } from './audio';
import { ChatPanel, ChatTabs } from './chat'; import { ChatPanel, ChatTabs } from './chat';
import { useGame } from './game'; import { useGame } from './game';

View File

@@ -4,7 +4,8 @@
* @license MIT * @license MIT
*/ */
import { useSelector, useDispatch } from 'tgui/backend'; import { useDispatch, useSelector } from 'tgui/backend';
import { selectAudio } from './selectors'; import { selectAudio } from './selectors';
export const useAudio = () => { export const useAudio = () => {

View File

@@ -5,6 +5,7 @@
*/ */
import { createAction } from 'common/redux'; import { createAction } from 'common/redux';
import { createPage } from './model'; import { createPage } from './model';
export const loadChat = createAction('chat/load'); export const loadChat = createAction('chat/load');

View File

@@ -4,11 +4,12 @@
* @license MIT * @license MIT
*/ */
import DOMPurify from 'dompurify';
import { storage } from 'common/storage'; import { storage } from 'common/storage';
import { loadSettings, updateSettings, addHighlightSetting, removeHighlightSetting, updateHighlightSetting } from '../settings/actions'; import DOMPurify from 'dompurify';
import { addHighlightSetting, loadSettings, removeHighlightSetting, updateHighlightSetting, updateSettings } from '../settings/actions';
import { selectSettings } from '../settings/selectors'; import { selectSettings } from '../settings/selectors';
import { addChatPage, changeChatPage, changeScrollTracking, loadChat, rebuildChat, moveChatPageLeft, moveChatPageRight, removeChatPage, saveChatToDisk, purgeChatMessageArchive, toggleAcceptedType, updateMessageCount } from './actions'; import { addChatPage, changeChatPage, changeScrollTracking, loadChat, moveChatPageLeft, moveChatPageRight, purgeChatMessageArchive, rebuildChat, removeChatPage, saveChatToDisk, toggleAcceptedType, updateMessageCount } from './actions';
import { MESSAGE_SAVE_INTERVAL } from './constants'; import { MESSAGE_SAVE_INTERVAL } from './constants';
import { createMessage, serializeMessage } from './model'; import { createMessage, serializeMessage } from './model';
import { chatRenderer } from './renderer'; import { chatRenderer } from './renderer';

View File

@@ -5,7 +5,8 @@
*/ */
import { createUuid } from 'common/uuid'; import { createUuid } from 'common/uuid';
import { MESSAGE_TYPES, MESSAGE_TYPE_INTERNAL } from './constants';
import { MESSAGE_TYPE_INTERNAL, MESSAGE_TYPES } from './constants';
export const canPageAcceptType = (page, type) => export const canPageAcceptType = (page, type) =>
type.startsWith(MESSAGE_TYPE_INTERNAL) || page.acceptedTypes[type]; type.startsWith(MESSAGE_TYPE_INTERNAL) || page.acceptedTypes[type];

View File

@@ -4,7 +4,7 @@
* @license MIT * @license MIT
*/ */
import { addChatPage, changeChatPage, loadChat, removeChatPage, moveChatPageLeft, moveChatPageRight, toggleAcceptedType, updateChatPage, updateMessageCount, changeScrollTracking } from './actions'; import { addChatPage, changeChatPage, changeScrollTracking, loadChat, moveChatPageLeft, moveChatPageRight, removeChatPage, toggleAcceptedType, updateChatPage, updateMessageCount } from './actions';
import { canPageAcceptType, createMainPage } from './model'; import { canPageAcceptType, createMainPage } from './model';
const mainPage = createMainPage(); const mainPage = createMainPage();

View File

@@ -5,6 +5,7 @@
*/ */
import { useSelector } from 'tgui/backend'; import { useSelector } from 'tgui/backend';
import { selectGame } from './selectors'; import { selectGame } from './selectors';
export const useGame = () => { export const useGame = () => {

View File

@@ -6,8 +6,8 @@
import { pingSoft, pingSuccess } from '../ping/actions'; import { pingSoft, pingSuccess } from '../ping/actions';
import { connectionLost, connectionRestored, roundRestarted } from './actions'; import { connectionLost, connectionRestored, roundRestarted } from './actions';
import { selectGame } from './selectors';
import { CONNECTION_LOST_AFTER } from './constants'; import { CONNECTION_LOST_AFTER } from './constants';
import { selectGame } from './selectors';
const withTimestamp = (action) => ({ const withTimestamp = (action) => ({
...action, ...action,

View File

@@ -11,11 +11,13 @@ import './styles/themes/vchatdark.scss';
import { perf } from 'common/perf'; import { perf } from 'common/perf';
import { combineReducers } from 'common/redux'; import { combineReducers } from 'common/redux';
import { setupHotReloading } from 'tgui-dev-server/link/client.cjs'; import { setGlobalStore } from 'tgui/backend';
import { setupGlobalEvents } from 'tgui/events'; import { setupGlobalEvents } from 'tgui/events';
import { captureExternalLinks } from 'tgui/links'; import { captureExternalLinks } from 'tgui/links';
import { createRenderer } from 'tgui/renderer'; import { createRenderer } from 'tgui/renderer';
import { configureStore } from 'tgui/store'; import { configureStore } from 'tgui/store';
import { setupHotReloading } from 'tgui-dev-server/link/client.cjs';
import { audioMiddleware, audioReducer } from './audio'; import { audioMiddleware, audioReducer } from './audio';
import { chatMiddleware, chatReducer } from './chat'; import { chatMiddleware, chatReducer } from './chat';
import { gameMiddleware, gameReducer } from './game'; import { gameMiddleware, gameReducer } from './game';
@@ -23,7 +25,6 @@ import { setupPanelFocusHacks } from './panelFocus';
import { pingMiddleware, pingReducer } from './ping'; import { pingMiddleware, pingReducer } from './ping';
import { settingsMiddleware, settingsReducer } from './settings'; import { settingsMiddleware, settingsReducer } from './settings';
import { telemetryMiddleware } from './telemetry'; import { telemetryMiddleware } from './telemetry';
import { setGlobalStore } from 'tgui/backend';
perf.mark('inception', window.performance?.timing?.navigationStart); perf.mark('inception', window.performance?.timing?.navigationStart);
perf.mark('init'); perf.mark('init');

View File

@@ -5,6 +5,7 @@
*/ */
import { clamp01, scale } from 'common/math'; import { clamp01, scale } from 'common/math';
import { pingFail, pingSuccess } from './actions'; import { pingFail, pingSuccess } from './actions';
import { PING_MAX_FAILS, PING_ROUNDTRIP_BEST, PING_ROUNDTRIP_WORST } from './constants'; import { PING_MAX_FAILS, PING_ROUNDTRIP_BEST, PING_ROUNDTRIP_WORST } from './constants';

View File

@@ -1,5 +1,6 @@
import { Button } from 'tgui/components';
import { useDispatch } from 'tgui/backend'; import { useDispatch } from 'tgui/backend';
import { Button } from 'tgui/components';
import { dismissWarning } from './game/actions'; import { dismissWarning } from './game/actions';
let url: string | null = null; let url: string | null = null;

View File

@@ -5,6 +5,7 @@
*/ */
import { createAction } from 'common/redux'; import { createAction } from 'common/redux';
import { createHighlightSetting } from './model'; import { createHighlightSetting } from './model';
export const updateSettings = createAction('settings/update'); export const updateSettings = createAction('settings/update');

View File

@@ -5,7 +5,8 @@
*/ */
import { useDispatch, useSelector } from 'tgui/backend'; import { useDispatch, useSelector } from 'tgui/backend';
import { updateSettings, toggleSettings } from './actions';
import { toggleSettings, updateSettings } from './actions';
import { selectSettings } from './selectors'; import { selectSettings } from './selectors';
export const useSettings = () => { export const useSettings = () => {

View File

@@ -5,10 +5,11 @@
*/ */
import { storage } from 'common/storage'; import { storage } from 'common/storage';
import { setClientTheme } from '../themes'; import { setClientTheme } from '../themes';
import { loadSettings, updateSettings, addHighlightSetting, removeHighlightSetting, updateHighlightSetting } from './actions'; import { addHighlightSetting, loadSettings, removeHighlightSetting, updateHighlightSetting, updateSettings } from './actions';
import { selectSettings } from './selectors';
import { FONTS_DISABLED } from './constants'; import { FONTS_DISABLED } from './constants';
import { selectSettings } from './selectors';
let overrideRule = null; let overrideRule = null;
let overrideFontFamily = null; let overrideFontFamily = null;

View File

@@ -4,9 +4,9 @@
* @license MIT * @license MIT
*/ */
import { changeSettingsTab, loadSettings, openChatSettings, toggleSettings, updateSettings, addHighlightSetting, removeHighlightSetting, updateHighlightSetting } from './actions'; import { addHighlightSetting, changeSettingsTab, loadSettings, openChatSettings, removeHighlightSetting, toggleSettings, updateHighlightSetting, updateSettings } from './actions';
import { FONTS, MAX_HIGHLIGHT_SETTINGS, SETTINGS_TABS } from './constants';
import { createDefaultHighlightSetting } from './model'; import { createDefaultHighlightSetting } from './model';
import { SETTINGS_TABS, FONTS, MAX_HIGHLIGHT_SETTINGS } from './constants';
const defaultHighlightSetting = createDefaultHighlightSetting(); const defaultHighlightSetting = createDefaultHighlightSetting();

View File

@@ -4,10 +4,10 @@
* @license MIT * @license MIT
*/ */
import { Action, AnyAction, Middleware } from '../common/redux';
import { Dispatch } from 'common/redux'; import { Dispatch } from 'common/redux';
import { Action, AnyAction, Middleware } from '../common/redux';
const EXCLUDED_PATTERNS = [/v4shim/i]; const EXCLUDED_PATTERNS = [/v4shim/i];
const loadedMappings: Record<string, string> = {}; const loadedMappings: Record<string, string> = {};

View File

@@ -13,6 +13,7 @@
import { perf } from 'common/perf'; import { perf } from 'common/perf';
import { createAction } from 'common/redux'; import { createAction } from 'common/redux';
import { setupDrag } from './drag'; import { setupDrag } from './drag';
import { globalEvents } from './events'; import { globalEvents } from './events';
import { focusMap } from './focus'; import { focusMap } from './focus';

View File

@@ -1,4 +1,5 @@
import { Component, createRef } from 'react'; import { Component, createRef } from 'react';
import { resolveAsset } from '../assets'; import { resolveAsset } from '../assets';
import { Box } from './Box'; import { Box } from './Box';

View File

@@ -6,6 +6,7 @@
import { BooleanLike, classes } from 'common/react'; import { BooleanLike, classes } from 'common/react';
import { createElement, ReactNode } from 'react'; import { createElement, ReactNode } from 'react';
import { CSS_COLORS } from '../constants'; import { CSS_COLORS } from '../constants';
export type BoxProps = { export type BoxProps = {

View File

@@ -6,6 +6,7 @@
import { map, zipWith } from 'common/collections'; import { map, zipWith } from 'common/collections';
import { Component, createRef, RefObject } from 'react'; import { Component, createRef, RefObject } from 'react';
import { Box, BoxProps } from './Box'; import { Box, BoxProps } from './Box';
type Props = { type Props = {

View File

@@ -1,337 +1,177 @@
import { createPopper, VirtualElement } from '@popperjs/core';
import { classes } from 'common/react'; import { classes } from 'common/react';
import { Component, ReactNode } from 'react'; import { ReactNode, useCallback, useEffect, useRef, useState } from 'react';
import { findDOMNode, render } from 'react-dom';
import { Box, BoxProps } from './Box'; import { BoxProps, unit } from './Box';
import { Button } from './Button'; import { Button } from './Button';
import { Icon } from './Icon'; import { Icon } from './Icon';
import { Stack } from './Stack'; import { Popper } from './Popper';
export interface DropdownEntry { type DropdownEntry = {
displayText: string | number | ReactNode; displayText: ReactNode;
value: string | number | Enumerator; value: string | number;
};
type DropdownOption = string | DropdownEntry;
type Props = {
/** An array of strings which will be displayed in the
dropdown when open. See Dropdown.tsx for more advanced usage with DropdownEntry */
options: DropdownOption[];
/** Called when a value is picked from the list, `value` is the value that was picked */
onSelected: (value: any) => void;
} & Partial<{
/** Whether to display previous / next buttons */
buttons: boolean;
/** Whether to clip the selected text */
clipSelectedText: boolean;
/** Color of dropdown button */
color: string;
/** Disables the dropdown */
disabled: boolean;
/** Text to always display in place of the selected text */
displayText: ReactNode;
/** Icon to display in dropdown button */
icon: string;
/** Angle of the icon */
iconRotation: number;
/** Whether or not the icon should spin */
iconSpin: boolean;
/** Width of the dropdown menu. Default: 15rem */
menuWidth: string;
/** Whether or not the arrow on the right hand side of the dropdown button is visible */
noChevron: boolean;
/** Called when dropdown button is clicked */
onClick: (event) => void;
/** Dropdown renders over instead of below */
over: boolean;
/** Currently selected entry */
selected: string | number;
}> &
BoxProps;
function getOptionValue(option: DropdownOption) {
return typeof option === 'string' ? option : option.value;
} }
type DropdownUniqueProps = { options: string[] | DropdownEntry[] } & Partial<{ export function Dropdown(props: Props) {
buttons: boolean; const {
clipSelectedText: boolean; buttons,
color: string; className,
disabled: boolean; clipSelectedText = true,
displayText: string | number | ReactNode; color = 'default',
dropdownStyle: any; disabled,
icon: string; displayText,
iconRotation: number; icon,
iconSpin: boolean; iconRotation,
menuWidth: string; iconSpin,
nochevron: boolean; menuWidth = '15rem',
onClick: (event) => void; noChevron,
onSelected: (selected: any) => void; onClick,
over: boolean; onSelected,
// you freaks really are just doing anything with this shit options = [],
selected: any; over,
width: string; selected,
}>; width,
} = props;
export type DropdownProps = BoxProps & DropdownUniqueProps; const [open, setOpen] = useState(false);
const adjustedOpen = over ? !open : open;
const innerRef = useRef<HTMLDivElement>(null);
const DEFAULT_OPTIONS = { /** Update the selected value when clicking the left/right buttons */
placement: 'left-start', const updateSelected = useCallback(
modifiers: [ (direction: 'previous' | 'next') => {
{ if (options.length < 1 || disabled) {
name: 'eventListeners', return;
enabled: false,
},
],
};
const NULL_RECT: DOMRect = {
width: 0,
height: 0,
top: 0,
right: 0,
bottom: 0,
left: 0,
x: 0,
y: 0,
toJSON: () => null,
} as const;
type DropdownState = {
selected?: string;
open: boolean;
};
const DROPDOWN_DEFAULT_CLASSNAMES = 'Layout Dropdown__menu';
const DROPDOWN_SCROLL_CLASSNAMES = 'Layout Dropdown__menu-scroll';
export class Dropdown extends Component<DropdownProps, DropdownState> {
static renderedMenu: HTMLDivElement | undefined;
static singletonPopper: ReturnType<typeof createPopper> | undefined;
static currentOpenMenu: Element | undefined;
static virtualElement: VirtualElement = {
getBoundingClientRect: () =>
Dropdown.currentOpenMenu?.getBoundingClientRect() ?? NULL_RECT,
};
menuContents: any;
state: DropdownState = {
open: false,
selected: this.props.selected,
};
handleClick = () => {
if (this.state.open) {
this.setOpen(false);
}
};
getDOMNode() {
// eslint-disable-next-line react/no-find-dom-node
return findDOMNode(this) as Element;
}
componentDidMount() {
const domNode = this.getDOMNode();
if (!domNode) {
return;
}
}
openMenu() {
let renderedMenu = Dropdown.renderedMenu;
if (renderedMenu === undefined) {
renderedMenu = document.createElement('div');
renderedMenu.className = DROPDOWN_DEFAULT_CLASSNAMES;
document.body.appendChild(renderedMenu);
Dropdown.renderedMenu = renderedMenu;
}
const domNode = this.getDOMNode()!;
Dropdown.currentOpenMenu = domNode;
renderedMenu.scrollTop = 0;
renderedMenu.style.width = this.props.menuWidth || '10rem';
renderedMenu.style.opacity = '1';
renderedMenu.style.pointerEvents = 'auto';
// ie hack
// ie has this bizarre behavior where focus just silently fails if the
// element being targeted "isn't ready"
// 400 is probably way too high, but the lack of hotloading is testing my
// patience on tuning it
// I'm beyond giving a shit at this point it fucking works whatever
setTimeout(() => {
Dropdown.renderedMenu?.focus();
}, 400);
this.renderMenuContent();
}
closeMenu() {
if (Dropdown.currentOpenMenu !== this.getDOMNode()) {
return;
}
Dropdown.currentOpenMenu = undefined;
Dropdown.renderedMenu!.style.opacity = '0';
Dropdown.renderedMenu!.style.pointerEvents = 'none';
}
componentWillUnmount() {
this.closeMenu();
this.setOpen(false);
}
renderMenuContent() {
const renderedMenu = Dropdown.renderedMenu;
if (!renderedMenu) {
return;
}
if (renderedMenu.offsetHeight > 200) {
renderedMenu.className = DROPDOWN_SCROLL_CLASSNAMES;
} else {
renderedMenu.className = DROPDOWN_DEFAULT_CLASSNAMES;
}
const { options = [] } = this.props;
const ops = options.map((option) => {
let value, displayText;
if (typeof option === 'string') {
displayText = option;
value = option;
} else if (option !== null) {
displayText = option.displayText;
value = option.value;
} }
const startIndex = 0;
const endIndex = options.length - 1;
return ( let selectedIndex = options.findIndex(
<div (option) => getOptionValue(option) === selected
key={value}
className={classes([
'Dropdown__menuentry',
this.state.selected === value && 'selected',
])}
onClick={() => {
this.setSelected(value);
}}>
{displayText}
</div>
); );
});
const to_render = ops.length ? ops : 'No Options Found'; if (selectedIndex < 0) {
selectedIndex = direction === 'next' ? endIndex : startIndex;
render(<div>{to_render}</div>, renderedMenu, () => {
let singletonPopper = Dropdown.singletonPopper;
if (singletonPopper === undefined) {
singletonPopper = createPopper(Dropdown.virtualElement, renderedMenu!, {
...DEFAULT_OPTIONS,
placement: 'bottom-start',
});
Dropdown.singletonPopper = singletonPopper;
} else {
singletonPopper.setOptions({
...DEFAULT_OPTIONS,
placement: 'bottom-start',
});
singletonPopper.update();
} }
});
}
setOpen(open: boolean) { let newIndex = selectedIndex;
this.setState((state) => ({ if (direction === 'next') {
...state, newIndex = selectedIndex === endIndex ? startIndex : selectedIndex++;
open, } else {
})); newIndex = selectedIndex === startIndex ? endIndex : selectedIndex--;
if (open) { }
setTimeout(() => {
this.openMenu();
window.addEventListener('click', this.handleClick);
});
} else {
this.closeMenu();
window.removeEventListener('click', this.handleClick);
}
}
setSelected(selected: string) { onSelected?.(getOptionValue(options[newIndex]));
this.setState((state) => ({ },
...state, [disabled, onSelected, options, selected]
selected, );
}));
this.setOpen(false);
if (this.props.onSelected) {
this.props.onSelected(selected);
}
}
getOptionValue(option): string { /** Allows the menu to be scrollable on open */
return typeof option === 'string' ? option : option.value; useEffect(() => {
} if (!open) return;
getSelectedIndex(): number { innerRef.current?.focus();
const selected = this.state.selected || this.props.selected; }, [open]);
const { options = [] } = this.props;
return options.findIndex((option) => { return (
return this.getOptionValue(option) === selected; <Popper
}); isOpen={open}
} onClickOutside={() => setOpen(false)}
placement={over ? 'top-start' : 'bottom-start'}
content={
<div
className="Layout Dropdown__menu"
style={{ minWidth: menuWidth }}
ref={innerRef}>
{options.length === 0 && (
<div className="Dropdown__menuentry">No options</div>
)}
toPrevious(): void { {options.map((option, index) => {
if (this.props.options.length < 1) { const value = getOptionValue(option);
return;
}
let selectedIndex = this.getSelectedIndex(); return (
const startIndex = 0; <div
const endIndex = this.props.options.length - 1; className={classes([
'Dropdown__menuentry',
const hasSelected = selectedIndex >= 0; selected === value && 'selected',
if (!hasSelected) { ])}
selectedIndex = startIndex; key={index}
} onClick={() => {
setOpen(false);
const previousIndex = onSelected?.(value);
selectedIndex === startIndex ? endIndex : selectedIndex - 1; }}>
{typeof option === 'string' ? option : option.displayText}
this.setSelected(this.getOptionValue(this.props.options[previousIndex])); </div>
} );
})}
toNext(): void { </div>
if (this.props.options.length < 1) { }>
return; <div>
} <div className="Dropdown" style={{ width: unit(width) }}>
<div
let selectedIndex = this.getSelectedIndex();
const startIndex = 0;
const endIndex = this.props.options.length - 1;
const hasSelected = selectedIndex >= 0;
if (!hasSelected) {
selectedIndex = endIndex;
}
const nextIndex =
selectedIndex === endIndex ? startIndex : selectedIndex + 1;
this.setSelected(this.getOptionValue(this.props.options[nextIndex]));
}
render() {
const { props } = this;
const {
icon,
iconRotation,
iconSpin,
clipSelectedText = true,
color = 'default',
dropdownStyle,
over,
nochevron,
width,
onClick,
onSelected,
selected,
disabled,
displayText,
buttons,
...boxProps
} = props;
const { className, ...rest } = boxProps;
const adjustedOpen = over ? !this.state.open : this.state.open;
return (
<Stack fill>
<Stack.Item width={width}>
<Box
width={'100%'}
className={classes([ className={classes([
'Dropdown__control', 'Dropdown__control',
'Button', 'Button',
'Button--dropdown',
'Button--color--' + color, 'Button--color--' + color,
disabled && 'Button--disabled', disabled && 'Button--disabled',
className, className,
])} ])}
onClick={(event) => { onClick={(event) => {
if (disabled && !this.state.open) { if (disabled && !open) {
return; return;
} }
this.setOpen(!this.state.open); setOpen(!open);
if (onClick) { onClick?.(event);
onClick(event); }}>
}
}}
{...rest}>
{icon && ( {icon && (
<Icon <Icon
mr={1}
name={icon} name={icon}
rotation={iconRotation} rotation={iconRotation}
spin={iconSpin} spin={iconSpin}
mr={1}
/> />
)} )}
<span <span
@@ -339,48 +179,37 @@ export class Dropdown extends Component<DropdownProps, DropdownState> {
style={{ style={{
overflow: clipSelectedText ? 'hidden' : 'visible', overflow: clipSelectedText ? 'hidden' : 'visible',
}}> }}>
{displayText || this.state.selected} {displayText || selected}
</span> </span>
{nochevron || ( {!noChevron && (
<span className="Dropdown__arrow-button"> <span className="Dropdown__arrow-button">
<Icon name={adjustedOpen ? 'chevron-up' : 'chevron-down'} /> <Icon name={adjustedOpen ? 'chevron-up' : 'chevron-down'} />
</span> </span>
)} )}
</Box> </div>
</Stack.Item> {buttons && (
{buttons && ( <>
<>
<Stack.Item height={'100%'}>
<Button <Button
height={'100%'} disabled={disabled}
height={1.8}
icon="chevron-left" icon="chevron-left"
disabled={disabled}
onClick={() => { onClick={() => {
if (disabled) { updateSelected('previous');
return;
}
this.toPrevious();
}} }}
/> />
</Stack.Item>
<Stack.Item height={'100%'}>
<Button <Button
height={'100%'}
icon="chevron-right"
disabled={disabled} disabled={disabled}
height={1.8}
icon="chevron-right"
onClick={() => { onClick={() => {
if (disabled) { updateSelected('next');
return;
}
this.toNext();
}} }}
/> />
</Stack.Item> </>
</> )}
)} </div>
</Stack> </div>
); </Popper>
} );
} }

View File

@@ -5,6 +5,7 @@
*/ */
import { classes } from 'common/react'; import { classes } from 'common/react';
import { BoxProps, computeBoxClassName, computeBoxProps, unit } from './Box'; import { BoxProps, computeBoxClassName, computeBoxProps, unit } from './Box';
export type FlexProps = Partial<{ export type FlexProps = Partial<{

View File

@@ -8,6 +8,7 @@
import { classes } from 'common/react'; import { classes } from 'common/react';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { BoxProps, computeBoxClassName, computeBoxProps } from './Box'; import { BoxProps, computeBoxClassName, computeBoxProps } from './Box';
const FA_OUTLINE_REGEX = /-o$/; const FA_OUTLINE_REGEX = /-o$/;

View File

@@ -1,4 +1,5 @@
import { Component } from 'react'; import { Component } from 'react';
import { KeyEvent } from '../events'; import { KeyEvent } from '../events';
import { listenForKeyEvents } from '../hotkeys'; import { listenForKeyEvents } from '../hotkeys';

View File

@@ -6,8 +6,9 @@
import { classes } from 'common/react'; import { classes } from 'common/react';
import { Component, createRef, ReactNode, RefObject } from 'react'; import { Component, createRef, ReactNode, RefObject } from 'react';
import { Box } from './Box';
import { logger } from '../logging'; import { logger } from '../logging';
import { Box } from './Box';
import { Icon } from './Icon'; import { Icon } from './Icon';
type MenuProps = { type MenuProps = {

View File

@@ -1,82 +1,88 @@
import { createPopper } from '@popperjs/core'; import { Placement } from '@popperjs/core';
import { ArgumentsOf } from 'common/types'; import { PropsWithChildren, ReactNode, useEffect, useRef, useState } from 'react';
import { Component, CSSProperties, JSXElementConstructor, PropsWithChildren, ReactElement, RefObject } from 'react'; import { usePopper } from 'react-popper';
import { findDOMNode, render } from 'react-dom';
type PopperProps = { type RequiredProps = {
popperContent: ReactElement<any, string | JSXElementConstructor<any>>; /** The content to display in the popper */
options?: ArgumentsOf<typeof createPopper>[2]; content: ReactNode;
additionalStyles?: CSSProperties; /** Whether the popper is open */
} & PropsWithChildren; isOpen: boolean;
};
export class Popper extends Component<PopperProps> { type OptionalProps = Partial<{
static id: number = 0; /** Called when the user clicks outside the popper */
popperRef: RefObject<HTMLDivElement>; onClickOutside: () => void;
/** Where to place the popper relative to the reference element */
placement: Placement;
}>;
renderedContent: HTMLDivElement; type Props = RequiredProps & OptionalProps;
popperInstance: ReturnType<typeof createPopper>;
constructor(props) { /**
super(props); * ## Popper
* Popper lets you position elements so that they don't go out of the bounds of the window.
* @url https://popper.js.org/react-popper/ for more information.
*/
export function Popper(props: PropsWithChildren<Props>) {
const { children, content, isOpen, onClickOutside, placement } = props;
Popper.id += 1; const [referenceElement, setReferenceElement] =
useState<HTMLDivElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(
null
);
// One would imagine we could just use useref here, but it's against react-popper documentation and causes a positioning bug
// We still need them to call focus and clickoutside events :(
const popperRef = useRef<HTMLDivElement | null>(null);
const parentRef = useRef<HTMLDivElement | null>(null);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement,
});
/** Close the popper when the user clicks outside */
function handleClickOutside(event: MouseEvent) {
if (
!popperRef.current?.contains(event.target as Node) &&
!parentRef.current?.contains(event.target as Node)
) {
onClickOutside?.();
}
} }
componentDidMount() { useEffect(() => {
const { additionalStyles, options } = this.props; if (isOpen) {
document.addEventListener('mousedown', handleClickOutside);
this.renderedContent = document.createElement('div'); } else {
document.removeEventListener('mousedown', handleClickOutside);
if (additionalStyles) {
for (const [attribute, value] of Object.entries(additionalStyles)) {
this.renderedContent.style[attribute] = value;
}
} }
this.renderPopperContent(() => { return () => {
document.body.appendChild(this.renderedContent); document.removeEventListener('mousedown', handleClickOutside);
};
}, [isOpen]);
// HACK: We don't want to create a wrapper, as it could break the layout return (
// of consumers, so we use findDOMNode. <>
// This is usually bad as refs are usually better, but refs did <div
// not work in this case, as they weren't propagating correctly. ref={(node) => {
// A previous attempt was made as a render prop that passed an ID, setReferenceElement(node);
// but this made consuming use too unwieldly. parentRef.current = node;
// Because this component is written in TypeScript, we will know }}>
// immediately if this internal variable is removed. {children}
// </div>
// eslint-disable-next-line react/no-find-dom-node {isOpen && (
const domNode = findDOMNode(this) as Element; <div
if (!domNode) { ref={(node) => {
return; setPopperElement(node);
} popperRef.current = node;
}}
this.popperInstance = createPopper( style={{ ...styles.popper, zIndex: 5 }}
domNode, {...attributes.popper}>
this.renderedContent, {content}
options </div>
); )}
}); </>
} );
componentDidUpdate() {
this.renderPopperContent(() => this.popperInstance?.update());
}
componentWillUnmount() {
this.popperInstance?.destroy();
render(<> </>, this.renderedContent, () => {
this.renderedContent.remove();
});
}
renderPopperContent(callback: () => void) {
// `render` errors when given false, so we convert it to `null`,
// which is supported.
render(this.props.popperContent || null, this.renderedContent, callback);
}
render() {
return this.props.children;
}
} }

View File

@@ -4,12 +4,13 @@
* @license MIT * @license MIT
*/ */
import { clamp01, scale, keyOfMatchingRange, toFixed } from 'common/math'; import { clamp01, keyOfMatchingRange, scale, toFixed } from 'common/math';
import { classes } from 'common/react'; import { classes } from 'common/react';
import { BoxProps, computeBoxClassName, computeBoxProps } from './Box';
import { CSS_COLORS } from '../constants';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { CSS_COLORS } from '../constants';
import { BoxProps, computeBoxClassName, computeBoxProps } from './Box';
type Props = { type Props = {
value: number; value: number;
} & Partial<{ } & Partial<{

View File

@@ -5,7 +5,8 @@
*/ */
import { canRender, classes } from 'common/react'; import { canRender, classes } from 'common/react';
import { ReactNode, RefObject, createRef, useEffect } from 'react'; import { createRef, ReactNode, RefObject, useEffect } from 'react';
import { addScrollableNode, removeScrollableNode } from '../events'; import { addScrollableNode, removeScrollableNode } from '../events';
import { BoxProps, computeBoxClassName, computeBoxProps } from './Box'; import { BoxProps, computeBoxClassName, computeBoxProps } from './Box';

View File

@@ -6,6 +6,7 @@
import { classes } from 'common/react'; import { classes } from 'common/react';
import { RefObject } from 'react'; import { RefObject } from 'react';
import { computeFlexClassName, computeFlexItemClassName, computeFlexItemProps, computeFlexProps, FlexItemProps, FlexProps } from './Flex'; import { computeFlexClassName, computeFlexItemClassName, computeFlexItemProps, computeFlexProps, FlexItemProps, FlexProps } from './Flex';
type Props = Partial<{ type Props = Partial<{

View File

@@ -1,4 +1,5 @@
import { PropsWithChildren, ReactNode } from 'react'; import { PropsWithChildren, ReactNode } from 'react';
import { Box } from './Box'; import { Box } from './Box';
type Props = Partial<{ type Props = Partial<{

View File

@@ -6,6 +6,7 @@
import { canRender, classes } from 'common/react'; import { canRender, classes } from 'common/react';
import { PropsWithChildren, ReactNode } from 'react'; import { PropsWithChildren, ReactNode } from 'react';
import { BoxProps, computeBoxClassName, computeBoxProps } from './Box'; import { BoxProps, computeBoxClassName, computeBoxProps } from './Box';
import { Icon } from './Icon'; import { Icon } from './Icon';

View File

@@ -1,3 +1,5 @@
/* eslint-disable react/no-deprecated */
// TODO: Rewrite as an FC, remove this lint disable
import { createPopper, Placement, VirtualElement } from '@popperjs/core'; import { createPopper, Placement, VirtualElement } from '@popperjs/core';
import { Component, ReactNode } from 'react'; import { Component, ReactNode } from 'react';
import { findDOMNode, render } from 'react-dom'; import { findDOMNode, render } from 'react-dom';

View File

@@ -14,12 +14,13 @@ export { ByondUi } from './ByondUi';
export { Chart } from './Chart'; export { Chart } from './Chart';
export { Collapsible } from './Collapsible'; export { Collapsible } from './Collapsible';
export { ColorBox } from './ColorBox'; export { ColorBox } from './ColorBox';
export { Dialog } from './Dialog';
export { Dimmer } from './Dimmer'; export { Dimmer } from './Dimmer';
export { Divider } from './Divider'; export { Divider } from './Divider';
export { DraggableControl } from './DraggableControl'; export { DraggableControl } from './DraggableControl';
export { Dropdown } from './Dropdown'; export { Dropdown } from './Dropdown';
export { Flex } from './Flex';
export { FitText } from './FitText'; export { FitText } from './FitText';
export { Flex } from './Flex';
export { Grid } from './Grid'; export { Grid } from './Grid';
export { Icon } from './Icon'; export { Icon } from './Icon';
export { InfinitePlane } from './InfinitePlane'; export { InfinitePlane } from './InfinitePlane';
@@ -32,18 +33,17 @@ export { MenuBar } from './MenuBar';
export { Modal } from './Modal'; export { Modal } from './Modal';
export { NoticeBox } from './NoticeBox'; export { NoticeBox } from './NoticeBox';
export { NumberInput } from './NumberInput'; export { NumberInput } from './NumberInput';
export { ProgressBar } from './ProgressBar';
export { Popper } from './Popper'; export { Popper } from './Popper';
export { ProgressBar } from './ProgressBar';
export { RestrictedInput } from './RestrictedInput'; export { RestrictedInput } from './RestrictedInput';
export { RoundGauge } from './RoundGauge'; export { RoundGauge } from './RoundGauge';
export { Section } from './Section'; export { Section } from './Section';
export { Slider } from './Slider'; export { Slider } from './Slider';
export { StyleableSection } from './StyleableSection';
export { Stack } from './Stack'; export { Stack } from './Stack';
export { StyleableSection } from './StyleableSection';
export { Table } from './Table'; export { Table } from './Table';
export { Tabs } from './Tabs'; export { Tabs } from './Tabs';
export { TextArea } from './TextArea'; export { TextArea } from './TextArea';
export { TimeDisplay } from './TimeDisplay'; export { TimeDisplay } from './TimeDisplay';
export { TrackOutsideClicks } from './TrackOutsideClicks';
export { Tooltip } from './Tooltip'; export { Tooltip } from './Tooltip';
export { Dialog } from './Dialog'; export { TrackOutsideClicks } from './TrackOutsideClicks';

View File

@@ -5,6 +5,7 @@
*/ */
import { KEY_BACKSPACE, KEY_F10, KEY_F11, KEY_F12 } from 'common/keycodes'; import { KEY_BACKSPACE, KEY_F10, KEY_F11, KEY_F12 } from 'common/keycodes';
import { globalEvents } from '../events'; import { globalEvents } from '../events';
import { acquireHotKey } from '../hotkeys'; import { acquireHotKey } from '../hotkeys';
import { openExternalBrowser, toggleDebugLayout, toggleKitchenSink } from './actions'; import { openExternalBrowser, toggleDebugLayout, toggleKitchenSink } from './actions';

View File

@@ -4,10 +4,10 @@
* @license MIT * @license MIT
*/ */
import { storage } from 'common/storage';
import { vecAdd, vecMultiply, vecScale, vecSubtract } from 'common/vector'; import { vecAdd, vecMultiply, vecScale, vecSubtract } from 'common/vector';
import { createLogger } from './logging'; import { createLogger } from './logging';
import { storage } from 'common/storage';
const logger = createLogger('drag'); const logger = createLogger('drag');
const pixelRatio = window.devicePixelRatio ?? 1; const pixelRatio = window.devicePixelRatio ?? 1;

View File

@@ -1,4 +1,4 @@
import { KeyEvent, addScrollableNode, canStealFocus, removeScrollableNode, setupGlobalEvents } from './events'; import { addScrollableNode, canStealFocus, KeyEvent, removeScrollableNode, setupGlobalEvents } from './events';
describe('focusEvents', () => { describe('focusEvents', () => {
afterEach(() => { afterEach(() => {

View File

@@ -6,9 +6,8 @@
* @license MIT * @license MIT
*/ */
import { KEY_ALT, KEY_CTRL, KEY_F1, KEY_F12, KEY_SHIFT } from 'common/keycodes';
import { EventEmitter } from 'common/events'; import { EventEmitter } from 'common/events';
import { KEY_ALT, KEY_CTRL, KEY_F1, KEY_F12, KEY_SHIFT } from 'common/keycodes';
export const globalEvents = new EventEmitter(); export const globalEvents = new EventEmitter();
let ignoreWindowFocus = false; let ignoreWindowFocus = false;

View File

@@ -5,6 +5,7 @@
*/ */
import * as keycodes from 'common/keycodes'; import * as keycodes from 'common/keycodes';
import { globalEvents, KeyEvent } from './events'; import { globalEvents, KeyEvent } from './events';
import { createLogger } from './logging'; import { createLogger } from './logging';

View File

@@ -19,15 +19,15 @@ import './styles/themes/syndicate.scss';
import './styles/themes/wizard.scss'; import './styles/themes/wizard.scss';
import './styles/themes/abstract.scss'; import './styles/themes/abstract.scss';
import { configureStore } from './store';
import { captureExternalLinks } from './links';
import { createRenderer } from './renderer';
import { perf } from 'common/perf'; import { perf } from 'common/perf';
import { setupHotReloading } from 'tgui-dev-server/link/client.cjs';
import { setGlobalStore } from './backend';
import { setupGlobalEvents } from './events'; import { setupGlobalEvents } from './events';
import { setupHotKeys } from './hotkeys'; import { setupHotKeys } from './hotkeys';
import { setupHotReloading } from 'tgui-dev-server/link/client.cjs'; import { captureExternalLinks } from './links';
import { setGlobalStore } from './backend'; import { createRenderer } from './renderer';
import { configureStore } from './store';
perf.mark('inception', window.performance?.timing?.navigationStart); perf.mark('inception', window.performance?.timing?.navigationStart);
perf.mark('init'); perf.mark('init');

View File

@@ -1,4 +1,5 @@
import { sortBy } from 'common/collections'; import { sortBy } from 'common/collections';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Button, Section, Table } from '../components'; import { Button, Section, Table } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -73,8 +73,8 @@ export const AdminTicketPanel = (props) => {
<div dangerouslySetInnerHTML={{ __html: actions }} /> <div dangerouslySetInnerHTML={{ __html: actions }} />
</LabeledList.Item> </LabeledList.Item>
<LabeledList.Item label="Log"> <LabeledList.Item label="Log">
{Object.keys(log).map((L) => ( {Object.keys(log).map((L, i) => (
<div dangerouslySetInnerHTML={{ __html: log[L] }} /> <div key={i} dangerouslySetInnerHTML={{ __html: log[L] }} />
))} ))}
</LabeledList.Item> </LabeledList.Item>
</LabeledList> </LabeledList>

View File

@@ -1,4 +1,5 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Button, Section, Table } from '../components'; import { Button, Section, Table } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -1,8 +1,8 @@
import { Loader } from './common/Loader';
import { useBackend, useLocalState } from '../backend';
import { KEY_ENTER, KEY_ESCAPE, KEY_LEFT, KEY_RIGHT, KEY_SPACE, KEY_TAB } from '../../common/keycodes'; import { KEY_ENTER, KEY_ESCAPE, KEY_LEFT, KEY_RIGHT, KEY_SPACE, KEY_TAB } from '../../common/keycodes';
import { useBackend, useLocalState } from '../backend';
import { Autofocus, Box, Button, Flex, Section, Stack } from '../components'; import { Autofocus, Box, Button, Flex, Section, Stack } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
import { Loader } from './common/Loader';
type AlertModalData = { type AlertModalData = {
autofocus: boolean; autofocus: boolean;

View File

@@ -1,4 +1,5 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components'; import { Button, LabeledList, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -1,11 +1,11 @@
import { createSearch, decodeHtmlEntities } from 'common/string';
import { useBackend, useLocalState } from '../backend';
import { Button, Icon, Input, NoticeBox, Section, Stack, Table, Tooltip } from '../components'; import { Button, Icon, Input, NoticeBox, Section, Stack, Table, Tooltip } from '../components';
import { TableCell, TableRow } from '../components/Table'; import { TableCell, TableRow } from '../components/Table';
import { createSearch, decodeHtmlEntities } from 'common/string'; import { Window } from '../layouts';
import { useBackend, useLocalState } from '../backend';
import { InputButtons } from './common/InputButtons'; import { InputButtons } from './common/InputButtons';
import { Loader } from './common/Loader'; import { Loader } from './common/Loader';
import { Window } from '../layouts';
type Data = { type Data = {
items: string[]; items: string[];

View File

@@ -1,4 +1,5 @@
import { Fragment } from 'react'; import { Fragment } from 'react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Box, Button, Flex, Section } from '../components'; import { Box, Button, Flex, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -2,8 +2,9 @@ import { filter } from 'common/collections';
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { decodeHtmlEntities, toTitleCase } from 'common/string'; import { decodeHtmlEntities, toTitleCase } from 'common/string';
import { Fragment } from 'react'; import { Fragment } from 'react';
import { useBackend, useLocalState } from '../backend'; import { useBackend, useLocalState } from '../backend';
import { Box, ByondUi, Button, Flex, Icon, LabeledList, Input, Section, Table } from '../components'; import { Box, Button, ByondUi, Flex, Icon, Input, LabeledList, Section, Table } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
import { CrewManifestContent } from './CrewManifest'; import { CrewManifestContent } from './CrewManifest';

View File

@@ -1,8 +1,9 @@
import { decodeHtmlEntities } from 'common/string';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Box, Section, Table } from '../components'; import { Box, Section, Table } from '../components';
import { Window } from '../layouts';
import { COLORS } from '../constants'; import { COLORS } from '../constants';
import { decodeHtmlEntities } from 'common/string'; import { Window } from '../layouts';
/* /*
* Shared by the following templates (and used individually too) * Shared by the following templates (and used individually too)

View File

@@ -1,6 +1,7 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Button, LabeledList, Section, Box, ProgressBar } from '../components'; import { Box, Button, LabeledList, ProgressBar, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
type Data = { type Data = {

View File

@@ -1,6 +1,7 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend, useLocalState } from '../backend'; import { useBackend, useLocalState } from '../backend';
import { Flex, Tabs, Section, Button, Box, TextArea, Divider } from '../components'; import { Box, Button, Divider, Flex, Section, Tabs, TextArea } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
type data = { type data = {

View File

@@ -1,6 +1,7 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Button, Box, Flex, LabeledList, Section } from '../components'; import { Box, Button, Flex, LabeledList, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
type Data = { type Data = {

View File

@@ -2,7 +2,8 @@ import { classes } from 'common/react';
import { uniqBy } from 'common/collections'; import { uniqBy } from 'common/collections';
import { useBackend, useSharedState } from '../backend'; import { useBackend, useSharedState } from '../backend';
import { formatSiUnit, formatMoney } from '../format'; import { formatSiUnit, formatMoney } from '../format';
import { Flex, Section, Tabs, Box, Button, Fragment, ProgressBar, NumberInput, Icon, Input, Tooltip } from '../components'; import { Flex, Section, Tabs, Box, Button, ProgressBar, NumberInput, Icon, Input, Tooltip } from '../components';
import { Fragment } from 'react';
import { Window } from '../layouts'; import { Window } from '../layouts';
import { createSearch, toTitleCase } from 'common/string'; import { createSearch, toTitleCase } from 'common/string';
import { toFixed } from 'common/math'; import { toFixed } from 'common/math';

View File

@@ -1,8 +1,9 @@
import { useBackend } from '../backend';
import { Window } from '../layouts';
import { Button, Section, Table, Knob } from '../components';
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend';
import { Button, Knob, Section, Table } from '../components';
import { Window } from '../layouts';
export const GyrotronControl = () => ( export const GyrotronControl = () => (
<Window width={627} height={700}> <Window width={627} height={700}>
<Window.Content> <Window.Content>

View File

@@ -1,4 +1,5 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components'; import { Button, LabeledList, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -1,9 +1,9 @@
import { Loader } from './common/Loader'; import { KEY_A, KEY_DOWN, KEY_ENTER, KEY_ESCAPE, KEY_UP, KEY_Z } from '../../common/keycodes';
import { InputButtons } from './common/InputButtons';
import { Button, Input, Section, Stack } from '../components';
import { useBackend, useLocalState } from '../backend'; import { useBackend, useLocalState } from '../backend';
import { KEY_A, KEY_DOWN, KEY_ESCAPE, KEY_ENTER, KEY_UP, KEY_Z } from '../../common/keycodes'; import { Button, Input, Section, Stack } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
import { InputButtons } from './common/InputButtons';
import { Loader } from './common/Loader';
type ListInputData = { type ListInputData = {
init_value: string; init_value: string;

View File

@@ -72,8 +72,8 @@ export const MentorTicketPanel = (props) => {
<div dangerouslySetInnerHTML={{ __html: actions }} /> <div dangerouslySetInnerHTML={{ __html: actions }} />
</LabeledList.Item> </LabeledList.Item>
<LabeledList.Item label="Log"> <LabeledList.Item label="Log">
{Object.keys(log).map((L) => ( {Object.keys(log).map((L, i) => (
<div dangerouslySetInnerHTML={{ __html: log[L] }} /> <div key={i} dangerouslySetInnerHTML={{ __html: log[L] }} />
))} ))}
</LabeledList.Item> </LabeledList.Item>
</LabeledList> </LabeledList>

View File

@@ -1,5 +1,6 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { Fragment } from 'react'; import { Fragment } from 'react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section } from '../components'; import { Box, Button, LabeledList, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -1,5 +1,6 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { Fragment } from 'react'; import { Fragment } from 'react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section } from '../components'; import { Box, Button, LabeledList, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -1,8 +1,9 @@
import { BooleanLike } from 'common/react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Box, Button, NoticeBox, Section } from '../components';
import { NtosWindow } from '../layouts'; import { NtosWindow } from '../layouts';
import { IdentificationComputerRegions } from './IdentificationComputer'; import { IdentificationComputerRegions } from './IdentificationComputer';
import { NoticeBox, Box, Section, Button } from '../components';
import { BooleanLike } from 'common/react';
type Data = { type Data = {
message: string; message: string;

View File

@@ -1,8 +1,9 @@
import { Section, Button, LabeledList } from '../components';
import { useBackend } from '../backend';
import { NtosWindow } from '../layouts';
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components';
import { NtosWindow } from '../layouts';
type Data = { type Data = {
armed: BooleanLike; armed: BooleanLike;
}; };

View File

@@ -1,9 +1,9 @@
import { Loader } from './common/Loader';
import { InputButtons } from './common/InputButtons';
import { KEY_ENTER, KEY_ESCAPE } from '../../common/keycodes'; import { KEY_ENTER, KEY_ESCAPE } from '../../common/keycodes';
import { useBackend, useLocalState } from '../backend'; import { useBackend, useLocalState } from '../backend';
import { Box, Button, RestrictedInput, Section, Stack } from '../components'; import { Box, Button, RestrictedInput, Section, Stack } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
import { InputButtons } from './common/InputButtons';
import { Loader } from './common/Loader';
type NumberInputData = { type NumberInputData = {
init_value: number; init_value: number;

View File

@@ -1,8 +1,9 @@
import { useBackend } from '../backend'; import { BooleanLike } from 'common/react';
import { Fragment } from 'react'; import { Fragment } from 'react';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section } from '../components'; import { Box, Button, LabeledList, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
import { BooleanLike } from 'common/react';
const getStatusText = (port) => { const getStatusText = (port) => {
if (port.input) { if (port.input) {

View File

@@ -1,8 +1,9 @@
import { useBackend } from '../backend'; import { BooleanLike } from 'common/react';
import { Fragment } from 'react'; import { Fragment } from 'react';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section, Table } from '../components'; import { Box, Button, LabeledList, Section, Table } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
import { BooleanLike } from 'common/react';
const getStatusText = (port) => { const getStatusText = (port) => {
if (port.input) { if (port.input) {

View File

@@ -1,5 +1,6 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { Fragment } from 'react'; import { Fragment } from 'react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components'; import { Button, LabeledList, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -1,4 +1,5 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section } from '../components'; import { Box, Button, LabeledList, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -1,4 +1,5 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section } from '../components'; import { Box, Button, LabeledList, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -1,5 +1,5 @@
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Slider, Section, LabeledList } from '../components'; import { LabeledList, Section, Slider } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
import { PortableBasicInfo } from './common/PortableAtmos'; import { PortableBasicInfo } from './common/PortableAtmos';

View File

@@ -1,8 +1,6 @@
import { map, sortBy } from 'common/collections'; import { map, sortBy } from 'common/collections';
import { flow } from 'common/fp'; import { flow } from 'common/fp';
import { toFixed } from 'common/math'; import { toFixed } from 'common/math';
import { pureComponentHooks } from 'common/react';
import { Fragment } from 'react';
import { useBackend, useLocalState } from '../backend'; import { useBackend, useLocalState } from '../backend';
import { Box, Button, Chart, ColorBox, Flex, Icon, LabeledList, ProgressBar, Section, Table } from '../components'; import { Box, Button, Chart, ColorBox, Flex, Icon, LabeledList, ProgressBar, Section, Table } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
@@ -254,8 +252,6 @@ export const AreaCharge = (props) => {
); );
}; };
AreaCharge.defaultHooks = pureComponentHooks;
const AreaStatusColorBox = (props) => { const AreaStatusColorBox = (props) => {
const { status } = props; const { status } = props;
const power = Boolean(status & 2); const power = Boolean(status & 2);
@@ -269,5 +265,3 @@ const AreaStatusColorBox = (props) => {
/> />
); );
}; };
AreaStatusColorBox.defaultHooks = pureComponentHooks;

View File

@@ -1,8 +1,9 @@
import { useBackend } from '../backend';
import { Window } from '../layouts';
import { Button, Section, Table, Knob } from '../components';
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend';
import { Button, Knob, Section, Table } from '../components';
import { Window } from '../layouts';
export const RustCoreMonitor = () => ( export const RustCoreMonitor = () => (
<Window width={627} height={700}> <Window width={627} height={700}>
<Window.Content> <Window.Content>

View File

@@ -1,8 +1,9 @@
import { useBackend } from '../backend';
import { Window } from '../layouts';
import { Button, Section, Table } from '../components';
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend';
import { Button, Section, Table } from '../components';
import { Window } from '../layouts';
export const RustFuelControl = () => ( export const RustFuelControl = () => (
<Window width={627} height={700}> <Window width={627} height={700}>
<Window.Content> <Window.Content>

View File

@@ -1,4 +1,5 @@
import { toFixed } from 'common/math'; import { toFixed } from 'common/math';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Button, Grid, NumberInput, Section } from '../components'; import { Button, Grid, NumberInput, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -1,9 +1,10 @@
import { T0C } from '../constants';
import { useBackend } from '../backend';
import { Button, Knob, Section, LabeledControls, LabeledList } from '../components';
import { Window } from '../layouts';
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend';
import { Button, Knob, LabeledControls, LabeledList, Section } from '../components';
import { T0C } from '../constants';
import { Window } from '../layouts';
type Data = { type Data = {
temp: number; temp: number;
minTemp: number; minTemp: number;

View File

@@ -1,4 +1,5 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components'; import { Button, LabeledList, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -1,9 +1,9 @@
import { Loader } from './common/Loader';
import { InputButtons } from './common/InputButtons';
import { useBackend, useLocalState } from '../backend';
import { KEY_ENTER, KEY_ESCAPE } from '../../common/keycodes'; // CHOMPedit import { KEY_ENTER, KEY_ESCAPE } from '../../common/keycodes'; // CHOMPedit
import { useBackend, useLocalState } from '../backend';
import { Box, Section, Stack, TextArea } from '../components'; import { Box, Section, Stack, TextArea } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
import { InputButtons } from './common/InputButtons';
import { Loader } from './common/Loader';
type TextInputData = { type TextInputData = {
large_buttons: boolean; large_buttons: boolean;

View File

@@ -1,6 +1,6 @@
/* eslint-disable react/no-danger */ /* eslint-disable react/no-danger */
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Stack, Tabs, Section, Box } from '../components'; import { Box, Section, Stack, Tabs } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
type data = { type data = {

View File

@@ -1,4 +1,5 @@
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Button, LabeledList, NoticeBox, Section } from '../components'; import { Button, LabeledList, NoticeBox, Section } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';

View File

@@ -1,5 +1,5 @@
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Button, LabeledList, Slider, Section } from '../components'; import { Button, LabeledList, Section, Slider } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
type Data = { type Data = {

View File

@@ -1,8 +1,9 @@
import { useBackend } from '../backend';
import { Window } from '../layouts';
import { Button, Section } from '../components';
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend';
import { Button, Section } from '../components';
import { Window } from '../layouts';
const ModeSpan = { const ModeSpan = {
'Hold': '<span class="badge text-bg-secondary">Hold</span>', 'Hold': '<span class="badge text-bg-secondary">Hold</span>',
'Digest': '<span class="badge text-bg-danger">Digest</span>', 'Digest': '<span class="badge text-bg-danger">Digest</span>',

View File

@@ -1,8 +1,9 @@
import { useBackend } from '../backend';
import { Window } from '../layouts';
import { Box, Button, Section } from '../components';
import { BooleanLike } from 'common/react'; import { BooleanLike } from 'common/react';
import { useBackend } from '../backend';
import { Box, Button, Section } from '../components';
import { Window } from '../layouts';
export const XenoarchArtifactAnalyzer = () => { export const XenoarchArtifactAnalyzer = () => {
return ( return (
<Window width={250} height={140}> <Window width={250} height={140}>

Some files were not shown because too many files have changed in this diff Show More