code:
import { useBackend } from "../backend";
import { Stack, Section, Box, NoticeBox, Flex, Button } from '../components';
import { Window } from "../layouts";

export const PoliticalCompass = (props, context) => {
  const { act, data } = useBackend(context);
  const {
    questions = [],
    final_result,
    final_color,
    flagicon64,
  } = data;

  return (
    <Window
      title={`Political Compass`}
      width={500}
      height={600}>
      <Window.Content>
        <Stack
          fill
          vertical>
          <Stack.Item
            grow>
            <Section
              fill
              scrollable
              textAlign="center"
              title={`Questions`}>
              {questions.length !== 0 ? (
                <Flex.Item>
                  {questions.map(question => (
                    <Flex.Item
                      key={question.question}>
                      <Section
                        my={2}>
                        {question.question}
                      </Section>
                      {question.answers.map(answer => (
                        <Button
                          mx={0.5}
                          color={answer.selected ? "good" : null}
                          key={answer.answer}
                          onClick={() => act('answerquestion', {
                            question_id: question.qid,
                            answer_id: answer.aid,
                          })}>
                          {answer.answer}
                        </Button>
                      ))}
                      <Button
                        mx={0.5}
                        color={question.unanswered ? "bad" : null}
                        onClick={() => act('removeanswer', {
                          question_id: question.qid,
                        })}>
                        I am not sure
                      </Button>
                    </Flex.Item>
                  ))}
                </Flex.Item>
              ) : (
                <NoticeBox>No questions available!</NoticeBox>
              )}
            </Section>
          </Stack.Item>
          <Stack.Item>
            <Box my={1} />
          </Stack.Item>
          <Stack.Item>
            <Section
              textAlign="center"
              title={`Results`}>
              <Box
                fontSize={1.5}>
                You are:
                <Box color={final_color}>
                  {final_result}
                  {(flagicon64 ? true : false) && (
                    <Box
                      mx={0.5}
                      as="img"
                      src={`data:image/jpeg;base64,${flagicon64}`}
                      style={{
                        '-ms-interpolation-mode': 'nearest-neighbor',
                      }} />
                  )}
                </Box>
                <Button
                  color={"bad"}
                  onClick={() => act('reset')}>
                  Reset
                </Button>
              </Box>
            </Section>
          </Stack.Item>
        </Stack>
      </Window.Content>
    </Window>
  );
};


code:
import { sortBy } from 'common/collections';
import { toTitleCase } from 'common/string';
import { useBackend } from '../backend';
import { AnimatedNumber, Box, Button, LabeledList, Section } from '../components';
import { Window } from '../layouts';

export const PortableChemMixer = (props, context) => {
  const { act, data } = useBackend(context);
  const recording = !!data.recordingRecipe;
  const beakerTransferAmounts = data.beakerTransferAmounts || [];
  const beakerContents = recording
    && Object.keys(data.recordingRecipe)
      .map(id => ({
        id,
        name: toTitleCase(id.replace(/_/, ' ')),
        volume: data.recordingRecipe[id],
      }))
    || data.beakerContents
    || [];
  const chemicals = sortBy(chem => chem.title)(data.chemicals);
  return (
    <Window
      width={465}
      height={550}>
      <Window.Content scrollable>
        <Section
          title="Dispense"
          buttons={(
            beakerTransferAmounts.map(amount => (
              <Button
                key={amount}
                icon="plus"
                selected={amount === data.amount}
                content={amount}
                onClick={() => act('amount', {
                  target: amount,
                })} />
            ))
          )}>
          <Box>
            {chemicals.map(chemical => (
              <Button
                key={chemical.id}
                icon="tint"
                fluid
                lineHeight={1.75}
                content={`(${chemical.volume}) ${chemical.title}`}
                tooltip={"pH: " + chemical.pH}
                onClick={() => act('dispense', {
                  reagent: chemical.id,
                })} />
            ))}
          </Box>
        </Section>
        <Section
          title="Disposal controls"
          buttons={(
            beakerTransferAmounts.map(amount => (
              <Button
                key={amount}
                icon="minus"
                disabled={recording}
                content={amount}
                onClick={() => act('remove', { amount })} />
            ))
          )}>
          <LabeledList>
            <LabeledList.Item
              label="Beaker"
              buttons={!!data.isBeakerLoaded && (
                <Button
                  icon="eject"
                  content="Eject"
                  disabled={!data.isBeakerLoaded}
                  onClick={() => act('eject')} />
              )}>
              {recording
                && 'Virtual beaker'
                || data.isBeakerLoaded
                  && (
                    <>
                      <AnimatedNumber
                        initial={0}
                        value={data.beakerCurrentVolume} />
                      /{data.beakerMaxVolume} units
                    </>
                  )
                || 'No beaker'}
            </LabeledList.Item>
            <LabeledList.Item
              label="Contents">
              <Box color="label">
                {(!data.isBeakerLoaded && !recording) && 'N/A'
                  || beakerContents.length === 0 && 'Nothing'}
              </Box>
              {beakerContents.map(chemical => (
                <Box
                  key={chemical.name}
                  color="label">
                  <AnimatedNumber
                    initial={0}
                    value={chemical.volume} />
                  {' '}
                  units of {chemical.name}
                </Box>
              ))}
              {((beakerContents.length > 0 && !!data.showpH) && (
                <Box>
                  pH:
                  <AnimatedNumber
                    value={data.beakerCurrentpH} />
                </Box>)
              )}
            </LabeledList.Item>
          </LabeledList>
        </Section>
      </Window.Content>
    </Window>
  );
};

repo_name: AnalWerewolf/sundown

license: mit


