[MIRROR] Work on phasing out tgui collections.ts (#10059)

Co-authored-by: ShadowLarkens <shadowlarkens@gmail.com>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-02-03 03:15:50 -07:00
committed by GitHub
parent d887a54728
commit 56759cb95b
32 changed files with 296 additions and 267 deletions

View File

@@ -82,7 +82,7 @@
/obj/item/stack/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) /obj/item/stack/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
ui = SStgui.try_update_ui(user, src, ui) ui = SStgui.try_update_ui(user, src, ui)
if(!ui) if(!ui)
ui = new(user, src, "Stack", name) ui = new(user, src, "MaterialStack", name)
ui.open() ui.open()
/obj/item/stack/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) /obj/item/stack/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)

View File

@@ -4,12 +4,10 @@
* @license MIT * @license MIT
*/ */
import { map } from 'common/collections';
export const selectChat = (state) => state.chat; export const selectChat = (state) => state.chat;
export const selectChatPages = (state) => export const selectChatPages = (state) =>
map(state.chat.pages, (id: string) => state.chat.pageById[id]); state.chat.pages.map((id: string) => state.chat.pageById[id]);
export const selectCurrentChatPage = (state) => export const selectCurrentChatPage = (state) =>
state.chat.pageById[state.chat.currentPageId]; state.chat.pageById[state.chat.currentPageId];

View File