code:
import { useBackend } from '../backend';
import { Box, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../components';
import { Window } from '../layouts';

export const PortableGenerator = (props, context) => {
  const { act, data } = useBackend(context);
  const {
    stack_percent,
  } = data;
  const stackPercentState = (
    stack_percent > 50 && 'good'
    || stack_percent > 15 && 'average'
    || 'bad'
  );
  return (
    <Window
      width={450}
      height={340}>
      <Window.Content scrollable>
        {!data.anchored && (
          <NoticeBox>Generator not anchored.</NoticeBox>
        )}

          <LabeledList>
            <LabeledList.Item label="Power switch">
              <Button
                icon={data.active ? 'power-off' : 'times'}
                onClick={() => act('toggle_power')}
                disabled={!data.ready_to_boot}>
                {data.active ? 'On' : 'Off'}
              </Button>
            </LabeledList.Item>
            <LabeledList.Item label={data.sheet_name + ' sheets'}>
              <Box inline color={stackPercentState}>{data.sheets}</Box>
              {data.sheets >= 1 && (
                <Button
                  ml={1}
                  icon="eject"
                  disabled={data.active}
                  onClick={() => act('eject')}>
                  Eject
                </Button>
              )}
            </LabeledList.Item>
            <LabeledList.Item label="Current sheet level">
              <ProgressBar
                value={data.stack_percent / 100}
                ranges={{
                  good: [0.1, Infinity],
                  average: [0.01, 0.1],
                  bad: [-Infinity, 0.01],
                }} />
            </LabeledList.Item>
            <LabeledList.Item label="Heat level">
              {data.current_heat < 100 ? (
                <Box inline color="good">Nominal</Box>
              ) : (
                data.current_heat < 200 ? (
                  <Box inline color="average">Caution</Box>
                ) : (
                  <Box inline color="bad">DANGER</Box>
                )
              )}
            </LabeledList.Item>
          </LabeledList>

          <LabeledList>
            <LabeledList.Item label="Current output">
              {data.power_output}
            </LabeledList.Item>
            <LabeledList.Item label="Adjust output">
              <Button
                icon="minus"
                onClick={() => act('lower_power')}>
                {data.power_generated}
              </Button>
              <Button
                icon="plus"
                onClick={() => act('higher_power')}>
                {data.power_generated}
              </Button>
            </LabeledList.Item>
            <LabeledList.Item label="Power available">
              <Box inline color={!data.connected && 'bad'}>
                {data.connected ? data.power_available : "Unconnected"}
              </Box>
            </LabeledList.Item>
          </LabeledList>

      </Window.Content>
    </Window>
  );
};


code:
import { useBackend } from '../backend';
import { Button, LabeledList, NumberInput, Section } from '../components';
import { Window } from '../layouts';
import { PortableBasicInfo } from './common/PortableAtmos';

export const PortablePump = (props, context) => {
  const { act, data } = useBackend(context);
  const {
    direction,
    holding,
    target_pressure,
    default_pressure,
    min_pressure,
    max_pressure,
  } = data;
  return (
    <Window
      width={300}
      height={315}>
      <Window.Content>
        <PortableBasicInfo />
        <Section
          title="Pump"
          buttons={(
            <Button
              icon={direction ? 'sign-in-alt' : 'sign-out-alt'}
              content={direction ? 'In' : 'Out'}
              selected={direction}
              onClick={() => act('direction')} />
          )}>
          <LabeledList>
            <LabeledList.Item label="Output">
              <NumberInput
                value={target_pressure}
                unit="kPa"
                width="75px"
                minValue={min_pressure}
                maxValue={max_pressure}
                step={10}
                onChange={(e, value) => act('pressure', {
                  pressure: value,
                })} />
            </LabeledList.Item>
            <LabeledList.Item label="Presets">
              <Button
                icon="minus"
                disabled={target_pressure === min_pressure}
                onClick={() => act('pressure', {
                  pressure: 'min',
                })} />
              <Button
                icon="sync"
                disabled={target_pressure === default_pressure}
                onClick={() => act('pressure', {
                  pressure: 'reset',
                })} />
              <Button
                icon="plus"
                disabled={target_pressure === max_pressure}
                onClick={() => act('pressure', {
                  pressure: 'max',
                })} />
            </LabeledList.Item>
          </LabeledList>
        </Section>
      </Window.Content>
    </Window>
  );
};


code:
import { useBackend } from '../backend';
import { Button, Section } from '../components';
import { getGasLabel } from '../constants';
import { Window } from '../layouts';
import { PortableBasicInfo } from './common/PortableAtmos';

export const PortableScrubber = (props, context) => {
  const { act, data } = useBackend(context);
  const filter_types = data.filter_types || [];
  return (
    <Window
      width={320}
      height={396}>
      <Window.Content>
        <PortableBasicInfo />
        <Section title="Filters">
          {filter_types.map(filter => (
            <Button
              key={filter.id}
              icon={filter.enabled ? 'check-square-o' : 'square-o'}
              content={getGasLabel(filter.gas_id, filter.gas_name)}
              selected={filter.enabled}
              onClick={() => act('toggle_filter', {
                val: filter.gas_id,
              })} />
          ))}
        </Section>
      </Window.Content>
    </Window>
  );
};


code:
import { useBackend } from '../backend';
import { Button, LabeledList, NoticeBox, Section } from '../components';
import { Window } from '../layouts';

export const PortableTurret = (props, context) => {
  const { act, data } = useBackend(context);
  const {
    silicon_user,
    locked,
    on,
    check_weapons,
    neutralize_criminals,
    neutralize_all,
    neutralize_unidentified,
    neutralize_nonmindshielded,
    neutralize_cyborgs,
    neutralize_heads,
    manual_control,
    allow_manual_control,
    lasertag_turret,
  } = data;
  return (
    <Window
      width={310}
      height={lasertag_turret ? 110 : 292}>
      <Window.Content>
        <NoticeBox>
          Swipe an ID card to {locked ? 'unlock' : 'lock'} this interface.
        </NoticeBox>
        <>
          <Section>
            <LabeledList>
              <LabeledList.Item
                label="Status"
                buttons={!lasertag_turret && (!!allow_manual_control
                  || (!!manual_control && !!silicon_user)) && (
                  <Button
                    icon={manual_control ? "wifi" : "terminal"}
                    content={manual_control
                      ? "Remotely Controlled"
                      : "Manual Control"}
                    disabled={manual_control}
                    color="bad"
                    onClick={() => act('manual')} />
                )}>
                <Button
                  icon={on ? 'power-off' : 'times'}
                  content={on ? 'On' : 'Off'}
                  selected={on}
                  disabled={locked}
                  onClick={() => act('power')} />
              </LabeledList.Item>
            </LabeledList>
          </Section>
          {!lasertag_turret && (
            <Section
              title="Target Settings"
              buttons={(
                <Button.Checkbox
                  checked={!neutralize_heads}
                  content="Ignore Command"
                  disabled={locked}
                  onClick={() => act('shootheads')} />
              )}>
              <Button.Checkbox
                fluid
                checked={neutralize_all}
                content="Non-Security and Non-Command"
                disabled={locked}
                onClick={() => act('shootall')} />
              <Button.Checkbox
                fluid
                checked={check_weapons}
                content="Unauthorized Weapons"
                disabled={locked}
                onClick={() => act('authweapon')} />
              <Button.Checkbox
                fluid
                checked={neutralize_unidentified}
                content="Unidentified Life Signs"
                disabled={locked}
                onClick={() => act('checkxenos')} />
              <Button.Checkbox
                fluid
                checked={neutralize_nonmindshielded}
                content="Non-Mindshielded"
                disabled={locked}
                onClick={() => act('checkloyal')} />
              <Button.Checkbox
                fluid
                checked={neutralize_criminals}
                content="Wanted Criminals"
                disabled={locked}
                onClick={() => act('shootcriminals')} />
              <Button.Checkbox
                fluid
                checked={neutralize_cyborgs}
                content="Cyborgs"
                disabled={locked}
                onClick={() => act('shootborgs')} />
            </Section>
          )}
        </>
      </Window.Content>
    </Window>
  );
};


code:
import { resolveAsset } from '../assets';
import { useBackend, useLocalState } from '../backend';
import { Button, Flex, NoticeBox, Section, Tabs } from '../components';
import { Window } from '../layouts';

export const PortraitPicker = (props, context) => {
  const { act, data } = useBackend(context);
  const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0);
  const [listIndex, setListIndex] = useLocalState(context, 'listIndex', 0);
  const {
    library,
    library_secure,
    library_private,
  } = data;
  const TABS = [
    {
      name: 'Common Portraits',
      asset_prefix: "library",
      list: library,
    },
    {
      name: 'Secure Portraits',
      asset_prefix: "library_secure",
      list: library_secure,
    },
    {
      name: 'Private Portraits',
      asset_prefix: "library_private",
      list: library_private,
    },
  ];
  const tab2list = TABS[tabIndex].list;
  const current_portrait_title = tab2list[listIndex]["title"];
  const current_portrait_asset_name = TABS[tabIndex].asset_prefix + "_" + tab2list[listIndex]["md5"];
  return (
    <Window
      theme="ntos"
      title="Portrait Picker"
      width={400}
      height={406}>
      <Window.Content>
        <Flex height="100%" direction="column">
          <Flex.Item mb={1}>
            <Section fitted>
              <Tabs fluid textAlign="center">
                {TABS.map((tabObj, i) => (
                  <Tabs.Tab
                    key={i}
                    selected={i === tabIndex}
                    onClick={() => {
                      setListIndex(0);
                      setTabIndex(i);
                    }}>
                    {tabObj.name}
                  </Tabs.Tab>
                ))}
              </Tabs>
            </Section>
          </Flex.Item>
          <Flex.Item mb={1} grow={2}>
            <Section fill>
              <Flex
                height="100%"
                align="center"
                justify="center"
                direction="column">
                <Flex.Item>
                  <img
                    src={resolveAsset(current_portrait_asset_name)}
                    height="96px"
                    width="96px"
                    style={{
                      'vertical-align': 'middle',
                      '-ms-interpolation-mode': 'nearest-neighbor',
                    }} />
                </Flex.Item>
                <Flex.Item className="Section__titleText">
                  {current_portrait_title}
                </Flex.Item>
              </Flex>
            </Section>
          </Flex.Item>
          <Flex.Item>
            <Flex>
              <Flex.Item grow={3}>
                <Section height="100%">
                  <Flex justify="space-between">
                    <Flex.Item grow={1}>
                      <Button
                        icon="angle-double-left"
                        disabled={listIndex === 0}
                        onClick={() => setListIndex(0)}
                      />
                    </Flex.Item>
                    <Flex.Item grow={3}>
                      <Button
                        disabled={listIndex === 0}
                        icon="chevron-left"
                        onClick={() => setListIndex(listIndex-1)}
                      />
                    </Flex.Item>
                    <Flex.Item grow={3}>
                      <Button
                        icon="check"
                        content="Select Portrait"
                        onClick={() => act("select", {
                          tab: tabIndex+1,
                          selected: listIndex+1,
                        })}
                      />
                    </Flex.Item>
                    <Flex.Item grow={1}>
                      <Button
                        icon="chevron-right"
                        disabled={listIndex === tab2list.length-1}
                        onClick={() => setListIndex(listIndex+1)}
                      />
                    </Flex.Item>
                    <Flex.Item>
                      <Button
                        icon="angle-double-right"
                        disabled={listIndex === tab2list.length-1}
                        onClick={() => setListIndex(tab2list.length-1)}
                      />
                    </Flex.Item>
                  </Flex>
                </Section>
              </Flex.Item>
            </Flex>
            <Flex.Item mt={1}>
              <NoticeBox info>
                Only the 23x23 or 24x24 canvas size art can be
                displayed. Make sure you read the warning below
                before embracing the wide wonderful world of
                artistic expression!
              </NoticeBox>
            </Flex.Item>
            <Flex.Item>
              <NoticeBox danger>
                WARNING: While Central Command loves art as much as you do,
                choosing erotic art will lead to severe consequences.
                Additionally, Central Command reserves the right to request
                you change your display portrait, for any reason.
              </NoticeBox>
            </Flex.Item>
          </Flex.Item>
        </Flex>
      </Window.Content>
    </Window>
  );
};


code:
import { map, sortBy } from 'common/collections';
import { flow } from 'common/fp';
import { toFixed } from 'common/math';
import { pureComponentHooks } from 'common/react';
import { useBackend, useLocalState } from '../backend';
import { Box, Button, Chart, ColorBox, Flex, Icon, LabeledList, ProgressBar, Section, Table } from '../components';
import { Window } from '../layouts';

const PEAK_DRAW = 500000;

export const powerRank = str => {
  const unit = String(str.split(' ')[1]).toLowerCase();
  return ['w', 'kw', 'mw', 'gw'].indexOf(unit);
};

export const PowerMonitor = () => {
  return (
    <Window
      width={550}
      height={700}>
      <Window.Content scrollable>
        <PowerMonitorContent />
      </Window.Content>
    </Window>
  );
};

export const PowerMonitorContent = (props, context) => {
  const { data } = useBackend(context);
  const { history } = data;
  const [
    sortByField,
    setSortByField,
  ] = useLocalState(context, 'sortByField', null);
  const supply = history.supply[history.supply.length - 1] || 0;
  const demand = history.demand[history.demand.length - 1] || 0;
  const supplyData = history.supply.map((value, i) => [i, value]);
  const demandData = history.demand.map((value, i) => [i, value]);
  const maxValue = Math.max(
    PEAK_DRAW,
    ...history.supply,
    ...history.demand);
    // Process area data
  const areas = flow([
    map((area, i) => ({
      ...area,
      // Generate a unique id
      id: area.name + i,
    })),
    sortByField === 'name' && sortBy(area => area.name),
    sortByField === 'charge' && sortBy(area => -area.charge),
    sortByField === 'draw' && sortBy(
      area => -powerRank(area.load),
      area => -parseFloat(area.load)),
  ])(data.areas);
  return (
    <>
      <Flex mx={-0.5} mb={1}>
        <Flex.Item mx={0.5} width="200px">

            <LabeledList>
              <LabeledList.Item label="Supply">
                <ProgressBar
                  value={supply}
                  minValue={0}
                  maxValue={maxValue}
                  color="teal">
                  {toFixed(supply / 1000) + ' kW'}
                </ProgressBar>
              </LabeledList.Item>
              <LabeledList.Item label="Draw">
                <ProgressBar
                  value={demand}
                  minValue={0}
                  maxValue={maxValue}
                  color="pink">
                  {toFixed(demand / 1000) + ' kW'}
                </ProgressBar>
              </LabeledList.Item>
            </LabeledList>

        </Flex.Item>
        <Flex.Item mx={0.5} grow={1}>

            <Chart.Line
              fillPositionedParent
              data={supplyData}
              rangeX={[0, supplyData.length - 1]}
              rangeY={[0, maxValue]}
              strokeColor="rgba(0, 181, 173, 1)"
              fillColor="rgba(0, 181, 173, 0.25)" />
            <Chart.Line
              fillPositionedParent
              data={demandData}
              rangeX={[0, demandData.length - 1]}
              rangeY={[0, maxValue]}
              strokeColor="rgba(224, 57, 151, 1)"
              fillColor="rgba(224, 57, 151, 0.25)" />

        </Flex.Item>
      </Flex>

        <Box mb={1}>
          <Box inline mr={2} color="label">
            Sort by:
          </Box>
          <Button.Checkbox
            checked={sortByField === 'name'}
            content="Name"
            onClick={() => setSortByField(
              sortByField !== 'name' && 'name'
            )} />
          <Button.Checkbox
            checked={sortByField === 'charge'}
            content="Charge"
            onClick={() => setSortByField(
              sortByField !== 'charge' && 'charge'
            )} />
          <Button.Checkbox
            checked={sortByField === 'draw'}
            content="Draw"
            onClick={() => setSortByField(
              sortByField !== 'draw' && 'draw'
            )} />
        </Box>

              Area

              Charge

              Draw

              Eqp

              Lgt

              Env

          {areas.map((area, i) => (

                {area.name}

                <AreaCharge
                  charging={area.charging}
                  charge={area.charge} />

                {area.load}

                <AreaStatusColorBox status={area.eqp} />

                <AreaStatusColorBox status={area.lgt} />

                <AreaStatusColorBox status={area.env} />

          ))}

    </>
  );
};

export const AreaCharge = props => {
  const { charging, charge } = props;
  return (
    <>
      <Icon
        width="18px"
        textAlign="center"
        name={(
          charging === 0 && (
            charge > 50
              ? 'battery-half'
              : 'battery-quarter'
          )
          || charging === 1 && 'bolt'
          || charging === 2 && 'battery-full'
        )}
        color={(
          charging === 0 && (
            charge > 50
              ? 'yellow'
              : 'red'
          )
          || charging === 1 && 'yellow'
          || charging === 2 && 'green'
        )} />
      <Box
        inline
        width="36px"
        textAlign="right">
        {toFixed(charge) + '%'}
      </Box>
    </>
  );
};

AreaCharge.defaultHooks = pureComponentHooks;

const AreaStatusColorBox = props => {
  const { status } = props;
  const power = Boolean(status & 2);
  const mode = Boolean(status & 1);
  const tooltipText = (power ? 'On' : 'Off')
    + ` [${mode ? 'auto' : 'manual'}]`;
  return (
    <ColorBox
      color={power ? 'good' : 'bad'}
      content={mode ? undefined : 'M'}
      title={tooltipText} />
  );
};

AreaStatusColorBox.defaultHooks = pureComponentHooks;


code:
import { binaryInsertWith } from "common/collections";
import { classes } from "common/react";
import { useBackend, useLocalState } from "../../backend";
import { Box, Button, Divider, Flex, Section, Stack, Tooltip } from "../../components";
import { Antagonist, Category } from "./antagonists/base";
import { PreferencesMenuData } from "./data";

const requireAntag = require.context("./antagonists/antagonists", false, /.ts$/);

const antagsByCategory = new Map<Category, Antagonist[]>();

// This will break at priorities higher than 10, but that almost definitely
// will not happen.
const binaryInsertAntag = binaryInsertWith((antag: Antagonist) => {
  return `${antag.priority}_${antag.name}`;
});

for (const antagKey of requireAntag.keys()) {
  const antag = requireAntag<{
    default?: Antagonist,
  }>(antagKey).default;

  if (!antag) {
    continue;
  }

  antagsByCategory.set(
    antag.category,
    binaryInsertAntag(
      antagsByCategory.get(antag.category) || [],
      antag,
    )
  );
}

const AntagSelection = (props: {
  antagonists: Antagonist[],
  name: string,
}, context) => {
  const { act, data } = useBackend<PreferencesMenuData>(context);
  const className = "PreferencesMenu__Antags__antagSelection";

  const [predictedState, setPredictedState]
    = useLocalState(
      context,
      "AntagSelection_predictedState",
      new Set(data.selected_antags),
    );

  const enableAntags = (antags: string[]) => {
    const newState = new Set(predictedState);

    for (const antag of antags) {
      newState.add(antag);
    }

    setPredictedState(newState);

    act("set_antags", {
      antags,
      toggled: true,
    });
  };

  const disableAntags = (antags: string[]) => {
    const newState = new Set(predictedState);

    for (const antag of antags) {
      newState.delete(antag);
    }

    setPredictedState(newState);

    act("set_antags", {
      antags,
      toggled: false,
    });
  };

  const antagonistKeys = props.antagonists.map(antagonist => antagonist.key);

  return (
    <Section title={props.name} buttons={(
      <>
        <Button
          color="good"
          onClick={() => enableAntags(antagonistKeys)}
        >
          Enable All
        </Button>

        <Button
          color="bad"
          onClick={() => disableAntags(antagonistKeys)}
        >
          Disable All
        </Button>
      </>
    )}>
      <Flex className={className} align="flex-end" wrap>
        {props.antagonists.map(antagonist => {
          const isBanned = data.antag_bans
            && data.antag_bans.indexOf(antagonist.key) !== -1;

          const daysLeft
            = (data.antag_days_left && data.antag_days_left[antagonist.key])
              || 0;

          return (
            <Flex.Item
              className={classes([
                `${className}__antagonist`,
                `${className}__antagonist--${
                  (isBanned || daysLeft > 0)
                    ? "banned"
                    : predictedState.has(antagonist.key) ? "on" : "off"
                }`,
              ])}
              key={antagonist.key}
            >
              <Stack align="center" vertical>
                <Stack.Item style={{
                  "font-weight": "bold",
                  "margin-top": "auto",
                  "max-width": "100px",
                  "text-align": "center",
                }}>
                  {antagonist.name}
                </Stack.Item>

                <Stack.Item align="center">
                  <Tooltip content={
                    isBanned
                      ? `You are banned from ${antagonist.name}.`
                      : antagonist.description.map((text, index) => {
                        return (

                            {text}
                            {
                              index !== antagonist.description.length - 1
                              && <Divider />
                            }

                        );
                      })
                  } position="bottom">
                    <Box
                      className={"antagonist-icon-parent"}
                      onClick={() => {
                        if (isBanned) {
                          return;
                        }

                        if (predictedState.has(antagonist.key)) {
                          disableAntags([antagonist.key]);
                        } else {
                          enableAntags([antagonist.key]);
                        }
                      }}
                    >
                      <Box className={classes([
                        "antagonists96x96",
                        antagonist.key,
                        "antagonist-icon",
                      ])} />

                      {isBanned && (
                        <Box className="antagonist-banned-slash" />
                      )}

                      {daysLeft > 0 && (
                        <Box className="antagonist-days-left">
                          {daysLeft} days left
                        </Box>
                      )}
                    </Box>
                  </Tooltip>
                </Stack.Item>
              </Stack>
            </Flex.Item>
          );
        })}
      </Flex>

  );
};

export const AntagsPage = () => {
  return (
    <Box className="PreferencesMenu__Antags">
      <AntagSelection
        name="Roundstart"
        antagonists={antagsByCategory.get(Category.Roundstart)!}
      />

      <AntagSelection
        name="Midround"
        antagonists={antagsByCategory.get(Category.Midround)!}
      />

      <AntagSelection
        name="Latejoin"
        antagonists={antagsByCategory.get(Category.Latejoin)!}
      />
    </Box>
  );
};


code:
import { BlockQuote, Stack, Box, Tabs, Section, Button, Dropdown } from "../../components";
import { useBackend, useLocalState } from "../../backend";
import { AugmentItem, PreferencesMenuData } from "./data";