@@ -1,4 +1,3 @@
import { sortBy } from 'common/collections';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
import { Button, Section, Table } from 'tgui-core/components'; import { Button, Section, Table } from 'tgui-core/components';
@@ -35,11 +34,20 @@ export const ShuttleList = (props) => {
const { shuttles, overmap_ships } = data; const { shuttles, overmap_ships } = data;
shuttles.sort((a, b) => a.name.localeCompare(b.name));
overmap_ships.sort((a, b) => {
let a_cmp = a.name?.toLowerCase() || a.name || a.ref;
let b_cmp = a.name?.toLowerCase() || a.name || a.ref;
return a_cmp.localeCompare(b_cmp);
});
return ( return (
<Section noTopPadding> <Section noTopPadding>
<Section title="Classic Shuttles"> <Section title="Classic Shuttles">
<Table> <Table>
{sortBy(shuttles, (f: Shuttle) => f.name).map((shuttle) => ( {shuttles.map((shuttle) => (
<Table.Row key={shuttle.ref}> <Table.Row key={shuttle.ref}>
<Table.Cell collapsing> <Table.Cell collapsing>
<Button <Button
@@ -66,10 +74,7 @@ export const ShuttleList = (props) => {
</Section> </Section>
<Section title="Overmap Ships"> <Section title="Overmap Ships">
<Table> <Table>
{sortBy( {overmap_ships.map((ship) => (
overmap_ships,
(f: OvermapShip) => f.name?.toLowerCase() || f.name || f.ref,
).map((ship) => (
<Table.Row key={ship.ref}> <Table.Row key={ship.ref}>
<Table.Cell collapsing> <Table.Cell collapsing>
<Button onClick={() => act('adminobserve', { ref: ship.ref })}> <Button onClick={() => act('adminobserve', { ref: ship.ref })}>

View File

@@ -1,14 +1,15 @@
import { sortBy } from 'common/collections';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Button, LabeledList, Section, Stack } from 'tgui-core/components'; import { Button, LabeledList, Section, Stack } from 'tgui-core/components';
import { Data, species, styles } from './types'; import { Data, species } from './types';
export const AppearanceChangerSpecies = (props) => { export const AppearanceChangerSpecies = (props) => {
const { act, data } = useBackend<Data>(); const { act, data } = useBackend<Data>();
const { species, specimen } = data; const { species, specimen } = data;
const sortedSpecies = sortBy(species || [], (val: species) => val.specimen); const sortedSpecies = (species || []).sort((a: species, b: species) =>
a.specimen.localeCompare(b.specimen),
);
return ( return (
<Section title="Species" fill scrollable> <Section title="Species" fill scrollable>
@@ -65,6 +66,10 @@ export const AppearanceChangerEars = (props) => {
const { ear_style, ear_styles } = data; const { ear_style, ear_styles } = data;
ear_styles.sort((a, b) =>
a.name.toLowerCase().localeCompare(b.name.toLowerCase()),
);
return ( return (
<Stack vertical fill> <Stack vertical fill>
<Stack.Item grow> <Stack.Item grow>
@@ -75,8 +80,7 @@ export const AppearanceChangerEars = (props) => {
> >
-- Not Set -- -- Not Set --
</Button> </Button>
{sortBy(ear_styles, (e: styles) => e.name.toLowerCase()).map( {ear_styles.map((ear) => (
(ear) => (
<Button <Button
key={ear.instance} key={ear.instance}
onClick={() => act('ear', { ref: ear.instance })} onClick={() => act('ear', { ref: ear.instance })}
@@ -84,8 +88,7 @@ export const AppearanceChangerEars = (props) => {
> >
{ear.name} {ear.name}
</Button> </Button>
), ))}
)}
</Section> </Section>
</Stack.Item> </Stack.Item>
<Stack.Item grow> <Stack.Item grow>
@@ -96,8 +99,7 @@ export const AppearanceChangerEars = (props) => {
> >
-- Not Set -- -- Not Set --
</Button> </Button>
{sortBy(ear_styles, (e: styles) => e.name.toLowerCase()).map( {ear_styles.map((ear) => (
(ear) => (
<Button <Button
key={ear.instance} key={ear.instance}
onClick={() => act('ear_secondary', { ref: ear.instance })} onClick={() => act('ear_secondary', { ref: ear.instance })}
@@ -105,8 +107,7 @@ export const AppearanceChangerEars = (props) => {
> >
{ear.name} {ear.name}
</Button> </Button>
), ))}
)}
</Section> </Section>
</Stack.Item> </Stack.Item>
</Stack> </Stack>
@@ -118,6 +119,10 @@ export const AppearanceChangerTails = (props) => {
const { tail_style, tail_styles } = data; const { tail_style, tail_styles } = data;
tail_styles.sort((a, b) =>
a.name.toLowerCase().localeCompare(b.name.toLowerCase()),
);
return ( return (
<Section title="Tails" fill scrollable> <Section title="Tails" fill scrollable>
<Button <Button
@@ -126,7 +131,7 @@ export const AppearanceChangerTails = (props) => {
> >
-- Not Set -- -- Not Set --
</Button> </Button>
{sortBy(tail_styles, (e: styles) => e.name.toLowerCase()).map((tail) => ( {tail_styles.map((tail) => (
<Button <Button
key={tail.instance} key={tail.instance}
onClick={() => act('tail', { ref: tail.instance })} onClick={() => act('tail', { ref: tail.instance })}
@@ -143,6 +148,9 @@ export const AppearanceChangerWings = (props) => {
const { act, data } = useBackend<Data>(); const { act, data } = useBackend<Data>();
const { wing_style, wing_styles } = data; const { wing_style, wing_styles } = data;
wing_styles.sort((a, b) =>
a.name.toLowerCase().localeCompare(b.name.toLowerCase()),
);
return ( return (
<Section title="Wings" fill scrollable> <Section title="Wings" fill scrollable>
@@ -152,7 +160,7 @@ export const AppearanceChangerWings = (props) => {
> >
-- Not Set -- -- Not Set --
</Button> </Button>
{sortBy(wing_styles, (e: styles) => e.name.toLowerCase()).map((wing) => ( {wing_styles.map((wing) => (
<Button <Button
key={wing.instance} key={wing.instance}
onClick={() => act('wing', { ref: wing.instance })} onClick={() => act('wing', { ref: wing.instance })}

View File

@@ -1,4 +1,3 @@
import { sortBy } from 'common/collections';
import { useState } from 'react'; import { useState } from 'react';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { NanoMap } from 'tgui/components'; import { NanoMap } from 'tgui/components';
@@ -32,9 +31,8 @@ export const AtmosControl = (props) => {
export const AtmosControlContent = (props) => { export const AtmosControlContent = (props) => {
const { act, data, config } = useBackend<Data>(); const { act, data, config } = useBackend<Data>();
let sortedAlarms = sortBy(data.alarms || [], (alarm: alarm) => alarm.name); const { alarms } = data;
alarms.sort((a, b) => a.name.localeCompare(b.name));
// sortedAlarms = sortedAlarms.slice(1, 3);
const [tabIndex, setTabIndex] = useState(0); const [tabIndex, setTabIndex] = useState(0);
const [zoom, setZoom] = useState(1); const [zoom, setZoom] = useState(1);
@@ -44,7 +42,7 @@ export const AtmosControlContent = (props) => {
if (tabIndex === 0) { if (tabIndex === 0) {
body = ( body = (
<Section title="Alarms"> <Section title="Alarms">
{sortedAlarms.map((alarm) => ( {alarms.map((alarm) => (
<Button <Button
key={alarm.name} key={alarm.name}
color={ color={
@@ -64,7 +62,7 @@ export const AtmosControlContent = (props) => {
body = ( body = (
<Box height="526px" mb="0.5rem" overflow="hidden"> <Box height="526px" mb="0.5rem" overflow="hidden">
<NanoMap zoomScale={data.zoomScale} onZoom={(v) => setZoom(v)}> <NanoMap zoomScale={data.zoomScale} onZoom={(v) => setZoom(v)}>
{sortedAlarms {alarms
.filter((x) => ~~x.z === ~~config.mapZLevel) .filter((x) => ~~x.z === ~~config.mapZLevel)
.map((cm) => ( .map((cm) => (
<NanoMap.Marker <NanoMap.Marker

View File

@@ -1,4 +1,3 @@
import { filter, sortBy } from 'common/collections';
import { useState } from 'react'; import { useState } from 'react';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
@@ -10,7 +9,6 @@ import {
Section, Section,
Stack, Stack,
} from 'tgui-core/components'; } from 'tgui-core/components';
import { flow } from 'tgui-core/fp';
import { BooleanLike, classes } from 'tgui-core/react'; import { BooleanLike, classes } from 'tgui-core/react';
import { createSearch } from 'tgui-core/string'; import { createSearch } from 'tgui-core/string';
@@ -57,31 +55,24 @@ export const selectCameras = (
networkFilter: string = '', networkFilter: string = '',
): camera[] => { ): camera[] => {
const testSearch = createSearch(searchText, (camera: camera) => camera.name); const testSearch = createSearch(searchText, (camera: camera) => camera.name);
return flow([
(cameras: camera[]) => return cameras
// Null camera filter .filter((camera) => notEmpty(camera?.name))
filter(cameras, (camera) => notEmpty(camera?.name)), .filter((camera) => {
(cameras: camera[]) => {
// Optional search term
if (!searchText) { if (!searchText) {
return cameras; return true;
} else { } else {
return filter(cameras, testSearch); return testSearch(camera);
} }
}, })
(cameras: camera[]) => { .filter((camera) => {
// Optional network filter
if (!networkFilter) { if (!networkFilter) {
return cameras; return true;
} else { } else {
return filter(cameras, (camera) => return camera.networks.includes(networkFilter);
camera.networks.includes(networkFilter),
);
} }
}, })
// Slightly expensive, but way better than sorting in BYOND .sort((a, b) => a.name.localeCompare(b.name));
(cameras: camera[]) => sortBy(cameras, (camera) => camera.name),
])(cameras);
}; };
export const CameraConsole = (props) => { export const CameraConsole = (props) => {

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Box, LabeledList, Section } from 'tgui-core/components'; import { Box, LabeledList, Section } from 'tgui-core/components';
import { decodeHtmlEntities, toTitleCase } from 'tgui-core/string'; import { decodeHtmlEntities, toTitleCase } from 'tgui-core/string';
@@ -16,13 +15,14 @@ export const CommunicatorWeatherTab = (props) => {
<Section title="Weather"> <Section title="Weather">
<Section title="Current Conditions"> <Section title="Current Conditions">
<LabeledList> <LabeledList>
{filter( {aircontents
aircontents, .filter(
(i: AirContent) => (i: AirContent) =>
i.val !== '0' || i.val !== '0' ||
i.entry === 'Pressure' || i.entry === 'Pressure' ||
i.entry === 'Temperature', i.entry === 'Temperature',
).map((item: AirContent) => ( )
.map((item: AirContent) => (
<LabeledList.Item <LabeledList.Item
key={item.entry} key={item.entry}
label={item.entry} label={item.entry}

View File

@@ -1,11 +1,9 @@
import { sortBy } from 'common/collections';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Box, Icon, Tabs } from 'tgui-core/components'; import { Box, Icon, Tabs } from 'tgui-core/components';
import { flow } from 'tgui-core/fp';
import { CrewMonitorCrew } from './CrewMonitorCrew'; import { CrewMonitorCrew } from './CrewMonitorCrew';
import { CrewMonitorMapView } from './CrewMonitorMapView'; import { CrewMonitorMapView } from './CrewMonitorMapView';
import { crewmember, Data } from './types'; import { Data } from './types';
export const CrewMonitorContent = (props: { export const CrewMonitorContent = (props: {
tabIndex: number; tabIndex: number;
@@ -17,19 +15,12 @@ export const CrewMonitorContent = (props: {
const { crewmembers = [] } = data; const { crewmembers = [] } = data;
const crew: crewmember[] = flow([
(crewmembers: crewmember[]) => sortBy(crewmembers, (cm) => cm.name),
(crewmembers: crewmember[]) => sortBy(crewmembers, (cm) => cm?.x),
(crewmembers: crewmember[]) => sortBy(crewmembers, (cm) => cm?.y),
(crewmembers: crewmember[]) => sortBy(crewmembers, (cm) => cm?.realZ),
])(crewmembers);
const tab: React.JSX.Element[] = []; const tab: React.JSX.Element[] = [];
// Data view // Data view
// Please note, if you ever change the zoom values, // Please note, if you ever change the zoom values,
// you MUST update styles/components/Tooltip.scss // you MUST update styles/components/Tooltip.scss
// and change the @for scss to match. // and change the @for scss to match.
tab[0] = <CrewMonitorCrew crew={crew} />; tab[0] = <CrewMonitorCrew crew={crewmembers} />;
tab[1] = <CrewMonitorMapView zoom={props.zoom} onZoom={props.onZoom} />; tab[1] = <CrewMonitorMapView zoom={props.zoom} onZoom={props.onZoom} />;

View File

@@ -88,7 +88,13 @@ export function getSortedCrew(
return flow([ return flow([
(shownCrew: crewmember[]) => { (shownCrew: crewmember[]) => {
if (sortType === 'name') { if (sortType === 'name') {
const sorted = shownCrew.sort((a, b) => a.name.localeCompare(b.name)); const sorted = shownCrew.sort(
(a, b) =>
a.name.localeCompare(b.name) ||
a.realZ - b.realZ ||
a.x - b.x ||
a.y - b.y,
);
if (nameSortOrder) { if (nameSortOrder) {
return sorted.reverse(); return sorted.reverse();
} }
@@ -100,7 +106,9 @@ export function getSortedCrew(
(shownCrew: crewmember[]) => { (shownCrew: crewmember[]) => {
if (sortType === 'damage') { if (sortType === 'damage') {
const sorted = shownCrew.sort( const sorted = shownCrew.sort(
(a, b) => getTotalDamage(a) - getTotalDamage(b), (a, b) =>
getTotalDamage(a) - getTotalDamage(b) ||
a.name.localeCompare(b.name),
); );
if (damageSortOrder) { if (damageSortOrder) {
return sorted.reverse(); return sorted.reverse();
@@ -112,29 +120,13 @@ export function getSortedCrew(
}, },
(shownCrew: crewmember[]) => { (shownCrew: crewmember[]) => {
if (sortType === 'location') { if (sortType === 'location') {
const sorted = shownCrew.sort((a, b) => a.x - b.x); const sorted = shownCrew.sort(
if (locationSortOrder) { (a, b) =>
return sorted.reverse(); a.realZ - b.realZ ||
} a.x - b.x ||
return sorted; a.y - b.y ||
} else { a.name.localeCompare(b.name),
return shownCrew; );
}
},
(shownCrew: crewmember[]) => {
if (sortType === 'location') {
const sorted = shownCrew.sort((a, b) => a.y - b.y);
if (locationSortOrder) {
return sorted.reverse();
}
return sorted;
} else {
return shownCrew;
}
},
(shownCrew: crewmember[]) => {
if (sortType === 'location') {
const sorted = shownCrew.sort((a, b) => a.realZ - b.realZ);
if (locationSortOrder) { if (locationSortOrder) {
return sorted.reverse(); return sorted.reverse();
} }

View File

@@ -1,4 +1,3 @@
import { sortBy } from 'common/collections';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
import { Button, Section } from 'tgui-core/components'; import { Button, Section } from 'tgui-core/components';
@@ -13,13 +12,13 @@ export const FileCabinet = (props) => {
const { contents } = data; const { contents } = data;
// Wow, the filing cabinets sort themselves in 2320. // Wow, the filing cabinets sort themselves in 2320.
const sortedContents = sortBy(contents || [], (val: content) => val.name); contents.sort((a, b) => a.name.localeCompare(b.name));
return ( return (
<Window width={350} height={300}> <Window width={350} height={300}>
<Window.Content scrollable> <Window.Content scrollable>
<Section> <Section>
{sortedContents.map((item) => ( {contents.map((item) => (
<Button <Button
key={item.ref} key={item.ref}
fluid fluid

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { flow } from 'tgui-core/fp'; import { flow } from 'tgui-core/fp';
import { createSearch } from 'tgui-core/string'; import { createSearch } from 'tgui-core/string';
@@ -27,7 +26,7 @@ export function selectRecords(records: record[], searchText = ''): record[] {
if (!searchText) { if (!searchText) {
return records; return records;
} else { } else {
return filter(records, (record) => { return records.filter((record) => {
return nameSearch(record) || idSearch(record) || dnaSearch(record); return nameSearch(record) || idSearch(record) || dnaSearch(record);
}); });
} }

View File

@@ -1,6 +1,5 @@
// Currently not used! // Currently not used!
import { map, sortBy } from 'common/collections';
import { vecLength, vecSubtract } from 'common/vector'; import { vecLength, vecSubtract } from 'common/vector';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
@@ -16,7 +15,7 @@ import { flow } from 'tgui-core/fp';
import { clamp } from 'tgui-core/math'; import { clamp } from 'tgui-core/math';
import { BooleanLike } from 'tgui-core/react'; import { BooleanLike } from 'tgui-core/react';
const coordsToVec = (coords) => map(coords.split(', '), parseFloat); const coordsToVec = (coords: string) => coords.split(', ').map(parseFloat);
type Data = { type Data = {
currentArea: string; currentArea: string;
@@ -31,16 +30,25 @@ type Data = {
type signal = { type signal = {
entrytag: string; entrytag: string;
coords: string; coords: string;
dist: number; dist?: number;
degrees: number; degrees: number;
}; };
function sortSignal(a: signal, b: signal) {
if (a.dist === undefined && b.dist === undefined) return 0;
if (a.dist === undefined) return 1;
if (b.dist === undefined) return -1;
if (a.dist < b.dist) return -1;
if (a.dist > b.dist) return 1;
else return a.entrytag.localeCompare(b.entrytag);
}
export const Gps = (props) => { export const Gps = (props) => {
const { act, data } = useBackend<Data>(); const { act, data } = useBackend<Data>();
const { currentArea, currentCoords, globalmode, power, tag, updating } = data; const { currentArea, currentCoords, globalmode, power, tag, updating } = data;
const signals: signal[] = flow([ const signals: signal[] = flow([
(signals: signal[]) => (signals: signal[]) =>
map(signals, (signal, index) => { signals.map((signal, index) => {
// Calculate distance to the target. BYOND distance is capped to 127, // Calculate distance to the target. BYOND distance is capped to 127,
// that's why we roll our own calculations here. // that's why we roll our own calculations here.
const dist = const dist =
@@ -55,14 +63,7 @@ export const Gps = (props) => {
); );
return { ...signal, dist, index }; return { ...signal, dist, index };
}), }),
(signals: signal[]) => (signals: signal[]) => signals.sort((a, b) => sortSignal(a, b)),
sortBy(
signals,
// Signals with distance metric go first
(signal) => signal.dist === undefined,
// Sort alphabetically
(signal) => signal.entrytag,
),
])(data.signals || []); ])(data.signals || []);
return ( return (
<Window title="Global Positioning System" width={470} height={700}> <Window title="Global Positioning System" width={470} height={700}>

View File

@@ -1,5 +1,4 @@
/* eslint react/no-danger: "off" */ /* eslint react/no-danger: "off" */
import { sortBy } from 'common/collections';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
import { Box, Button, LabeledList, Section } from 'tgui-core/components'; import { Box, Button, LabeledList, Section } from 'tgui-core/components';
@@ -24,6 +23,8 @@ export const GuestPass = (props) => {
const { area, giver, giveName, reason, duration, mode, log, uid } = data; const { area, giver, giveName, reason, duration, mode, log, uid } = data;
area.sort((a, b) => a.area_name.localeCompare(b.area_name));
return ( return (
<Window width={500} height={520}> <Window width={500} height={520}>
<Window.Content scrollable> <Window.Content scrollable>
@@ -80,7 +81,7 @@ export const GuestPass = (props) => {
Issue Pass Issue Pass
</Button.Confirm> </Button.Confirm>
<Section title="Access"> <Section title="Access">
{sortBy(area, (a: area) => a.area_name).map((a) => ( {area.map((a) => (
<Button.Checkbox <Button.Checkbox
checked={a.on} checked={a.on}
key={a.area} key={a.area}

View File

@@ -1,4 +1,3 @@
import { filter, sortBy } from 'common/collections';
import { useBackend, useSharedState } from 'tgui/backend'; import { useBackend, useSharedState } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
import { import {
@@ -95,17 +94,18 @@ const ICPrinterCategories = (props) => {
'', '',
); );
const selectedCategory = filter( const selectedCategory = categories.filter(
categories,
(cat: category) => cat.name === categoryTarget, (cat: category) => cat.name === categoryTarget,
)[0]; )[0];
categories.sort((a, b) => a.name.localeCompare(b.name));
return ( return (
<Section fill title="Circuits"> <Section fill title="Circuits">
<Stack fill> <Stack fill>
<Stack.Item mr={2} basis="20%"> <Stack.Item mr={2} basis="20%">
<Tabs vertical> <Tabs vertical>
{sortBy(categories, (cat: category) => cat.name).map((cat) => ( {categories.map((cat) => (
<Tabs.Tab <Tabs.Tab
selected={categoryTarget === cat.name} selected={categoryTarget === cat.name}
onClick={() => setcategoryTarget(cat.name)} onClick={() => setcategoryTarget(cat.name)}
@@ -120,8 +120,9 @@ const ICPrinterCategories = (props) => {
{selectedCategory ? ( {selectedCategory ? (
<Section fill scrollable> <Section fill scrollable>
<LabeledList> <LabeledList>
{sortBy(selectedCategory.items, (item: item) => item.name).map( {selectedCategory.items
(item) => ( .sort((a, b) => a.name.localeCompare(b.name))
.map((item) => (
<LabeledList.Item <LabeledList.Item
key={item.name} key={item.name}
label={item.name} label={item.name}
@@ -138,8 +139,7 @@ const ICPrinterCategories = (props) => {
> >
{item.desc} {item.desc}
</LabeledList.Item> </LabeledList.Item>
), ))}
)}
</LabeledList> </LabeledList>
</Section> </Section>
) : ( ) : (

View File

@@ -1,4 +1,3 @@
import { sortBy } from 'common/collections';
import { Fragment } from 'react'; import { Fragment } from 'react';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
@@ -276,13 +275,21 @@ export const IdentificationComputerRegions = (props: { actName: string }) => {
const { regions } = data; const { regions } = data;
if (regions) {
regions.sort((a, b) => a.name.localeCompare(b.name));
for (let region of regions) {
region.accesses.sort((a, b) => a.desc.localeCompare(b.desc));
}
}
return ( return (
<Stack wrap="wrap"> <Stack wrap="wrap">
{regions && {regions &&
sortBy(regions, (r) => r.name).map((region) => ( regions.map((region) => (
<Stack.Item mb={1} basis="content" grow key={region.name}> <Stack.Item mb={1} basis="content" grow key={region.name}>
<Section title={region.name} height="100%"> <Section title={region.name} height="100%">
{sortBy(region.accesses, (a) => a.desc).map((access) => ( {region.accesses.map((access) => (
<Box key={access.ref}> <Box key={access.ref}>
<Button <Button
fluid fluid

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { useBackend, useSharedState } from 'tgui/backend'; import { useBackend, useSharedState } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
import { import {
@@ -579,7 +578,7 @@ const prepareSearch = (
if (!searchText) { if (!searchText) {
return laws; return laws;
} else { } else {
return filter(laws, testSearch); return laws.filter(testSearch);
} }
}, },
])(laws); ])(laws);

View File

@@ -1,8 +1,18 @@
import { useState } from 'react';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
import { Box, Button, Collapsible, Section, Table } from 'tgui-core/components'; import {
Box,
Button,
Collapsible,
Input,
Section,
Stack,
Table,
} from 'tgui-core/components';
import { createSearch } from 'tgui-core/string';
type Data = { amount: number; recipes: recipe[] }; type Data = { amount: number; recipes: Record<string, recipe>[] };
type recipe = { type recipe = {
res_amount: number; res_amount: number;
@@ -11,27 +21,49 @@ type recipe = {
ref: string; ref: string;
}; };
export const Stack = (props) => { export const MaterialStack = (props) => {
const { data } = useBackend<Data>(); const { data } = useBackend<Data>();
const { amount, recipes } = data; const { amount, recipes } = data;
const [searchText, setSearchText] = useState('');
return ( return (
<Window width={400} height={600}> <Window width={400} height={600}>
<Window.Content scrollable> <Window.Content scrollable>
<Section title={'Amount: ' + amount}> <Section title={'Amount: ' + amount}>
<RecipeList recipes={recipes} /> <Stack vertical>
<Stack.Item>
<Input
fluid
placeholder="Search for recipe..."
value={searchText}
onInput={(e, val) => setSearchText(val)}
/>
</Stack.Item>
<Stack.Item>
<RecipeList recipes={recipes} searchText={searchText} />
</Stack.Item>
</Stack>
</Section> </Section>
</Window.Content> </Window.Content>
</Window> </Window>
); );
}; };
const RecipeList = (props: { recipes: recipe[] }) => { const RecipeList = (props: {
const { recipes } = props; recipes: Record<string, recipe>[];
searchText: string;
}) => {
const { recipes, searchText } = props;
const sortedKeys = Object.keys(recipes).sort(); const sortedKeys = Object.keys(recipes).sort();
const searcher = createSearch(searchText, (recipe: string) => {
return recipe;
});
const filteredKeys = sortedKeys.filter(searcher);
// Shunt all categories to the top. // Shunt all categories to the top.
// We're not using this for now, keeping it here in case someone really hates color coding later. // We're not using this for now, keeping it here in case someone really hates color coding later.
// let nonCategories = sortedKeys.filter(item => recipes[item].ref !== undefined); // let nonCategories = sortedKeys.filter(item => recipes[item].ref !== undefined);
@@ -41,7 +73,7 @@ const RecipeList = (props: { recipes: recipe[] }) => {
// let newSortedKeys = nonCategories.concat(categories); // let newSortedKeys = nonCategories.concat(categories);
return sortedKeys.map((title, index) => { return filteredKeys.map((title, index) => {
// if (title === "--DIVIDER--") { // if (title === "--DIVIDER--") {
// return ( // return (
// <Box mt={1} mb={1}> // <Box mt={1} mb={1}>
@@ -54,7 +86,7 @@ const RecipeList = (props: { recipes: recipe[] }) => {
return ( return (
<Collapsible key={index} ml={1} mb={-0.7} color="label" title={title}> <Collapsible key={index} ml={1} mb={-0.7} color="label" title={title}>
<Box ml={1}> <Box ml={1}>
<RecipeList recipes={recipe} /> <RecipeList recipes={recipe} searchText={searchText} />
</Box> </Box>
</Collapsible> </Collapsible>
); );

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { flow } from 'tgui-core/fp'; import { flow } from 'tgui-core/fp';
import { createSearch } from 'tgui-core/string'; import { createSearch } from 'tgui-core/string';
@@ -23,7 +22,7 @@ export function prepareSearch<T extends SearchObject>(
if (!searchText) { if (!searchText) {
return objects as any; return objects as any;
} else { } else {
return filter(objects, testSearch) as any; return objects.filter(testSearch) as any;
} }
}, },
])(objects); ])(objects);

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { NtosWindow } from 'tgui/layouts'; import { NtosWindow } from 'tgui/layouts';
import { Button, LabeledList, Section, Table } from 'tgui-core/components'; import { Button, LabeledList, Section, Table } from 'tgui-core/components';
@@ -63,8 +62,7 @@ const WarrantList = (props) => {
const { allwarrants = [] } = data; const { allwarrants = [] } = data;
const ourWarrants = filter( const ourWarrants = allwarrants.filter(
allwarrants,
(w: warrant) => w.arrestsearch === type, (w: warrant) => w.arrestsearch === type,
); );

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Box, LabeledList } from 'tgui-core/components'; import { Box, LabeledList } from 'tgui-core/components';
import { decodeHtmlEntities } from 'tgui-core/string'; import { decodeHtmlEntities } from 'tgui-core/string';
@@ -38,13 +37,14 @@ export const pda_atmos_scan = (props) => {
return ( return (
<Box> <Box>
<LabeledList> <LabeledList>
{filter( {aircontents
aircontents, .filter(
(i: aircontent) => (i: aircontent) =>
i.val !== '0' || i.val !== '0' ||
i.entry === 'Pressure' || i.entry === 'Pressure' ||
i.entry === 'Temperature', i.entry === 'Temperature',
).map((item) => ( )
.map((item) => (
<LabeledList.Item <LabeledList.Item
key={item.entry} key={item.entry}
label={item.entry} label={item.entry}

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { ReactNode, useEffect, useRef, useState } from 'react'; import { ReactNode, useEffect, useRef, useState } from 'react';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Box, Button, Image, LabeledList, Section } from 'tgui-core/components'; import { Box, Button, Image, LabeledList, Section } from 'tgui-core/components';
@@ -290,8 +289,9 @@ const ActiveConversationASCII = (props: {
return ( return (
<Box> <Box>
{filter(messages, (im: message) => im.target === active_conversation).map( {messages
(im, i) => ( .filter((im: message) => im.target === active_conversation)
.map((im, i) => (
<Box <Box
key={i} key={i}
className={ className={
@@ -300,8 +300,7 @@ const ActiveConversationASCII = (props: {
> >
{im.sent ? 'You:' : 'Them:'} {decodeHtmlEntities(im.message)} {im.sent ? 'You:' : 'Them:'} {decodeHtmlEntities(im.message)}
</Box> </Box>
), ))}
)}
</Box> </Box>
); );
}; };

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { useState } from 'react'; import { useState } from 'react';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
@@ -96,12 +95,12 @@ export const PersonalCrafting = (props) => {
const shownRecipes: uiRecipe[] = flow([ const shownRecipes: uiRecipe[] = flow([
(recipes: uiRecipe[]) => (recipes: uiRecipe[]) =>
filter(recipes, (recipe) => recipe.category === tab), recipes.filter((recipe) => recipe.category === tab),
(recipes: uiRecipe[]) => { (recipes: uiRecipe[]) => {
if (!searchText) { if (!searchText) {
return recipes; return recipes;
} else { } else {
return filter(recipes, testSearch); return recipes.filter(testSearch);
} }
}, },
])(recipes); ])(recipes);

View File

@@ -1,4 +1,3 @@
import { sortBy } from 'common/collections';
import { useState } from 'react'; import { useState } from 'react';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { import {
@@ -33,6 +32,7 @@ export const PowerMonitorFocus = (props: { focus: sensor }) => {
...history.supply, ...history.supply,
...history.demand, ...history.demand,
); );
// Process area data // Process area data
const areas: area[] = flow([ const areas: area[] = flow([
(areas: area[]) => (areas: area[]) =>
@@ -45,24 +45,24 @@ export const PowerMonitorFocus = (props: { focus: sensor }) => {
if (sortByField !== 'name') { if (sortByField !== 'name') {
return areas; return areas;
} else { } else {
return sortBy(areas, (area) => area.name); return areas.sort((a, b) => a.name.localeCompare(b.name));
} }
}, },
(areas: area[]) => { (areas: area[]) => {
if (sortByField !== 'charge') { if (sortByField !== 'charge') {
return areas; return areas;
} else { } else {
return sortBy(areas, (area) => -area.charge); return areas.sort((a, b) => b.charge - a.charge);
} }
}, },
(areas: area[]) => { (areas: area[]) => {
if (sortByField !== 'draw') { if (sortByField !== 'draw') {
return areas; return areas;
} else { } else {
return sortBy( return areas.sort(
areas, (a, b) =>
(area) => -powerRank(area.load), powerRank(b.load) - powerRank(a.load) ||
(area) => -parseFloat(area.load), parseFloat(b.load) - parseFloat(a.load),
); );
} }
}, },
@@ -70,17 +70,18 @@ export const PowerMonitorFocus = (props: { focus: sensor }) => {
if (sortByField !== 'problems') { if (sortByField !== 'problems') {
return areas; return areas;
} else { } else {
return sortBy( return areas.sort(
areas, (a, b) =>
(area) => area.eqp, a.eqp - b.eqp ||
(area) => area.lgt, a.lgt - b.lgt ||
(area) => area.env, a.env - b.env ||
(area) => area.charge, a.charge - b.charge ||
(area) => area.name, a.name.localeCompare(b.name),
); );
} }
}, },
])(focus.areas); ])(focus.areas);
return ( return (
<> <>
<Section <Section

View File

@@ -1,4 +1,4 @@
import { binaryInsertWith, sortBy } from 'common/collections'; import { binaryInsertWith } from 'common/collections';
import { ReactNode, useState } from 'react'; import { ReactNode, useState } from 'react';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { import {
@@ -25,8 +25,13 @@ const binaryInsertPreference = (
value: PreferenceChild, value: PreferenceChild,
) => binaryInsertWith(collection, value, (child) => child.name); ) => binaryInsertWith(collection, value, (child) => child.name);
function sortPref(k: [string, PreferenceChild[]]) {
k[1].sort((a, b) => a.name.localeCompare(b.name));
return k;
}
const sortByName = (array: [string, PreferenceChild[]][]) => const sortByName = (array: [string, PreferenceChild[]][]) =>
sortBy(array, ([name]) => name); array.map((k, _) => sortPref(k)).sort((a, b) => a[0].localeCompare(b[0]));
export const GamePreferencesPage = (props) => { export const GamePreferencesPage = (props) => {
const { act, data } = useBackend<PreferencesMenuData>(); const { act, data } = useBackend<PreferencesMenuData>();

View File

@@ -1,4 +1,3 @@
import { sortBy } from 'common/collections';
import { import {
ComponentType, ComponentType,
createElement, createElement,
@@ -6,6 +5,7 @@ import {
useEffect, useEffect,
useState, useState,
} from 'react'; } from 'react';
import React from 'react';
import { sendAct, useBackend } from 'tgui/backend'; import { sendAct, useBackend } from 'tgui/backend';
import { import {
Box, Box,
@@ -21,8 +21,12 @@ import { BooleanLike } from 'tgui-core/react';
import { createSetPreference, PreferencesMenuData } from '../../data'; import { createSetPreference, PreferencesMenuData } from '../../data';
import { ServerPreferencesFetcher } from '../../ServerPreferencesFetcher'; import { ServerPreferencesFetcher } from '../../ServerPreferencesFetcher';
export const sortChoices = (array: [string, ReactNode][]) => function sortNode(...node: [string, ReactNode][]) {
sortBy(array, ([name]) => name); node.sort((a, b) => a[0].localeCompare(b[0]));
return node;
}
export const sortChoices = (array: [string, ReactNode][]) => sortNode(...array);
export type Feature< export type Feature<
TReceiving, TReceiving,

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { useBackend, useSharedState } from 'tgui/backend'; import { useBackend, useSharedState } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
import { Box, Button, LabeledList, Section, Tabs } from 'tgui-core/components'; import { Box, Button, LabeledList, Section, Tabs } from 'tgui-core/components';
@@ -194,8 +193,9 @@ const ResearchServerData = (props: { server: server }) => {
))} ))}
</Section> </Section>
<Section title="Designs"> <Section title="Designs">
{filter(server.designs, (design: techDes) => !!design.name).map( {server.designs
(design) => ( .filter((design: techDes) => !!design.name)
.map((design) => (
<LabeledList.Item <LabeledList.Item
label={design.name} label={design.name}
key={design.name} key={design.name}
@@ -215,8 +215,7 @@ const ResearchServerData = (props: { server: server }) => {
</Button.Confirm> </Button.Confirm>
} }
/> />
), ))}
)}
</Section> </Section>
</> </>
); );

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { flow } from 'tgui-core/fp'; import { flow } from 'tgui-core/fp';
import { createSearch } from 'tgui-core/string'; import { createSearch } from 'tgui-core/string';
@@ -33,14 +32,14 @@ export function robotSpriteSearcher(
if (!searchText) { if (!searchText) {
return sprites; return sprites;
} else { } else {
return filter(sprites, testSearch); return sprites.filter(testSearch);
} }
}, },
(sprites: spriteOption[]) => { (sprites: spriteOption[]) => {
if (!subtypes.length) { if (!subtypes.length) {
return sprites; return sprites;
} else { } else {
return filter(sprites, (sprite) => subtypes.includes(sprite.type)); return sprites.filter((sprite) => subtypes.includes(sprite.type));
} }
}, },
])(sprites); ])(sprites);

View File

@@ -1,4 +1,3 @@
import { sortBy } from 'common/collections';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
import { import {
@@ -39,13 +38,15 @@ export const SeedStorage = (props) => {
const { seeds } = data; const { seeds } = data;
const sortedSeeds = sortBy(seeds, (seed: seed) => seed.name.toLowerCase()); seeds.sort((a, b) =>
a.name.toLowerCase().localeCompare(b.name.toLowerCase()),
);
return ( return (
<Window width={600} height={760}> <Window width={600} height={760}>
<Window.Content scrollable> <Window.Content scrollable>
<Section title="Seeds"> <Section title="Seeds">
{sortedSeeds.map((seed) => ( {seeds.map((seed) => (
<Stack mt={-1} key={seed.name + seed.uid}> <Stack mt={-1} key={seed.name + seed.uid}>
<Stack.Item basis="60%"> <Stack.Item basis="60%">
<Collapsible title={toTitleCase(seed.name) + ' #' + seed.uid}> <Collapsible title={toTitleCase(seed.name) + ' #' + seed.uid}>

View File

@@ -1,8 +1,6 @@
import { filter, sortBy } from 'common/collections';
import { useState } from 'react'; import { useState } from 'react';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Box, Button, Section, Stack } from 'tgui-core/components'; import { Box, Button, Section, Stack } from 'tgui-core/components';
import { flow } from 'tgui-core/fp';
import { Data, supplyPack } from './types'; import { Data, supplyPack } from './types';
@@ -13,15 +11,19 @@ export const SupplyConsoleMenuOrder = (props) => {
const [activeCategory, setActiveCategory] = useState<string | null>(null); const [activeCategory, setActiveCategory] = useState<string | null>(null);
const viewingPacks: supplyPack[] = flow([ function sortPack(a: supplyPack, b: supplyPack) {
(supply_packs: supplyPack[]) => if (a.cost < supply_points && b.cost > supply_points) return -1;
filter(supply_packs, (val) => val.group === activeCategory), if (a.cost > supply_points && b.cost < supply_points) return 1;
(supply_packs: supplyPack[]) =>
filter(supply_packs, (val) => !val.contraband || !!contraband), return a.name.localeCompare(b.name);
(supply_packs: supplyPack[]) => sortBy(supply_packs, (val) => val.name), }
(supply_packs: supplyPack[]) =>
sortBy(supply_packs, (val) => val.cost > supply_points), const viewingPacks: supplyPack[] = supply_packs
])(supply_packs); .filter(
(pack) =>
(pack.group === activeCategory && !pack.contraband) || !!contraband,
)
.sort((a, b) => sortPack(a, b));
// const viewingPacks = sortBy(val => val.name)(supply_packs).filter(val => val.group === activeCategory); // const viewingPacks = sortBy(val => val.name)(supply_packs).filter(val => val.group === activeCategory);

View File

@@ -1,4 +1,3 @@
import { sortBy } from 'common/collections';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
import { import {
@@ -73,6 +72,10 @@ export const TelesciConsoleContent = (props) => {
lastTeleData, lastTeleData,
} = data; } = data;
if (sectorOptions) {
sectorOptions.sort();
}
return ( return (
<Section <Section
title="Telepad Controls" title="Telepad Controls"
@@ -120,7 +123,7 @@ export const TelesciConsoleContent = (props) => {
</LabeledList.Item> </LabeledList.Item>
<LabeledList.Item label="Sector"> <LabeledList.Item label="Sector">
{sectorOptions && {sectorOptions &&
sortBy(sectorOptions, (v) => v).map((z) => ( sectorOptions.map((z) => (
<Button <Button
key={z} key={z}
icon="check-circle" icon="check-circle"

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { useState } from 'react'; import { useState } from 'react';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
@@ -208,7 +207,7 @@ export const prepareSearch = (
if (!searchText) { if (!searchText) {
return products; return products;
} else { } else {
return filter(products, testSearch); return products.filter(testSearch);
} }
}, },
])(products); ])(products);

View File

@@ -1,4 +1,3 @@
import { filter } from 'common/collections';
import { useBackend } from 'tgui/backend'; import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts'; import { Window } from 'tgui/layouts';
import { LabeledList, Section } from 'tgui-core/components'; import { LabeledList, Section } from 'tgui-core/components';
@@ -47,13 +46,14 @@ export const pAIAtmos = (props) => {
<Window.Content scrollable> <Window.Content scrollable>
<Section> <Section>
<LabeledList> <LabeledList>
{filter( {aircontents
aircontents, .filter(
(i: aircontent) => (i: aircontent) =>
i.val !== '0' || i.val !== '0' ||
i.entry === 'Pressure' || i.entry === 'Pressure' ||
i.entry === 'Temperature', i.entry === 'Temperature',
).map((item: aircontent) => ( )
.map((item: aircontent) => (
<LabeledList.Item <LabeledList.Item
key={item.entry} key={item.entry}
label={item.entry} label={item.entry}