const AugmentStackItem = (props: {
  removal: boolean;
  our_item: AugmentItem;
}, context) => {
  const { act, data } = useBackend<PreferencesMenuData>(context);
  const maxmarkings = data.maximum_markings_per_limb;
  return (
    <Stack.Item>

        {props.our_item.description}

        {props.removal && (
          <Box>
            {props.our_item.can_use_styles && (
              <Dropdown
                mt={1}
                mr={2}
                displayText={data.selected_augments_styles[props.our_item.slot]
                  ? data.selected_augments_styles[props.our_item.slot] : "Styles"}
                options={data.unselected_augments_styles}
                onSelected={(value) => act("set_augment_style", {
                  augment_item: props.our_item.name,
                  augment_slot: props.our_item.slot,
                  augment_style: value,
                })}
              />
            ) || (
              <Box />
            )}
            <Button
              icon="dollar-sign"
              tooltipPosition={"top"}
              tooltip={props.our_item.cant_buy}
              disabled={!!props.our_item.cant_buy}
              color={(props.our_item.value > 0) ? "bad" : (props.our_item.value < 0 ? "good" : null)}
              onClick={() => act("remove_augment", {
                augment_item: props.our_item.name,
                augment_slot: props.our_item.slot,
                augment_category: props.our_item.category,
              })}
              mt={1}
              mr={2}
            >
              Remove ({-props.our_item.value})
            </Button>
          </Box>
        ) || (
          <Box>
            <Button
              icon="dollar-sign"
              tooltipPosition={"top"}
              tooltip={props.our_item.cant_buy}
              disabled={!!props.our_item.cant_buy}
              color={(props.our_item.value > 0) ? "good" : (props.our_item.value < 0 ? "bad" : null)}
              onClick={() => act("add_augment", {
                augment_item: props.our_item.name,
                augment_slot: props.our_item.slot,
                augment_category: props.our_item.category,
              })}
              mt={2}
              mr={2}
            >
              Buy ({props.our_item.value})
            </Button>
          </Box>
        )}

    </Stack.Item>
  );
};

export const AugmentsPage = (props, context) => {
  const { data } = useBackend<PreferencesMenuData>(context);
  let [currentCategory, setCurrentCategory] = useLocalState(context, 'currentCategory', data.unselected_augments[0]);
  let [currentSlot, setCurrentSlot] = useLocalState(context, 'currentSlot', currentCategory.slots[0]);

  const remainingpoints = "Remaining customization points: " + data.augment_balance + "/" + data.maximum_customization_points;

  return (
    <Stack
      vertical>
      <Stack.Item>

          <Box mb={1.5} bold>
            {remainingpoints}
          </Box>
          <Box>
            Here you can buy augments with customization points.

            You can get more points with negative augments,
            but you can only have one augment per augment slot!
          </Box>

      </Stack.Item>
      <Stack.Item>
        <Tabs
          fill
        >
          {data.unselected_augments.map(category => (
            <Tabs.Tab
              key={category.name}
              selected={category === currentCategory}
              onClick={() => {
                setCurrentCategory(category);
                setCurrentSlot(category.slots[0]);
              }}
            >
              {category.name}
            </Tabs.Tab>
          ))}
        </Tabs>
      </Stack.Item>
      <Stack.Item>

          <Stack
            height={40}
            vertical={false}>
            <Stack.Item
              width={"10%"}
              height={"100%"}
            >
              <Tabs
                fill
                vertical>
                {currentCategory.slots.map(slot => (
                  <Tabs.Tab
                    key={slot.name}
                    selected={slot === currentSlot}
                    onClick={() => setCurrentSlot(slot)}
                  >
                    {slot.name}
                  </Tabs.Tab>
                ))}
              </Tabs>
            </Stack.Item>
            <Stack.Item
              width={"60%"}
              height={"100%"}
            >

                <Stack
                  vertical>
                  {currentSlot.items.map(item => (
                    <AugmentStackItem
                      key={item.name}
                      our_item={item}
                      removal={false}
                    />
                  ))}
                </Stack>

            </Stack.Item>
            <Stack.Item
              width={"30%"}
              height={"100%"}
            >

                {!(data.selected_augments.length) && (
                  <Box>
                    No augments selected.
                  </Box>
                )}
                <Stack
                  vertical>
                  {data.selected_augments.map(augslot => (
                    <Stack.Item
                      key={augslot.name}>
                      <Stack
                        vertical>
                        <Stack.Item>

                            {augslot.name}

                        </Stack.Item>
                        <AugmentStackItem
                          our_item={augslot.item}
                          removal
                        />
                      </Stack>
                    </Stack.Item>
                  ))}
                </Stack>

            </Stack.Item>
          </Stack>

      </Stack.Item>
    </Stack>
  );
};


code:
import { Stack, Box, BlockQuote, Button } from "../../components";
import { useBackend } from "../../backend";
import { Birthsign, PreferencesMenuData } from "./data";

const Gap = (props: {
  amount: number,
}) => {
  // 0.2em comes from the padding-bottom in the department listing
  return <Box height={`calc(${props.amount}px + 0.2em)`} />;
};

const BirthsignBlock = (props: {
  selected: boolean,
  sign: Birthsign
}, context) => {
  const { act, data } = useBackend<PreferencesMenuData>(context);
  return (
    <Stack.Item
      mb={2}>
      <Stack
        vertical={false}>
        <Stack.Item
          height="128px"
          width="128px">
          <Box
            height="100%"
            width="100%"
            className="PreferencesMenu__papericonbox">
            <Box
              height="100%"
              width="100%"
              mr={1}
              className={'birthsigns128x128 ' + props.sign.icon}
            />
          </Box>
        </Stack.Item>
        <Stack.Item
          width="85%"
          height="128px">
          <Stack
            width="100%"
            height="100%"
            vertical>
            <Stack.Item
              height="25%">
              <Box
                height="100%"
                width="100%"
                className="PreferencesMenu__papersplease__header__dotted"
                style={{
                  "font-size": "150%",
                  "padding-left": "3px",
                }}
              >
                {props.sign.name}
                {props.sign.patron_saint ? " (" + props.sign.patron_saint + ")" : null}
                {!props.selected && (
                  <Button
                    tooltip={props.sign.cant_buy}
                    tooltipPosition={"bottom"}
                    disabled={!!props.sign.cant_buy}
                    color="paperplease"
                    ml={2}
                    height="90%"
                    style={{
                      "font-size": "75%",
                    }}
                    onClick={() => act("select_birthsign", {
                      birthsign_name: props.sign.name,
                    })}>
                    Select ({props.sign.value})
                  </Button>
                ) || (
                  <Button
                    disabled
                    color="paperplease"
                    ml={2}
                    height="90%"
                    style={{
                      "font-size": "75%",
                    }}>
                    Cost ({props.sign.value})
                  </Button>
                )}
              </Box>
            </Stack.Item>
            <Stack.Item
              height="75%">
              <Box
                height="100%"
                width="100%"
                className="PreferencesMenu__papersplease__dotted">
                {props.sign.description}
              </Box>
            </Stack.Item>
          </Stack>
        </Stack.Item>
      </Stack>
    </Stack.Item>
  );
};

export const BackgroundPage = (props, context) => {
  const { data } = useBackend<PreferencesMenuData>(context);

  const remainingpoints = "Remaining customization points: " + data.birthsign_balance + "/" + data.maximum_customization_points;

  return (
    <Stack
      vertical>
      <Stack.Item mb={1}>

          <Box mb={1.5} bold>
            {remainingpoints}
          </Box>
          <Box>
            Here you can choose a birthsign for your character.

            In Nevado, mysticism is prevalent, and not just fiction -
            Most birthsigns cost nothing, and are small
            sidegrades for your character.
          </Box>

      </Stack.Item>
      <Stack.Item
        height={18}>
        <Box
          height="100%">
          <Box
            height="20%"
            className="PreferencesMenu__papersplease__header__center">
            <Box
              textAlign="center"
              className="PreferencesMenu__papersplease__header__title"
              style={{
                "font-size": "275%",
              }}>
              Selected Birthsign
            </Box>
          </Box>
          <Box
            overflowX="hidden"
            overflowY="scroll"
            height="80%"
            className="PreferencesMenu__papersplease__center">
            {!data.selected_birthsign && (
              <Box
                textAlign="center"
                style={{
                  "padding-top": "10px",
                  "font-size": "200%",
                }}>
                No birthsign!
              </Box>
            ) || (
              <Stack
                vertical>
                <Gap
                  amount={12} />
                <BirthsignBlock
                  selected
                  sign={data.selected_birthsign} />
              </Stack>
            )}
          </Box>
        </Box>
      </Stack.Item>
      <Stack.Item>
        <Gap amount={5} />
      </Stack.Item>
      <Stack.Item
        height={20}>
        <Box
          height="100%">
          <Box
            height="20%"
            className="PreferencesMenu__papersplease__header__center">
            <Box
              textAlign="center"
              className="PreferencesMenu__papersplease__header__title"
              style={{
                "font-size": "275%",
              }}>
              Available Birthsigns
            </Box>
          </Box>
          <Box
            overflowX="hidden"
            overflowY="scroll"
            height="80%"
            className="PreferencesMenu__papersplease__center">
            {!data.unselected_birthsigns && (
              <Box
                textAlign="center"
                style={{
                  "padding-top": "10px",
                  "font-size": "200%",
                }}>
                No birthsigns available!
              </Box>
            ) || (
              <Stack
                vertical>
                <Stack.Item>
                  <Gap
                    amount={12} />
                </Stack.Item>
                {data.unselected_birthsigns.map(birthsign => (
                  <BirthsignBlock
                    selected={false}
                    sign={birthsign}
                    key={birthsign.name}
                  />
                ))}
              </Stack>
            )}
          </Box>
        </Box>
      </Stack.Item>
    </Stack>
  );
};


code:
import { exhaustiveCheck } from "common/exhaustive";
import { useBackend, useLocalState } from "../../backend";
import { Button, Stack } from "../../components";
import { Window } from "../../layouts";
import { PreferencesMenuData } from "./data";
import { PageButton } from "./PageButton";
import { AntagsPage } from "./AntagsPage";
import { JobsPage } from "./JobsPage";
import { MainPage } from "./MainPage";
import { SpeciesPage } from "./SpeciesPage";
import { QuirksPage } from "./QuirksPage";
import { LanguagesPage } from "./LanguagesPage";
import { MarkingsPage } from "./MarkingsPage";
import { AugmentsPage } from "./AugmentsPage";
import { BackgroundPage } from "./BackgroundPage";

enum Page {
  Main,
  Markings,
  Augments,
  Languages,
  Background,
  Jobs,
  Antags,
  Species,
  Quirks,
}

const CharacterProfiles = (props: {
  activeSlot: number,
  onClick: (index: number) => void,
  profiles: (string | null)[],
}) => {
  const { profiles } = props;

  return (
    <Stack justify="center" wrap>
      {profiles.map((profile, slot) => (
        <Stack.Item key={slot}>
          <Button
            selected={slot === props.activeSlot}
            onClick={() => {
              props.onClick(slot);
            }} fluid>{profile ?? "New Character"}
          </Button>
        </Stack.Item>
      ))}
    </Stack>
  );
};

export const CharacterPreferenceWindow = (props, context) => {
  const { act, data } = useBackend<PreferencesMenuData>(context);

  const [currentPage, setCurrentPage] = useLocalState(context, "currentPage", Page.Main);

  let pageContents;

  switch (currentPage) {
    case Page.Antags:
      pageContents = <AntagsPage />;
      break;
    case Page.Jobs:
      pageContents = <JobsPage />;
      break;
    case Page.Main:
      pageContents = (<MainPage
        openSpecies={() => setCurrentPage(Page.Species)}
      />);
      break;
    case Page.Species:
      pageContents = (<SpeciesPage
        closeSpecies={() => setCurrentPage(Page.Main)}
      />);
      break;
    case Page.Quirks:
      pageContents = <QuirksPage />;
      break;
    case Page.Languages:
      pageContents = <LanguagesPage />;
      break;
    case Page.Markings:
      pageContents = <MarkingsPage />;
      break;
    case Page.Augments:
      pageContents = <AugmentsPage />;
      break;
    case Page.Background:
      pageContents = <BackgroundPage />;
      break;
    default:
      exhaustiveCheck(currentPage);
  }

  return (
    <Window
      title="Character Preferences"
      width={1000}
      height={770}
    >
      <Window.Content scrollable>
        <Stack vertical fill>
          <Stack.Item>
            <CharacterProfiles
              activeSlot={data.active_slot - 1}
              onClick={(slot) => {
                act("change_slot", {
                  slot: slot + 1,
                });
              }}
              profiles={data.character_profiles}
            />
          </Stack.Item>

          {!data.content_unlocked && (
            <Stack.Item align="center">
              Buy BYOND premium for an extra 5 slots!
            </Stack.Item>
          )}
          {!data.donator_rank && (
            <Stack.Item align="center">
              Support Septic Shock on patreon for an extra 5 slots!
            </Stack.Item>
          )}

          <Stack.Divider />

          <Stack.Item>
            <Stack fill>
              <Stack.Item grow>
                <PageButton
                  currentPage={currentPage}
                  page={Page.Main}
                  setPage={setCurrentPage}
                  otherActivePages={[Page.Species]}
                >
                  General
                </PageButton>
              </Stack.Item>

              <Stack.Item grow>
                <PageButton
                  currentPage={currentPage}
                  page={Page.Markings}
                  setPage={setCurrentPage}
                >
                  Markings
                </PageButton>
              </Stack.Item>

              <Stack.Item grow>
                <PageButton
                  currentPage={currentPage}
                  page={Page.Augments}
                  setPage={setCurrentPage}
                >
                  Augments
                </PageButton>
              </Stack.Item>

              <Stack.Item grow>
                <PageButton
                  currentPage={currentPage}
                  page={Page.Languages}
                  setPage={setCurrentPage}
                >
                  Languages
                </PageButton>
              </Stack.Item>

              <Stack.Item grow>
                <PageButton
                  currentPage={currentPage}
                  page={Page.Background}
                  setPage={setCurrentPage}
                >
                  Background
                </PageButton>
              </Stack.Item>

              <Stack.Item grow>
                <PageButton
                  currentPage={currentPage}
                  page={Page.Jobs}
                  setPage={setCurrentPage}
                >
                  Occupations
                </PageButton>
              </Stack.Item>

              <Stack.Item grow>
                <PageButton
                  currentPage={currentPage}
                  page={Page.Antags}
                  setPage={setCurrentPage}
                >
                  Antagonism
                </PageButton>
              </Stack.Item>
            </Stack>
          </Stack.Item>

          <Stack.Divider />

          <Stack.Item>
            {pageContents}
          </Stack.Item>
        </Stack>
      </Window.Content>
    </Window>
  );
};


code:
import { ByondUi } from "../../components";

export const CharacterPreview = (props: {
  height: string,
  id: string,
}) => {
  return (<ByondUi
    width="220px"
    height={props.height}
    params={{
      id: props.id,
      type: "map",
    }}
  />);
};


code:
import { Stack } from "../../components";
import { Window } from "../../layouts";
import { KeybindingsPage } from "./KeybindingsPage";
import { GamePreferencesPage } from "./GamePreferencesPage";
import { PageButton } from "./PageButton";
import { useBackend, useLocalState } from "../../backend";
import { GamePreferencesSelectedPage, PreferencesMenuData } from "./data";
import { exhaustiveCheck } from "common/exhaustive";

export const GamePreferenceWindow = (props: {
  startingPage?: GamePreferencesSelectedPage,
}, context) => {
  const { act, data } = useBackend<PreferencesMenuData>(context);

  const [currentPage, setCurrentPage]
    = useLocalState(
      context,
      "currentPage",
      props.startingPage ?? GamePreferencesSelectedPage.Settings,
    );

  let pageContents;

  switch (currentPage) {
    case GamePreferencesSelectedPage.Keybindings:
      pageContents = <KeybindingsPage />;
      break;
    case GamePreferencesSelectedPage.Settings:
      pageContents = <GamePreferencesPage />;
      break;
    default:
      exhaustiveCheck(currentPage);
  }

  return (
    <Window
      title="Game Preferences"
      width={920}
      height={770}
    >
      <Window.Content>
        <Stack vertical fill>
          <Stack.Item>
            <Stack fill>
              <Stack.Item grow>
                <PageButton
                  currentPage={currentPage}
                  page={GamePreferencesSelectedPage.Settings}
                  setPage={setCurrentPage}
                >
                  Settings
                </PageButton>
              </Stack.Item>

              <Stack.Item grow>
                <PageButton
                  currentPage={currentPage}
                  page={GamePreferencesSelectedPage.Keybindings}
                  setPage={setCurrentPage}
                >
                  Keybindings
                </PageButton>
              </Stack.Item>
            </Stack>
          </Stack.Item>

          <Stack.Divider />

          <Stack.Item grow shrink basis="1px">
            {pageContents}
          </Stack.Item>
        </Stack>
      </Window.Content>
    </Window>
  );
};


code:
import { binaryInsertWith, sortBy } from "common/collections";
import { InfernoNode } from "inferno";
import { useBackend } from "../../backend";
import { Box, Flex, Tooltip } from "../../components";
import { PreferencesMenuData } from "./data";
import features from "./preferences/features";
import { FeatureValueInput } from "./preferences/features/base";
import { TabbedMenu } from "./TabbedMenu";

type PreferenceChild = {
  name: string,
  children: InfernoNode,
};

const binaryInsertPreference = binaryInsertWith<PreferenceChild>(
  (child) => child.name,
);

const sortByName = sortBy<[string, PreferenceChild[]]>(([name]) => name);

export const GamePreferencesPage = (props, context) => {
  const { act, data } = useBackend<PreferencesMenuData>(context);

  const gamePreferences: Record<string, PreferenceChild[]> = {};

  for (const [featureId, value] of Object.entries(
    data.character_preferences.game_preferences
  )) {
    const feature = features[featureId];

    let nameInner: InfernoNode = feature?.name || featureId;

    if (feature?.description) {
      nameInner = (
        <Box as="span" style={{
          "border-bottom": "2px dotted rgba(255, 255, 255, 0.8)",
        }}>
          {nameInner}
        </Box>
      );
    }

    let name: InfernoNode = (
      <Flex.Item grow={1} pr={2} basis={0} ml={2}>
        {nameInner}
      </Flex.Item>
    );

    if (feature?.description) {
      name = (
        <Tooltip content={feature.description} position="bottom-start">
          {name}
        </Tooltip>
      );
    }

    const child = (
      <Flex align="center" key={featureId} pb={2}>
        {name}

        <Flex.Item grow={1} basis={0}>
          {feature && <FeatureValueInput
            feature={feature}
            featureId={featureId}
            value={value}
            act={act}
          /> || (
            <Box as="b" color="red">
              ...is not filled out properly!!!
            </Box>
          )}
        </Flex.Item>
      </Flex>
    );

    const entry = {
      name: feature?.name || featureId,
      children: child,
    };

    const category = feature?.category || "ERROR";

    gamePreferences[category]
      = binaryInsertPreference(gamePreferences[category] || [], entry);
  }

  const gamePreferenceEntries: [string, InfernoNode][] = sortByName(
    Object.entries(gamePreferences)
  ).map(
    ([category, preferences]) => {
      return [category, preferences.map(entry => entry.children)];
    });

  return (
    <TabbedMenu
      categoryEntries={gamePreferenceEntries}
      contentProps={{
        fontSize: 1.5,
      }}
    />
  );
};


code:
import { binaryInsertWith } from "common/collections";
import { classes } from "common/react";
import { InfernoNode } from "inferno";
import { useBackend } from "../../backend";
import { Box, Button, Dropdown, Stack, Tooltip, Icon } from "../../components";
import { createSetPreference, JoblessRole, JobPriority, PreferencesMenuData } from "./data";
import { Job } from "./occupations/base";
import * as Departments from "./occupations/departments";

const requireJob = require.context("./occupations/jobs", false, /.ts$/);
const jobsByDepartment = new Map<Departments.Department, {
  jobs: Job[],
  head?: Job,
}>();

const binaryInsertJob = binaryInsertWith((job: Job) => {
  return job.name;
});

const PRIORITY_BUTTON_SIZE = "18px";

for (const jobKey of requireJob.keys()) {
  const job = requireJob<{
    default?: Job,
  }>(jobKey).default;

  if (!job) {
    continue;
  }

  let departmentInfo = jobsByDepartment.get(job.department);
  if (departmentInfo === undefined) {
    departmentInfo = {
      jobs: [],
      head: undefined,
    };

    jobsByDepartment.set(job.department, departmentInfo);
  }

  if (job.department.head === job.name) {
    departmentInfo.head = job;
  } else {
    departmentInfo.jobs = binaryInsertJob(departmentInfo.jobs, job);
  }
}

const PriorityButton = (props: {
  name: string,
  color: string,
  modifier?: string,
  enabled: boolean,
  onClick: () => void,
}) => {
  const className = `PreferencesMenu__Jobs__departments__priority`;

  return (
    <Stack.Item height={PRIORITY_BUTTON_SIZE}>
      <Button
        className={classes([
          className,
          props.modifier && `${className}--${props.modifier}`,
        ])}
        color={props.enabled ? props.color : "null"}
        circular
        onClick={props.onClick}
        tooltip={props.name}
        tooltipPosition="bottom"
        height={PRIORITY_BUTTON_SIZE}
        width={PRIORITY_BUTTON_SIZE}
      />
    </Stack.Item>
  );
};

type CreateSetPriority = (priority: JobPriority | null) => () => void;

const createSetPriorityCache: Record<string, CreateSetPriority> = {};

const createCreateSetPriorityFromName
  = (context, jobName: string): CreateSetPriority => {
    if (createSetPriorityCache[jobName] !== undefined) {
      return createSetPriorityCache[jobName];
    }

    const perPriorityCache: Map<JobPriority | null, () => void> = new Map();

    const createSetPriority = (priority: JobPriority | null) => {
      const existingCallback = perPriorityCache.get(priority);
      if (existingCallback !== undefined) {
        return existingCallback;
      }

      const setPriority = () => {
        const { act } = useBackend<PreferencesMenuData>(context);

        act("set_job_preference", {
          job: jobName,
          level: priority,
        });
      };

      perPriorityCache.set(priority, setPriority);
      return setPriority;
    };

    createSetPriorityCache[jobName] = createSetPriority;

    return createSetPriority;
  };

const PriorityHeaders = () => {
  const className = "PreferencesMenu__Jobs__PriorityHeader";

  return (
    <Stack>
      <Stack.Item grow />

      <Stack.Item className={className}>
        Off
      </Stack.Item>

      <Stack.Item className={className}>
        Low
      </Stack.Item>

      <Stack.Item className={className}>
        Medium
      </Stack.Item>

      <Stack.Item className={className}>
        High
      </Stack.Item>
    </Stack>
  );
};

const PriorityButtons = (props: {
  createSetPriority: CreateSetPriority,
  isOverflow: boolean,
  priority: JobPriority,
}) => {
  const { createSetPriority, isOverflow, priority } = props;

  return (
    <Stack
      style={{
        "align-items": "center",
        "height": "100%",
        "justify-content": "flex-end",
        "padding-left": "0.3em",
        "padding-right": "0.3em",
      }}
    >
      {isOverflow
        ? (
          <>
            <PriorityButton
              name="Off"
              modifier="off"
              color="paperplease"
              enabled={!priority}
              onClick={createSetPriority(null)}
            />

            <PriorityButton
              name="On"
              color="paperplease"
              enabled={!!priority}
              onClick={createSetPriority(JobPriority.High)}
            />
          </>
        )
        : (
          <>
            <PriorityButton
              name="Off"
              modifier="off"
              color="paperplease"
              enabled={!priority}
              onClick={createSetPriority(null)}
            />

            <PriorityButton
              name="Low"
              color="paperplease"
              enabled={priority === JobPriority.Low}
              onClick={createSetPriority(JobPriority.Low)}
            />

            <PriorityButton
              name="Medium"
              color="paperplease"
              enabled={priority === JobPriority.Medium}
              onClick={createSetPriority(JobPriority.Medium)}
            />

            <PriorityButton
              name="High"
              color="paperplease"
              enabled={priority === JobPriority.High}
              onClick={createSetPriority(JobPriority.High)}
            />
          </>
        )}
    </Stack>
  );
};

const JobRow = (props: {
  className?: string,
  job: Job,
}, context) => {
  const { data } = useBackend<PreferencesMenuData>(context);
  const { job } = props;

  const isOverflow = data.overflow_role === job.name;
  const priority = data.job_preferences[job.name];

  const createSetPriority = createCreateSetPriorityFromName(context, job.name);

  const experienceNeeded = data.job_required_experience
    && data.job_required_experience[job.name];
  const daysLeft = data.job_days_left ? data.job_days_left[job.name] : 0;

  let rightSide: InfernoNode;

  if (experienceNeeded) {
    const { experience_type, required_playtime } = experienceNeeded;
    const hoursNeeded = Math.ceil(required_playtime / 60);

    rightSide = (
      <Stack align="center" height="100%" pr={1}>
        <Stack.Item grow textAlign="right">
          {hoursNeeded}h as {experience_type}
        </Stack.Item>
      </Stack>
    );
  } else if (daysLeft > 0) {
    rightSide = (
      <Stack align="center" height="100%" pr={1}>
        <Stack.Item grow textAlign="right">
          {daysLeft} day{daysLeft === 1 ? "" : "s"} left
        </Stack.Item>
      </Stack>
    );
  } else if (data.job_bans && data.job_bans.indexOf(job.name) !== -1) {
    rightSide = (
      <Stack align="center" height="100%" pr={1}>
        <Stack.Item grow textAlign="right">
          Banned
        </Stack.Item>
      </Stack>
    );
  } else {
    rightSide = (<PriorityButtons
      createSetPriority={createSetPriority}
      isOverflow={isOverflow}
      priority={priority}
    />);
  }

  return (
    <Stack.Item className={props.className} height="100%" style={{
      "margin-top": 0,
    }}>
      <Stack fill align="center">
        <Tooltip
          content={job.description}
          position="bottom-start"
        >
          <Stack.Item className="job-name" width="50%" style={{
            "padding-left": "0.3em",
          }}>

            {props.job.name}
          </Stack.Item>
        </Tooltip>

        <Stack.Item grow className="options">
          {rightSide}
        </Stack.Item>
      </Stack>
    </Stack.Item>
  );
};

const Department = (props: {
  department: Departments.Department,
  name: string,
}) => {
  const { department, name } = props;
  const jobs = jobsByDepartment.get(department);
  const className = `PreferencesMenu__Jobs__departments--${name}`;

  if (!jobs) {
    return (
      <Box color="red">
        ERROR: Department {name} could not be found!
      </Box>
    );
  }

  return (
    <Box>
      <Stack
        vertical
        fill>
        {jobs.head
          && <JobRow className={`${className} head`} job={jobs.head} />}
        {jobs.jobs.map((job) => {
          if (job === jobs.head) {
            return null;
          }

          return <JobRow className={className} key={job.name} job={job} />;
        })}
      </Stack>
    </Box>
  );
};

// *Please* find a better way to do this, this is RIDICULOUS.
// All I want is for a gap to pretend to be an empty space.
// But in order for everything to align, I also need to add the 0.2em padding.
// But also, we can't be aligned with names that break into multiple lines!
const Gap = (props: {
  amount: number,
}) => {
  // 0.2em comes from the padding-bottom in the department listing
  return <Box height={`calc(${props.amount}px + 0.2em)`} />;
};

const JoblessRoleDropdown = (props, context) => {
  const { act, data } = useBackend<PreferencesMenuData>(context);
  const selected = data.character_preferences.misc.joblessrole;

  const options = [
    {
      displayText: `Join as ${data.overflow_role} if unavailable`,
      value: JoblessRole.BeOverflow,
    },
    {
      displayText: `Join as a random job if unavailable`,
      value: JoblessRole.BeRandomJob,
    },
    {
      displayText: `Return to lobby if unavailable`,
      value: JoblessRole.ReturnToLobby,
    },
  ];

  return (
    <Box
      className="PreferencesMenu__Jobs__joblessdropdown"
      position="absolute"
      right={1}
      width="30%"
    >
      <Dropdown
        color="paperplease"
        width="100%"
        selected={selected}
        onSelected={createSetPreference(act, "joblessrole")}
        options={options}
        displayText={
          <Box pr={1}>
            {options.find(option => option.value === selected)!.displayText}
          </Box>
        }
      />
    </Box>
  );
};

const FancyText = (props: {
  text: string,
  fontsize: string,
}) => {
  return (
    <Box
      textAlign="center"
      style={{
        "font-size": props.fontsize,
      }}>
      {props.text}
    </Box>
  );
};

export const JobsPage = () => {
  return (
    <Box>
      <Gap amount={12} />
      <JoblessRoleDropdown />
      <Stack vertical fill>
        <Gap amount={24} />

        <Stack.Item>
          <Stack fill className="PreferencesMenu__Jobs">
            <Stack.Item>
              <Box
                height="12.5%"
                className="PreferencesMenu__papersplease__header__left"
                textAlign="center">
                <Box className="PreferencesMenu__papersplease__header__title">
                  <Icon
                    mr={2}
                    height="50%"
                    name="dollar-sign" />
                  Higher Class
                </Box>
              </Box>

              <Box
                height="87.5%"
                className="PreferencesMenu__papersplease__left">
                <FancyText
                  text="Nobility"
                  fontsize="400%" />

                <Gap amount={12} />

                <Department
                  department={Departments.Nobility}
                  name="Nobility" />

                <Gap amount={12} />

                <FancyText
                  text="Bourgeouis"
                  fontsize="400%" />

                <Gap amount={12} />

                <Department
                  department={Departments.Bourgeouis}
                  name="Bourgeouis" />

                <Gap amount={12} />
              </Box>

            </Stack.Item>

            <Stack.Item>
              <Box
                height="12.5%"
                className="PreferencesMenu__papersplease__header__right"
                textAlign="center">
                <Box className="PreferencesMenu__papersplease__header__title">
                  <Icon
                    mr={2}
                    height="50%"
                    name="search-dollar" />
                  Lower Class
                </Box>
              </Box>

              <Box
                height="87.5%"
                className="PreferencesMenu__papersplease__right">
                <FancyText
                  text="Proletariat"
                  fontsize="400%" />

                <Gap amount={12} />

                <Department
                  department={Departments.Proletariat}
                  name="Proletariat" />

                <Gap amount={12} />

                <FancyText
                  text="Unpersons"
                  fontsize="400%" />

                <Gap amount={12} />

                <Department
                  department={Departments.Unpersons}
                  name="Unpersons" />

                <Gap amount={12} />
              </Box>

            </Stack.Item>

          </Stack>
        </Stack.Item>
      </Stack>
    </Box>
  );
};


code:
import { Component } from "inferno";
import { Box, Button, KeyListener, Stack, Tooltip, TrackOutsideClicks } from "../../components";
import { resolveAsset } from "../../assets";
import { PreferencesMenuData } from "./data";
import { useBackend } from "../../backend";
import { range, sortBy } from "common/collections";
import { KeyEvent } from "../../events";
import { TabbedMenu } from "./TabbedMenu";
import { fetchRetry } from "../../http";

type Keybinding = {
  name: string;
  description?: string;
};

type Keybindings = Record<string, Record<string, Keybinding>>;

type KeybindingsPageState = {
  keybindings?: Keybindings;
  lastKeyboardEvent?: KeyboardEvent;
  selectedKeybindings?: PreferencesMenuData["keybindings"];

  /**
   * The current hotkey that the user is rebinding.
   *
   * First element is the hotkey name, the second is the slot.
   */
  rebindingHotkey?: [string, number];
};

const isStandardKey = (event: KeyboardEvent): boolean => {
  return event.key !== "Alt"
    && event.key !== "Control"
    && event.key !== "Shift"
    && event.key !== "Esc";
};

const KEY_CODE_TO_BYOND: Record<string, string> = {
  "DEL": "Delete",
  "DOWN": "South",
  "END": "Southwest",
  "HOME": "Northwest",
  "INSERT": "Insert",
  "LEFT": "West",
  "PAGEDOWN": "Southeast",
  "PAGEUP": "Northeast",
  "RIGHT": "East",
  "SPACEBAR": "Space",
  "UP": "North",
};

const sortKeybindings = sortBy(
  ([_, keybinding]: [string, Keybinding]) => {
    return keybinding.name;
  });

const sortKeybindingsByCategory = sortBy(
  ([category, _]: [string, Record<string, Keybinding>]) => {
    return category;
  });

const formatKeyboardEvent = (event: KeyboardEvent): string => {
  let text = "";

  if (event.altKey) {
    text += "Alt";
  }

  if (event.ctrlKey) {
    text += "Ctrl";
  }

  if (event.shiftKey) {
    text += "Shift";
  }

  if (event.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD) {
    text += "Numpad";
  }

  if (isStandardKey(event)) {
    const key = event.key.toUpperCase();
    text += KEY_CODE_TO_BYOND[key] || key;
  }

  return text;
};

const moveToBottom = (entries: [string, unknown][], findCategory: string) => {
  entries.push(
    entries.splice(
      entries.findIndex(([category, _]) => {
        return category === findCategory;
      }), 1
    )[0]
  );
};

class KeybindingButton extends Component<{
  currentHotkey?: string,
  onClick?: () => void,
  typingHotkey?: string,
}> {
  shouldComponentUpdate(nextProps) {
    return this.props.typingHotkey !== nextProps.typingHotkey
      || this.props.currentHotkey !== nextProps.currentHotkey;
  }

  render() {
    const {
      currentHotkey,
      onClick,
      typingHotkey,
    } = this.props;

    const child = (
      <Button
        fluid
        textAlign="center"
        captureKeys={typingHotkey === undefined}
        onClick={onClick}
        selected={typingHotkey !== undefined}
      >
        {typingHotkey || currentHotkey || "Unbound"}
      </Button>
    );

    if (typingHotkey && onClick) {
      return (
        // onClick will cancel it
        <TrackOutsideClicks onOutsideClick={onClick}>
          {child}
        </TrackOutsideClicks>
      );
    } else {
      return child;
    }
  }
}

const KeybindingName = (props: {
  keybinding: Keybinding,
}) => {
  const { keybinding } = props;

  return keybinding.description
    ? (
      <Tooltip
        content={keybinding.description}
        position="bottom"
      >
        <Box as="span" style={{
          "border-bottom": "2px dotted rgba(255, 255, 255, 0.8)",
        }}>
          {keybinding.name}
        </Box>
      </Tooltip>
    )
    : <span>{keybinding.name}</span>;
};

KeybindingName.defaultHooks = {
  onComponentShouldUpdate: (lastProps, nextProps) => {
    return lastProps.keybinding !== nextProps.keybinding;
  },
};

const ResetToDefaultButton = (props: {
  keybindingId: string,
}, context) => {
  const { act } = useBackend<PreferencesMenuData>(context);

  return (
    <Button
      fluid
      textAlign="center"
      onClick={() => {
        act("reset_keybinds_to_defaults", {
          keybind_name: props.keybindingId,
        });
      }}
    >
      Reset to Defaults
    </Button>
  );
};

export class KeybindingsPage extends Component<{}, KeybindingsPageState> {
  cancelNextKeyUp?: number;
  keybindingOnClicks: Record<string, (() => void)[]> = {};
  lastKeybinds?: PreferencesMenuData["keybindings"];

  state: KeybindingsPageState = {
    lastKeyboardEvent: undefined,
    keybindings: undefined,
    selectedKeybindings: undefined,
    rebindingHotkey: undefined,
  };

  constructor() {
    super();

    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleKeyUp = this.handleKeyUp.bind(this);
  }

  componentDidMount() {
    this.populateSelectedKeybindings();
    this.populateKeybindings();
  }

  componentDidUpdate() {
    const { data } = useBackend<PreferencesMenuData>(this.context);

    // keybindings is static data, so it'll pass `===` checks.
    // This'll change when resetting to defaults.
    if (data.keybindings !== this.lastKeybinds) {
      this.populateSelectedKeybindings();
    }
  }

  setRebindingHotkey(value?: string) {
    const { act } = useBackend<PreferencesMenuData>(this.context);

    this.setState((state) => {
      let selectedKeybindings = state.selectedKeybindings;
      if (!selectedKeybindings) {
        return state;
      }

      if (!state.rebindingHotkey) {
        return state;
      }

      selectedKeybindings = { ...selectedKeybindings };

      const [keybindName, slot] = state.rebindingHotkey;

      if (selectedKeybindings[keybindName]) {
        if (value) {
          selectedKeybindings[keybindName][Math.min(
            selectedKeybindings[keybindName].length,
            slot
          )] = value;
        } else {
          selectedKeybindings[keybindName].splice(slot, 1);
        }
      } else if (!value) {
        return state;
      } else {
        selectedKeybindings[keybindName] = [value];
      }

      act("set_keybindings", {
        "keybind_name": keybindName,
        "hotkeys": selectedKeybindings[keybindName],
      });

      return {
        lastKeyboardEvent: undefined,
        rebindingHotkey: undefined,
        selectedKeybindings,
      };
    });
  }

  handleKeyDown(keyEvent: KeyEvent) {
    const event = keyEvent.event;
    const rebindingHotkey = this.state.rebindingHotkey;

    if (!rebindingHotkey) {
      return;
    }

    event.preventDefault();

    this.cancelNextKeyUp = keyEvent.code;

    if (isStandardKey(event)) {
      this.setRebindingHotkey(formatKeyboardEvent(event));
      return;
    } else if (event.key === "Esc") {
      this.setRebindingHotkey(undefined);
      return;
    }

    this.setState({
      lastKeyboardEvent: event,
    });
  }

  handleKeyUp(keyEvent: KeyEvent) {
    if (this.cancelNextKeyUp === keyEvent.code) {
      this.cancelNextKeyUp = undefined;
      keyEvent.event.preventDefault();
    }

    const { lastKeyboardEvent, rebindingHotkey } = this.state;

    if (rebindingHotkey && lastKeyboardEvent) {
      this.setRebindingHotkey(formatKeyboardEvent(lastKeyboardEvent));
    }
  }

  getKeybindingOnClick(
    keybindingId: string,
    slot: number,
  ): () => void {
    if (!this.keybindingOnClicks[keybindingId]) {
      this.keybindingOnClicks[keybindingId] = [];
    }

    if (!this.keybindingOnClicks[keybindingId][slot]) {
      this.keybindingOnClicks[keybindingId][slot] = () => {
        if (this.state.rebindingHotkey === undefined) {
          this.setState({
            lastKeyboardEvent: undefined,
            rebindingHotkey: [keybindingId, slot],
          });
        } else {
          this.setState({
            lastKeyboardEvent: undefined,
            rebindingHotkey: undefined,
          });
        }
      };
    }

    return this.keybindingOnClicks[keybindingId][slot];
  }

  getTypingHotkey(keybindingId: string, slot: number): string | undefined {
    const { lastKeyboardEvent, rebindingHotkey } = this.state;

    if (!rebindingHotkey) {
      return undefined;
    }

    if (rebindingHotkey[0] !== keybindingId
        || rebindingHotkey[1] !== slot
    ) {
      return undefined;
    }

    if (lastKeyboardEvent === undefined) {
      return "...";
    }

    return formatKeyboardEvent(lastKeyboardEvent);
  }

  async populateKeybindings() {
    const keybindingsResponse = await fetchRetry(resolveAsset("keybindings.json"));
    const keybindingsData: Keybindings = await keybindingsResponse.json();

    this.setState({
      keybindings: keybindingsData,
    });
  }

  populateSelectedKeybindings() {
    const { data } = useBackend<PreferencesMenuData>(this.context);

    this.lastKeybinds = data.keybindings;

    this.setState({
      selectedKeybindings: Object.fromEntries(
        Object.entries(data.keybindings)
          .map(([keybind, hotkeys]) => {
            return [keybind, hotkeys.filter(value => value !== "Unbound")];
          })
      ),
    });
  }

  render() {
    const { act } = useBackend(this.context);
    const keybindings = this.state.keybindings;

    if (!keybindings) {
      return <Box>Loading keybindings...</Box>;
    }

    const keybindingEntries = sortKeybindingsByCategory(
      Object.entries(keybindings)
    );

    moveToBottom(keybindingEntries, "EMOTE");
    moveToBottom(keybindingEntries, "ADMIN");

    return (
      <>
        <KeyListener
          onKeyDown={this.handleKeyDown}
          onKeyUp={this.handleKeyUp}
        />

        <Stack vertical fill>
          <Stack.Item grow>
            <TabbedMenu
              categoryEntries={keybindingEntries.map(
                ([category, keybindings]) => {
                  return [category, (
                    <Stack key={category} vertical fill>
                      {sortKeybindings(Object.entries(keybindings)).map(
                        ([keybindingId, keybinding]) => {
                          const keys
                        = this.state.selectedKeybindings![keybindingId]
                          || [];

                          const name = (
                            <Stack.Item basis="25%">
                              <KeybindingName keybinding={keybinding} />
                            </Stack.Item>
                          );

                          return (
                            <Stack.Item key={keybindingId}>
                              <Stack fill>
                                {name}

                                {range(0, 3).map(key => (
                                  <Stack.Item key={key} grow basis="10%">
                                    <KeybindingButton
                                      currentHotkey={keys[key]}
                                      typingHotkey={this.getTypingHotkey(
                                        keybindingId,
                                        key,
                                      )}
                                      onClick={this.getKeybindingOnClick(
                                        keybindingId,
                                        key,
                                      )}
                                    />
                                  </Stack.Item>
                                ))}

                                <Stack.Item shrink>
                                  <ResetToDefaultButton
                                    keybindingId={keybindingId} />
                                </Stack.Item>
                              </Stack>
                            </Stack.Item>
                          );
                        }
                      )}
                    </Stack>
                  )];
                }
              )}
            />
          </Stack.Item>

          <Stack.Item align="center">
            <Button.Confirm
              content="Reset all keybindings"
              onClick={() => act("reset_all_keybinds")}
            />
          </Stack.Item>

        </Stack>
      </>
    );
  }
}


code:
import { Stack, Section, Button, Box, BlockQuote } from "../../components";
import { useBackend } from "../../backend";
import { Language, PreferencesMenuData } from "./data";

const LanguageUnderstood = 1;
const LanguageSpoken = 2;

const KnownLanguage = (props: {
  language: Language
}, context) => {
  const { act, data } = useBackend<PreferencesMenuData>(context);
  return (
    <Section
      title={(
        <Box>
          {props.language.name}
          <Box
            ml={1}
            className={'languages16x16 ' + props.language.icon} />
        </Box>
      )}>
      {props.language.description}

      <Button
        mt={2}
        mr={2}
        icon="brain"
        tooltip={props.language.cant_learn_understand ? "Not enough points!" : null}
        tooltipPosition={"top"}
        disabled={!!props.language.cant_learn_understand}
        color={(props.language.understand_type >= LanguageUnderstood) ? "good" : null}
        onClick={() => act("give_language", {
          language_name: props.language.name,
          understand_type: LanguageUnderstood,
          language_balance: data.language_balance,
          already_understood_type: props.language.understand_type,
        })}
      >
        Understood ({props.language.value_understand})
      </Button>
      <Button
        mt={2}
        mr={2}
        icon="volume-up"
        tooltip={props.language.cant_learn_speak}
        tooltipPosition={"top"}
        disabled={!!props.language.cant_learn_speak}
        color={(props.language.understand_type >= LanguageSpoken) ? "good" : null}
        onClick={() => act("give_language", {
          language_name: props.language.name,
          understand_type: LanguageSpoken,
          language_balance: data.language_balance,
          already_understood_type: props.language.understand_type,
        })}
      >
        Spoken ({props.language.value_speak})
      </Button>
      <Button
        mt={2}
        mr={2}
        color="bad"
        icon="times"
        tooltip={props.language.cant_forget}
        tooltipPosition={"top"}
        disabled={!!props.language.cant_forget}
        onClick={() => act("remove_language", {
          language_name: props.language.name,
        })}
      >
        Forget
        (-{props.language.understand_type >= LanguageSpoken
          ? props.language.value_speak : props.language.value_understand})
      </Button>

  );
};

const UnknownLanguage = (props: {
  language: Language
}, context) => {
  const { act, data } = useBackend<PreferencesMenuData>(context);
  return (
    <Section
      title={(
        <Box>
          {props.language.name}
          <Box
            ml={1}
            className={'languages16x16 ' + props.language.icon} />
        </Box>
      )}>
      {props.language.description}

      <Button
        mt={2}
        mr={2}
        icon="brain"
        tooltip={props.language.cant_learn_understand}
        tooltipPosition={"top"}
        disabled={!!props.language.cant_learn_understand}
        onClick={() => act("give_language", {
          language_name: props.language.name,
          understand_type: LanguageUnderstood,
          language_balance: data.language_balance,
          already_understood_type: props.language.understand_type,
        })}
      >
        Understand ({props.language.value_understand})
      </Button>
      <Button
        mt={2}
        mr={2}
        icon="volume-up"
        tooltip={props.language.cant_learn_speak}
        tooltipPosition={"top"}
        disabled={!!props.language.cant_learn_speak}
        onClick={() => act("give_language", {
          language_name: props.language.name,
          understand_type: LanguageSpoken,
          language_balance: data.language_balance,
          already_understood_type: props.language.understand_type,
        })}
      >
        Speak ({props.language.value_speak})
      </Button>

  );
};

export const LanguagesPage = (props, context) => {
  const { data } = useBackend<PreferencesMenuData>(context);

  const remainingpoints = "Remaining customization points: " + data.language_balance + "/" + data.maximum_customization_points;

  return (
    <Stack vertical>
      <Stack.Item>

          <Box mb={1.5} bold>
            {remainingpoints}
          </Box>
          <Box>
            Here you can buy languages with customization points.

            Each language costs 1 point to understand,
            plus 1 point to speak.
          </Box>

      </Stack.Item>
      <Stack.Item>
        <Stack
          vertical={false}>
          <Stack.Item
            grow
            minWidth="50%"
            height={40}>

              <Stack vertical>
                {!data.unselected_languages.length && (
                  <Stack.Item>
                    No languages available.
                  </Stack.Item>
                )}
                {data.unselected_languages.map(language => (
                  <Stack.Item key={language.name}>
                    <UnknownLanguage
                      language={language}
                    />
                  </Stack.Item>
                ))}
              </Stack>

          </Stack.Item>
          <Stack.Item
            fill
            minWidth="50%"
            height={40}>

              <Stack vertical>
                {!data.selected_languages.length && (
                  <Stack.Item>
                    No languages selected.
                  </Stack.Item>
                )}
                {data.selected_languages.map(language => (
                  <Stack.Item key={language.name}>
                    <KnownLanguage
                      language={language}
                    />
                  </Stack.Item>
                ))}
              </Stack>

          </Stack.Item>
        </Stack>
      </Stack.Item>
    </Stack>
  );
};