This commit is contained in:
@@ -0,0 +1,981 @@
|
||||
# Component Reference
|
||||
|
||||
> Notice: This documentation might be out of date, so always check the source
|
||||
> code to see the most up-to-date information.
|
||||
|
||||
<!--
|
||||
This table of contents must be manually maintained.
|
||||
Make sure to add new items to this list if you document new components.
|
||||
-->
|
||||
|
||||
- [General Concepts](#general-concepts)
|
||||
- [`tgui/components`](#tguicomponents)
|
||||
- [`AnimatedNumber`](#animatednumber)
|
||||
- [`BlockQuote`](#blockquote)
|
||||
- [`Box`](#box)
|
||||
- [`Button`](#button)
|
||||
- [`Button.Checkbox`](#buttoncheckbox)
|
||||
- [`Button.Confirm`](#buttonconfirm)
|
||||
- [`Button.Input`](#buttoninput)
|
||||
- [`ByondUi`](#byondui)
|
||||
- [`Collapsible`](#collapsible)
|
||||
- [`ColorBox`](#colorbox)
|
||||
- [`Dimmer`](#dimmer)
|
||||
- [`Divider`](#divider)
|
||||
- [`Dropdown`](#dropdown)
|
||||
- [`Flex`](#flex)
|
||||
- [`Flex.Item`](#flexitem)
|
||||
- [`Grid`](#grid)
|
||||
- [`Grid.Column`](#gridcolumn)
|
||||
- [`Icon`](#icon)
|
||||
- [`Input`](#input)
|
||||
- [`Knob`](#knob)
|
||||
- [`LabeledList`](#labeledlist)
|
||||
- [`LabeledList.Item`](#labeledlistitem)
|
||||
- [`LabeledList.Divider`](#labeledlistdivider)
|
||||
- [`Modal`](#modal)
|
||||
- [`NoticeBox`](#noticebox)
|
||||
- [`NumberInput`](#numberinput)
|
||||
- [`ProgressBar`](#progressbar)
|
||||
- [`Section`](#section)
|
||||
- [`Slider`](#slider)
|
||||
- [`Table`](#table)
|
||||
- [`Table.Row`](#tablerow)
|
||||
- [`Table.Cell`](#tablecell)
|
||||
- [`Tabs`](#tabs)
|
||||
- [`Tabs.Tab`](#tabstab)
|
||||
- [`Tooltip`](#tooltip)
|
||||
- [`tgui/layouts`](#tguilayouts)
|
||||
- [`Window`](#window)
|
||||
- [`Window.Content`](#windowcontent)
|
||||
|
||||
## General Concepts
|
||||
|
||||
These are the components which you can use for interface construction.
|
||||
If you have trouble finding the exact prop you need on a component,
|
||||
please note, that most of these components inherit from other basic
|
||||
components, such as `Box`. This component in particular provides a lot
|
||||
of styling options for all components, e.g. `color` and `opacity`, thus
|
||||
it is used a lot in this framework.
|
||||
|
||||
There are a few important semantics you need to know about:
|
||||
|
||||
- Some elements support a `content` prop, which is a synonym to a
|
||||
`children` prop.
|
||||
- `content` is better used when your element is a self-closing tag
|
||||
(like `<Button content="Hello" />`), and when content is small and simple
|
||||
enough to fit in a prop. Keep in mind, that this prop is **not** native
|
||||
to React, and is only available on these components: `Button`, `Tooltip`.
|
||||
- You should never use `children` explicitly as a prop on an element.
|
||||
Instead open a full tag, and place children or text inside the tag.
|
||||
- Inferno supports both camelcase (`onClick`) and lowercase (`onclick`)
|
||||
event names.
|
||||
- Camel case names are what's called "synthetic" events, and are the
|
||||
*preferred way* of handling events in React, for efficiency and
|
||||
performance reasons. Please read
|
||||
[Inferno Event Handling](https://infernojs.org/docs/guides/event-handling)
|
||||
to understand what this is about.
|
||||
- Lower case names are native browser events and should be used sparingly,
|
||||
for example when you need an explicit IE8 support. **DO NOT** use
|
||||
lowercase event handlers unless you really know what you are doing.
|
||||
- [Button](#button) component does not support lowercase `onclick` event.
|
||||
Use the camel case `onClick` instead.
|
||||
|
||||
## `tgui/components`
|
||||
|
||||
### `AnimatedNumber`
|
||||
|
||||
This component provides animations for numeric values.
|
||||
|
||||
Props:
|
||||
|
||||
- `value: number` - Value to animate.
|
||||
- `initial: number` - Initial value to use in animation when element
|
||||
first appears. If you set initial to `0` for example, number will always
|
||||
animate starting from `0`, and if omitted, it will not play an initial
|
||||
animation.
|
||||
- `format: value => value` - Output formatter.
|
||||
- Example: `value => Math.round(value)`.
|
||||
- `children: (formattedValue, rawValue) => any` - Pull the animated number to
|
||||
animate more complex things deeper in the DOM tree.
|
||||
- Example: `(_, value) => <Icon rotation={value} />`
|
||||
|
||||
### `BlockQuote`
|
||||
|
||||
Just a block quote, just like this example in markdown:
|
||||
|
||||
> Here's an example of a block quote.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
|
||||
### `Box`
|
||||
|
||||
The Box component serves as a wrapper component for most of the CSS utility
|
||||
needs. It creates a new DOM element, a `<div>` by default that can be changed
|
||||
with the `as` property. Let's say you want to use a `<span>` instead:
|
||||
|
||||
```jsx
|
||||
<Box as="span" m={1}>
|
||||
<Button />
|
||||
</Box>
|
||||
```
|
||||
|
||||
This works great when the changes can be isolated to a new DOM element.
|
||||
For instance, you can change the margin this way.
|
||||
|
||||
However, sometimes you have to target the underlying DOM element.
|
||||
For instance, you want to change the text color of the button. The Button
|
||||
component defines its own color. CSS inheritance doesn't help.
|
||||
|
||||
To workaround this problem, the Box children accept a render props function.
|
||||
This way, `Button` can pull out the `className` generated by the `Box`.
|
||||
|
||||
```jsx
|
||||
<Box color="primary">
|
||||
{props => <Button {...props} />}
|
||||
</Box>
|
||||
```
|
||||
|
||||
`Box` units, like width, height and margins can be defined in two ways:
|
||||
- By plain numbers (1 unit equals `0.5em`);
|
||||
- In absolute measures, by providing a full unit string (e.g. `100px`).
|
||||
|
||||
Units which are used in `Box` are `0.5em`, which are half font-size.
|
||||
Default font size is `12px`, so each unit is effectively `6px` in size.
|
||||
If you need more precision, you can always use fractional numbers.
|
||||
|
||||
Props:
|
||||
|
||||
- `as: string` - The component used for the root node.
|
||||
- `color: string` - Applies an atomic `color-<name>` class to the element.
|
||||
- See `styles/atomic/color.scss`.
|
||||
- `width: number` - Box width.
|
||||
- `minWidth: number` - Box minimum width.
|
||||
- `maxWidth: number` - Box maximum width.
|
||||
- `height: number` - Box height.
|
||||
- `minHeight: number` - Box minimum height.
|
||||
- `maxHeight: number` - Box maximum height.
|
||||
- `fontSize: number` - Font size.
|
||||
- `fontFamily: string` - Font family.
|
||||
- `lineHeight: number` - Directly affects the height of text lines.
|
||||
Useful for adjusting button height.
|
||||
- `inline: boolean` - Forces the `Box` to appear as an `inline-block`,
|
||||
or in other words, makes the `Box` flow with the text instead of taking
|
||||
all available horizontal space.
|
||||
- `m: number` - Margin on all sides.
|
||||
- `mx: number` - Horizontal margin.
|
||||
- `my: number` - Vertical margin.
|
||||
- `mt: number` - Top margin.
|
||||
- `mb: number` - Bottom margin.
|
||||
- `ml: number` - Left margin.
|
||||
- `mr: number` - Right margin.
|
||||
- `p: number` - Padding on all sides.
|
||||
- `px: number` - Horizontal padding.
|
||||
- `py: number` - Vertical padding.
|
||||
- `pt: number` - Top padding.
|
||||
- `pb: number` - Bottom padding.
|
||||
- `pl: number` - Left padding.
|
||||
- `pr: number` - Right padding.
|
||||
- `opacity: number` - Opacity, from 0 to 1.
|
||||
- `bold: boolean` - Make text bold.
|
||||
- `italic: boolean` - Make text italic.
|
||||
- `nowrap: boolean` - Stops text from wrapping.
|
||||
- `textAlign: string` - Align text inside the box.
|
||||
- `left` (default)
|
||||
- `center`
|
||||
- `right`
|
||||
- `position: string` - A direct mapping to `position` CSS property.
|
||||
- `relative` - Relative positioning.
|
||||
- `absolute` - Absolute positioning.
|
||||
- `fixed` - Fixed positioning.
|
||||
- `color: string` - An alias to `textColor`.
|
||||
- `textColor: string` - Sets text color.
|
||||
- `#ffffff` - Hex format
|
||||
- `rgba(255, 255, 255, 1)` - RGB format
|
||||
- `purple` - Applies an atomic `color-<name>` class to the element.
|
||||
See `styles/color-map.scss`.
|
||||
- `backgroundColor: string` - Sets background color.
|
||||
- `#ffffff` - Hex format
|
||||
- `rgba(255, 255, 255, 1)` - RGB format
|
||||
|
||||
### `Button`
|
||||
|
||||
Buttons allow users to take actions, and make choices, with a single click.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `fluid: boolean` - Fill all available horizontal space.
|
||||
- `icon: string` - Adds an icon to the button.
|
||||
- `color: string` - Button color, as defined in `variables.scss`.
|
||||
- There is also a special color `transparent` - makes the button
|
||||
transparent and slightly dim when inactive.
|
||||
- `disabled: boolean` - Disables and greys out the button.
|
||||
- `selected: boolean` - Activates the button (gives it a green color).
|
||||
- `tooltip: string` - A fancy, boxy tooltip, which appears when hovering
|
||||
over the button.
|
||||
- `tooltipPosition: string` - Position of the tooltip.
|
||||
- `top` - Show tooltip above the button.
|
||||
- `bottom` (default) - Show tooltip below the button.
|
||||
- `left` - Show tooltip on the left of the button.
|
||||
- `right` - Show tooltip on the right of the button.
|
||||
- `ellipsis: boolean` - If button width is constrained, button text will
|
||||
be truncated with an ellipsis. Be careful however, because this prop breaks
|
||||
the baseline alignment.
|
||||
- `title: string` - A native browser tooltip, which appears when hovering
|
||||
over the button.
|
||||
- `content/children: any` - Content to render inside the button.
|
||||
- `onClick: function` - Called when element is clicked.
|
||||
|
||||
### `Button.Checkbox`
|
||||
|
||||
A ghetto checkbox, made entirely using existing Button API.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Button](#button)
|
||||
- `checked: boolean` - Boolean value, which marks the checkbox as checked.
|
||||
|
||||
### `Button.Confirm`
|
||||
|
||||
A button with a an extra confirmation step, using native button component.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Button](#button)
|
||||
- `confirmMessage: string` - Text to display after first click; defaults to "Confirm?"
|
||||
- `confirmColor: string` - Color to display after first click; defaults to "bad"
|
||||
|
||||
### `Button.Input`
|
||||
|
||||
A button that turns into an input box after the first click. Turns back into a
|
||||
button after the user hits enter, defocuses, or hits escape. Enter and defocus
|
||||
commit, while escape cancels.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `fluid`: fill availible horizontal space
|
||||
- `onCommit: (e, value) => void`: function that is called after the user
|
||||
defocuses the input or presses enter
|
||||
- `currentValue: string`: default string to display when the input is shown
|
||||
- `defaultValue: string`: default value emitted if the user leaves the box
|
||||
blank when hitting enter or defocusing. If left undefined, will cancel the
|
||||
change on a blank defocus/enter
|
||||
|
||||
### `ByondUi`
|
||||
|
||||
Displays a BYOND UI element on top of the browser, and leverages browser's
|
||||
layout engine to position it just like any other HTML element. It is
|
||||
especially useful if you want to display a secondary game map in your
|
||||
interface.
|
||||
|
||||
Example (button):
|
||||
|
||||
```
|
||||
<ByondUi
|
||||
params={{
|
||||
id: 'test_button', // optional, can be auto-generated
|
||||
parent: config.window,
|
||||
type: 'button',
|
||||
text: 'Hello, world!',
|
||||
}} />
|
||||
```
|
||||
|
||||
Example (map):
|
||||
|
||||
```
|
||||
<ByondUi
|
||||
params={{
|
||||
id: 'test_map',
|
||||
parent: config.window,
|
||||
type: 'map',
|
||||
}} />
|
||||
```
|
||||
|
||||
It supports a full set of `Box` properties for layout purposes.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `params: any` - An object with parameters, which are directly passed to
|
||||
the `winset` proc call. You can find a full reference of these parameters
|
||||
in [BYOND controls and parameters guide](https://secure.byond.com/docs/ref/skinparams.html).
|
||||
|
||||
### `Collapsible`
|
||||
|
||||
Displays contents when open, acts as a fluid button when closed. Click to
|
||||
toggle, closed by default.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `children: any` - What is collapsed when closed
|
||||
- `title: string` - Text to display on the button for collapsing
|
||||
- `color: string` - Color of the button; see [Button](#button)
|
||||
- `buttons: any` - Buttons or other content to render inline with the button
|
||||
|
||||
### `ColorBox`
|
||||
|
||||
Displays a 1-character wide colored square. Can be used as a status indicator,
|
||||
or for visually representing a color.
|
||||
|
||||
If you want to set a background color on an element, use a plain
|
||||
[Box](#box) instead.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `color: string` - Color of the box.
|
||||
|
||||
### `Dimmer`
|
||||
|
||||
Dims surrounding area to emphasize content placed inside.
|
||||
|
||||
Content is automatically centered inside the dimmer.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
|
||||
### `Divider`
|
||||
|
||||
Draws a horizontal or vertical line, dividing a section into groups.
|
||||
Works like the good old `<hr>` element, but it's fancier.
|
||||
|
||||
Props:
|
||||
|
||||
- `vertical: boolean` - Divide content vertically.
|
||||
- `hidden: boolean` - Divider can divide content without creating a dividing
|
||||
line.
|
||||
|
||||
### `Dropdown`
|
||||
|
||||
A simple dropdown box component. Lets the user select from a list of options
|
||||
and displays selected entry.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `options: string[]` - An array of strings which will be displayed in the
|
||||
dropdown when open
|
||||
- `selected: string` - Currently selected entry
|
||||
- `width: number` - Width of dropdown button and resulting menu
|
||||
- `over: boolean` - dropdown renders over instead of below
|
||||
- `color: string` - color of dropdown button
|
||||
- `onClick: (e) => void` - Called when dropdown button is clicked
|
||||
- `onSelected: (value) => void` - Called when a value is picked from the list, `value` is the value that was picked
|
||||
|
||||
### `Flex`
|
||||
|
||||
Quickly manage the layout, alignment, and sizing of grid columns, navigation,
|
||||
components, and more with a full suite of responsive flexbox utilities.
|
||||
|
||||
If you are new to or unfamiliar with flexbox, we encourage you to read this
|
||||
[CSS-Tricks flexbox guide](https://css-tricks.com/snippets/css/a-guide-to-flexbox/).
|
||||
|
||||
Consists of two elements: `<Flex>` and `<Flex.Item>`. Both of them provide
|
||||
the most straight-forward mapping to flex CSS properties as possible.
|
||||
|
||||
One of the most basic usage of flex, is to align certain elements
|
||||
to the left, and certain elements to the right:
|
||||
|
||||
```jsx
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
Button description
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} />
|
||||
<Flex.Item>
|
||||
<Button content="Perform an action" />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
```
|
||||
|
||||
Flex item with `grow` property serves as a "filler", to separate the other
|
||||
two flex items as far as possible from each other.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `spacing: number` - Spacing between flex items, in integer units
|
||||
(1 unit - 0.5em). Does not directly relate to a flex css property
|
||||
(adds a modifier class under the hood), and only integer numbers are
|
||||
supported.
|
||||
- `inline: boolean` - Makes flexbox container inline, with similar behavior
|
||||
to an `inline` property on a `Box`.
|
||||
- `direction: string` - This establishes the main-axis, thus defining the
|
||||
direction flex items are placed in the flex container.
|
||||
- `row` (default) - left to right.
|
||||
- `row-reverse` - right to left.
|
||||
- `column` - top to bottom.
|
||||
- `column-reverse` - bottom to top.
|
||||
- `wrap: string` - By default, flex items will all try to fit onto one line.
|
||||
You can change that and allow the items to wrap as needed with this property.
|
||||
- `nowrap` (default) - all flex items will be on one line
|
||||
- `wrap` - flex items will wrap onto multiple lines, from top to bottom.
|
||||
- `wrap-reverse` - flex items will wrap onto multiple lines from bottom to top.
|
||||
- `align: string` - Default alignment of all children.
|
||||
- `stretch` (default) - stretch to fill the container.
|
||||
- `start` - items are placed at the start of the cross axis.
|
||||
- `end` - items are placed at the end of the cross axis.
|
||||
- `center` - items are centered on the cross axis.
|
||||
- `baseline` - items are aligned such as their baselines align.
|
||||
- `justify: string` - This defines the alignment along the main axis.
|
||||
It helps distribute extra free space leftover when either all the flex
|
||||
items on a line are inflexible, or are flexible but have reached their
|
||||
maximum size. It also exerts some control over the alignment of items
|
||||
when they overflow the line.
|
||||
- `flex-start` (default) - items are packed toward the start of the
|
||||
flex-direction.
|
||||
- `flex-end` - items are packed toward the end of the flex-direction.
|
||||
- `space-between` - items are evenly distributed in the line; first item is
|
||||
on the start line, last item on the end line
|
||||
- `space-around` - items are evenly distributed in the line with equal space
|
||||
around them. Note that visually the spaces aren't equal, since all the items
|
||||
have equal space on both sides. The first item will have one unit of space
|
||||
against the container edge, but two units of space between the next item
|
||||
because that next item has its own spacing that applies.
|
||||
- `space-evenly` - items are distributed so that the spacing between any two
|
||||
items (and the space to the edges) is equal.
|
||||
- TBD (not all properties are supported in IE11).
|
||||
|
||||
### `Flex.Item`
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `order: number` - By default, flex items are laid out in the source order.
|
||||
However, the order property controls the order in which they appear in the
|
||||
flex container.
|
||||
- `grow: number` - This defines the ability for a flex item to grow if
|
||||
necessary. It accepts a unitless value that serves as a proportion. It
|
||||
dictates what amount of the available space inside the flex container the
|
||||
item should take up. This number is unit-less and is relative to other
|
||||
siblings.
|
||||
- `shrink: number` - This defines the ability for a flex item to shrink
|
||||
if necessary. Inverse of `grow`.
|
||||
- `basis: string` - This defines the default size of an element before any
|
||||
flex-related calculations are done. Has to be a length (e.g. `20%`, `5rem`),
|
||||
an `auto` or `content` keyword.
|
||||
- **Important:** IE11 flex is buggy, and auto width/height calculations
|
||||
can sometimes end up in a circular dependency. This usually happens, when
|
||||
working with tables inside flex (they have wacky internal widths and such).
|
||||
Setting basis to `0` breaks the loop and fixes all of the problems.
|
||||
- `align: string` - This allows the default alignment (or the one specified by
|
||||
align-items) to be overridden for individual flex items. See: [Flex](#flex).
|
||||
|
||||
### `Grid`
|
||||
|
||||
> **Deprecated:** This component is no longer recommended due to the variety
|
||||
> of bugs that come with table-based layouts.
|
||||
> We recommend using [Flex](#flex) instead.
|
||||
|
||||
Helps you to divide horizontal space into two or more equal sections.
|
||||
It is essentially a single-row `Table`, but with some extra features.
|
||||
|
||||
Example:
|
||||
|
||||
```jsx
|
||||
<Grid>
|
||||
<Grid.Column>
|
||||
<Section title="Section 1">
|
||||
Hello world!
|
||||
</Section>
|
||||
</Grid.Column>
|
||||
<Grid.Column size={2}>
|
||||
<Section title="Section 2">
|
||||
Hello world!
|
||||
</Section>
|
||||
</Grid.Column>
|
||||
</Grid>
|
||||
```
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Table](#table)
|
||||
|
||||
### `Grid.Column`
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Table.Cell](#tablecell)
|
||||
- `size: number` (default: 1) - Size of the column relative to other columns.
|
||||
|
||||
### `Icon`
|
||||
|
||||
Renders one of the FontAwesome icons of your choice.
|
||||
|
||||
```jsx
|
||||
<Icon name="plus" />
|
||||
```
|
||||
|
||||
To smoothen the transition from v4 to v5, we have added a v4 semantic to
|
||||
transform names with `-o` suffixes to FA Regular icons. For example:
|
||||
- `square` will get transformed to `fas square`
|
||||
- `square-o` will get transformed to `far square`
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `name: string` - Icon name.
|
||||
- `size: number` - Icon size. `1` is normal size, `2` is two times bigger.
|
||||
Fractional numbers are supported.
|
||||
- `rotation: number` - Icon rotation, in degrees.
|
||||
- `spin: boolean` - Whether an icon should be spinning. Good for load
|
||||
indicators.
|
||||
|
||||
### `Input`
|
||||
|
||||
A basic text input, which allow users to enter text into a UI.
|
||||
|
||||
> Input does not support custom font size and height due to the way
|
||||
> it's implemented in CSS. Eventually, this needs to be fixed.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `value: string` - Value of an input.
|
||||
- `placeholder: string` - Text placed into Input box when it's empty,
|
||||
otherwise nothing. Clears automatically when focused.
|
||||
- `fluid: boolean` - Fill all available horizontal space.
|
||||
- `selfClear: boolean` - Clear after hitting enter, as well as remain focused
|
||||
when this happens. Useful for things like chat inputs.
|
||||
- `onChange: (e, value) => void` - An event, which fires when you commit
|
||||
the text by either unfocusing the input box, or by pressing the Enter key.
|
||||
- `onInput: (e, value) => void` - An event, which fires on every keypress.
|
||||
|
||||
### `Knob`
|
||||
|
||||
A radial control, which allows dialing in precise values by dragging it
|
||||
up and down.
|
||||
|
||||
Single click opens an input box to manually type in a number.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `animated: boolean` - Animates the value if it was changed externally.
|
||||
- `bipolar: boolean` - Knob can be bipolar or unipolar.
|
||||
- `size: number` - Relative size of the knob. `1` is normal size, `2` is two
|
||||
times bigger. Fractional numbers are supported.
|
||||
- `color: string` - Color of the outer ring around the knob.
|
||||
- `value: number` - Value itself, controls the position of the cursor.
|
||||
- `unit: string` - Unit to display to the right of value.
|
||||
- `minValue: number` - Lowest possible value.
|
||||
- `maxValue: number` - Highest possible value.
|
||||
- `fillValue: number` - If set, this value will be used to set the fill
|
||||
percentage of the outer ring independently of the main value.
|
||||
- `ranges: { color: [from, to] }` - Applies a `color` to the outer ring around
|
||||
the knob based on whether the value lands in the range between `from` and `to`.
|
||||
See an example of this prop in [ProgressBar](#progressbar).
|
||||
- `step: number` (default: 1) - Adjust value by this amount when
|
||||
dragging the input.
|
||||
- `stepPixelSize: number` (default: 1) - Screen distance mouse needs
|
||||
to travel to adjust value by one `step`.
|
||||
- `format: value => value` - Format value using this function before
|
||||
displaying it.
|
||||
- `suppressFlicker: number` - A number in milliseconds, for which the input
|
||||
will hold off from updating while events propagate through the backend.
|
||||
Default is about 250ms, increase it if you still see flickering.
|
||||
- `onChange: (e, value) => void` - An event, which fires when you release
|
||||
the input, or successfully enter a number.
|
||||
- `onDrag: (e, value) => void` - An event, which fires about every 500ms
|
||||
when you drag the input up and down, on release and on manual editing.
|
||||
|
||||
### `LabeledList`
|
||||
|
||||
LabeledList is a continuous, vertical list of text and other content, where
|
||||
every item is labeled. It works just like a two column table, where first
|
||||
column is labels, and second column is content.
|
||||
|
||||
```jsx
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Item">
|
||||
Content
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
```
|
||||
|
||||
If you want to have a button on the right side of an item (for example,
|
||||
to perform some sort of action), there is a way to do that:
|
||||
|
||||
```jsx
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Item"
|
||||
buttons={(
|
||||
<Button content="Click me!" />
|
||||
)}>
|
||||
Content
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
```
|
||||
|
||||
Props:
|
||||
|
||||
- `children: LabeledList.Item` - Items to render.
|
||||
|
||||
### `LabeledList.Item`
|
||||
|
||||
Props:
|
||||
|
||||
- `label: string` - Item label.
|
||||
- `color: string` - Sets the color of the text.
|
||||
- `buttons: any` - Buttons to render aside the content.
|
||||
- `content/children: any` - Content of this labeled item.
|
||||
|
||||
### `LabeledList.Divider`
|
||||
|
||||
Adds some empty space between LabeledList items.
|
||||
|
||||
Example:
|
||||
|
||||
```jsx
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Foo">
|
||||
Content
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider size={1} />
|
||||
</LabeledList>
|
||||
```
|
||||
|
||||
Props:
|
||||
|
||||
- `size: number` - Size of the divider.
|
||||
|
||||
### `Modal`
|
||||
|
||||
A modal window. Uses a [Dimmer](#dimmer) under the hood, and dynamically
|
||||
adjusts its own size to fit the content you're trying to display.
|
||||
|
||||
Must be a direct child of a layout component (e.g. [Window](#window)).
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
|
||||
### `NoticeBox`
|
||||
|
||||
A notice box, which warns you about something very important.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `info: boolean` - Info box
|
||||
- `success: boolean` - Success box
|
||||
- `warning: bolean` - Warning box
|
||||
- `danger: boolean` - Danger box
|
||||
|
||||
### `NumberInput`
|
||||
|
||||
A fancy, interactive number input, which you can either drag up and down
|
||||
to fine tune the value, or single click it to manually type a number.
|
||||
|
||||
Props:
|
||||
|
||||
- `animated: boolean` - Animates the value if it was changed externally.
|
||||
- `fluid: boolean` - Fill all available horizontal space.
|
||||
- `value: number` - Value itself.
|
||||
- `unit: string` - Unit to display to the right of value.
|
||||
- `minValue: number` - Lowest possible value.
|
||||
- `maxValue: number` - Highest possible value.
|
||||
- `step: number` (default: 1) - Adjust value by this amount when
|
||||
dragging the input.
|
||||
- `stepPixelSize: number` (default: 1) - Screen distance mouse needs
|
||||
to travel to adjust value by one `step`.
|
||||
- `width: string|number` - Width of the element, in `Box` units or pixels.
|
||||
- `height: string|numer` - Height of the element, in `Box` units or pixels.
|
||||
- `lineHeight: string|number` - lineHeight of the element, in `Box` units or pixels.
|
||||
- `fontSize: string|number` - fontSize of the element, in `Box` units or pixels.
|
||||
- `format: value => value` - Format value using this function before
|
||||
displaying it.
|
||||
- `suppressFlicker: number` - A number in milliseconds, for which the input
|
||||
will hold off from updating while events propagate through the backend.
|
||||
Default is about 250ms, increase it if you still see flickering.
|
||||
- `onChange: (e, value) => void` - An event, which fires when you release
|
||||
the input, or successfully enter a number.
|
||||
- `onDrag: (e, value) => void` - An event, which fires about every 500ms
|
||||
when you drag the input up and down, on release and on manual editing.
|
||||
|
||||
### `ProgressBar`
|
||||
|
||||
Progress indicators inform users about the status of ongoing processes.
|
||||
|
||||
```jsx
|
||||
<ProgressBar value={0.6} />
|
||||
```
|
||||
|
||||
Usage of `ranges` prop:
|
||||
|
||||
```jsx
|
||||
<ProgressBar
|
||||
ranges={{
|
||||
good: [0.5, Infinity],
|
||||
average: [0.25, 0.5],
|
||||
bad: [-Infinity, 0.25],
|
||||
}}
|
||||
value={0.6} />
|
||||
```
|
||||
|
||||
Props:
|
||||
|
||||
- `value: number` - Current progress as a floating point number between
|
||||
`minValue` (default: 0) and `maxValue` (default: 1). Determines the
|
||||
percentage and how filled the bar is.
|
||||
- `minValue: number` - Lowest possible value.
|
||||
- `maxValue: number` - Highest possible value.
|
||||
- `ranges: { color: [from, to] }` - Applies a `color` to the progress bar
|
||||
based on whether the value lands in the range between `from` and `to`.
|
||||
- `color: string` - Color of the progress bar.
|
||||
- `content/children: any` - Content to render inside the progress bar.
|
||||
|
||||
### `Section`
|
||||
|
||||
Section is a surface that displays content and actions on a single topic.
|
||||
|
||||
They should be easy to scan for relevant and actionable information.
|
||||
Elements, like text and images, should be placed in them in a way that
|
||||
clearly indicates hierarchy.
|
||||
|
||||
Section can also be titled to clearly define its purpose.
|
||||
|
||||
```jsx
|
||||
<Section title="Cargo">
|
||||
Here you can order supply crates.
|
||||
</Section>
|
||||
```
|
||||
|
||||
If you want to have a button on the right side of an section title
|
||||
(for example, to perform some sort of action), there is a way to do that:
|
||||
|
||||
```jsx
|
||||
<Section
|
||||
title="Cargo"
|
||||
buttons={(
|
||||
<Button content="Send shuttle" />
|
||||
)}>
|
||||
Here you can order supply crates.
|
||||
</Section>
|
||||
```
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `title: string` - Title of the section.
|
||||
- `level: number` - Section level in hierarchy. Default is 1, higher number
|
||||
means deeper level of nesting. Must be an integer number.
|
||||
- `buttons: any` - Buttons to render aside the section title.
|
||||
- `content/children: any` - Content of this section.
|
||||
|
||||
### `Slider`
|
||||
|
||||
A horizontal, [ProgressBar](#progressbar)-like control, which allows dialing
|
||||
in precise values by dragging it left and right.
|
||||
|
||||
Single click opens an input box to manually type in a number.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `animated: boolean` - Animates the value if it was changed externally.
|
||||
- `color: string` - Color of the slider.
|
||||
- `value: number` - Value itself, controls the position of the cursor.
|
||||
- `unit: string` - Unit to display to the right of value.
|
||||
- `minValue: number` - Lowest possible value.
|
||||
- `maxValue: number` - Highest possible value.
|
||||
- `fillValue: number` - If set, this value will be used to set the fill
|
||||
percentage of the progress bar filler independently of the main value.
|
||||
- `ranges: { color: [from, to] }` - Applies a `color` to the slider
|
||||
based on whether the value lands in the range between `from` and `to`.
|
||||
See an example of this prop in [ProgressBar](#progressbar).
|
||||
- `step: number` (default: 1) - Adjust value by this amount when
|
||||
dragging the input.
|
||||
- `stepPixelSize: number` (default: 1) - Screen distance mouse needs
|
||||
to travel to adjust value by one `step`.
|
||||
- `format: value => value` - Format value using this function before
|
||||
displaying it.
|
||||
- `suppressFlicker: number` - A number in milliseconds, for which the input
|
||||
will hold off from updating while events propagate through the backend.
|
||||
Default is about 250ms, increase it if you still see flickering.
|
||||
- `onChange: (e, value) => void` - An event, which fires when you release
|
||||
the input, or successfully enter a number.
|
||||
- `onDrag: (e, value) => void` - An event, which fires about every 500ms
|
||||
when you drag the input up and down, on release and on manual editing.
|
||||
|
||||
### `Table`
|
||||
|
||||
A straight forward mapping to a standard html table, which is slightly
|
||||
simplified (does not need a `<tbody>` tag) and with sane default styles
|
||||
(e.g. table width is 100% by default).
|
||||
|
||||
Example:
|
||||
|
||||
```jsx
|
||||
<Table>
|
||||
<Table.Row>
|
||||
<Table.Cell bold>
|
||||
Hello world!
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing color="label">
|
||||
Label
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table>
|
||||
```
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `collapsing: boolean` - Collapses table to the smallest possible size.
|
||||
|
||||
### `Table.Row`
|
||||
|
||||
A straight forward mapping to `<tr>` element.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
|
||||
### `Table.Cell`
|
||||
|
||||
A straight forward mapping to `<td>` element.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `collapsing: boolean` - Collapses table cell to the smallest possible size,
|
||||
and stops any text inside from wrapping.
|
||||
|
||||
### `Tabs`
|
||||
|
||||
Tabs make it easy to explore and switch between different views.
|
||||
|
||||
Here is an example of how you would construct a simple tabbed view:
|
||||
|
||||
```jsx
|
||||
<Tabs>
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 1}
|
||||
onClick={() => setTabIndex(1)}>
|
||||
Tab one
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 2}
|
||||
onClick={() => setTabIndex(2)}>
|
||||
Tab two
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
<Box>
|
||||
Tab selected: {tabIndex}
|
||||
</Box>
|
||||
```
|
||||
|
||||
Notice that tabs do not contain state. It is your job to track the selected
|
||||
tab, handle clicks and place tab content where you need it. In return, you get
|
||||
a lot of flexibility in regards to how you can layout your tabs.
|
||||
|
||||
Tabs also support a vertical configuration. This is usually paired with a
|
||||
[Flex](#flex) component to render tab content to the right.
|
||||
|
||||
```jsx
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
...
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
Tab content.
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
```
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Box](#box)
|
||||
- `vertical: boolean` - Use a vertical configuration, where tabs will be
|
||||
stacked vertically.
|
||||
- `children: Tab[]` - This component only accepts tabs as its children.
|
||||
|
||||
### `Tabs.Tab`
|
||||
|
||||
An individual tab element. Tabs function like buttons, so they inherit
|
||||
a lot of `Button` props.
|
||||
|
||||
Props:
|
||||
|
||||
- See inherited props: [Button](#button)
|
||||
- `altSelection` - Whether the tab buttons select via standard select (color
|
||||
change) or by adding a white indicator to the selected tab.
|
||||
Intended for usage on interfaces where tab color has relevance.
|
||||
- `icon: string` - Tab icon.
|
||||
- `children: any` - Tab text.
|
||||
- `onClick: function` - Called when element is clicked.
|
||||
|
||||
### `Tooltip`
|
||||
|
||||
A boxy tooltip from tgui 1. It is very hacky in its current state, and
|
||||
requires setting `position: relative` on the container.
|
||||
|
||||
Please note, that [Button](#button) component has a `tooltip` prop, and
|
||||
it is recommended to use that prop instead.
|
||||
|
||||
Usage:
|
||||
|
||||
```jsx
|
||||
<Box position="relative">
|
||||
Sample text.
|
||||
<Tooltip
|
||||
position="bottom"
|
||||
content="Box tooltip" />
|
||||
</Box>
|
||||
```
|
||||
|
||||
Props:
|
||||
|
||||
- `position: string` - Tooltip position.
|
||||
- `content/children: string` - Content of the tooltip. Must be a plain string.
|
||||
Fragments or other elements are **not** supported.
|
||||
|
||||
## `tgui/layouts`
|
||||
|
||||
### `Window`
|
||||
|
||||
A root-level component, which draws the window chrome, titlebar, resize
|
||||
handlers, and controls the UI theme. All tgui interfaces must implement
|
||||
it in one way or another.
|
||||
|
||||
Example:
|
||||
|
||||
```jsx
|
||||
<Window
|
||||
theme="hackerman"
|
||||
resizable>
|
||||
<Window.Content scrollable>
|
||||
Hello, world!
|
||||
</Window.Content>
|
||||
</Window>
|
||||
```
|
||||
|
||||
Props:
|
||||
|
||||
- `className: string` - Applies a CSS class to the element.
|
||||
- `theme: string` - A name of the theme.
|
||||
- For a list of themes, see `packages/tgui/styles/themes`.
|
||||
- `resizable: boolean` - Controls resizability of the window.
|
||||
- `children: any` - Child elements, which are rendered directly inside the
|
||||
window. If you use a [Dimmer](#dimmer) or [Modal](#modal) in your UI,
|
||||
they should be put as direct childs of a Window, otherwise you should be
|
||||
putting your content into [Window.Content](#windowcontent).
|
||||
|
||||
### `Window.Content`
|
||||
|
||||
Canonical window content, which is usually the main target of window focus.
|
||||
Can be scrollable.
|
||||
|
||||
Props:
|
||||
|
||||
- `className: string` - Applies a CSS class to the element.
|
||||
- `scrollable: boolean` - Shows or hides the scrollbar.
|
||||
- `children: any` - Main content of your window.
|
||||
@@ -0,0 +1,356 @@
|
||||
# Tutorial and Examples
|
||||
|
||||
## Main concepts
|
||||
|
||||
Basic tgui backend code consists of the following vars and procs:
|
||||
|
||||
```
|
||||
ui_interact(mob/user, ui_key, datum/tgui/ui, force_open,
|
||||
datum/tgui/master_ui, datum/ui_state/state)
|
||||
ui_data(mob/user)
|
||||
ui_act(action, params)
|
||||
```
|
||||
|
||||
- `src_object` - The atom, which UI corresponds to in the game world.
|
||||
- `ui_interact` - The proc where you will handle a request to open an
|
||||
interface. Typically, you would update an existing UI (if it exists),
|
||||
or set up a new instance of UI by calling the `SStgui` subsystem.
|
||||
- `ui_data` - In this proc you munges whatever complex data your `src_object`
|
||||
has into an associative list, which will then be sent to UI as a JSON string.
|
||||
- `ui_act` - This proc receives user actions and reacts to them by changing
|
||||
the state of the game.
|
||||
- `ui_state` (set in `ui_interact`) - This var dictates under what conditions
|
||||
a UI may be interacted with. This may be the standard checks that check if
|
||||
you are in range and conscious, or more.
|
||||
|
||||
Once backend is complete, you create an new interface component on the
|
||||
frontend, which will receive this JSON data and render it on screen.
|
||||
|
||||
States are easy to write and extend, and what make tgui interactions so
|
||||
powerful. Because states can be overridden from other procs, you can build
|
||||
powerful interactions for embedded objects or remote access.
|
||||
|
||||
## Using It
|
||||
|
||||
### Backend
|
||||
|
||||
Let's start with a very basic hello world.
|
||||
|
||||
```dm
|
||||
/obj/machinery/my_machine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "my_machine", name, 300, 300, master_ui, state)
|
||||
ui.open()
|
||||
```
|
||||
|
||||
This is the proc that defines our interface. There's a bit going on here, so
|
||||
let's break it down. First, we override the ui_interact proc on our object. This
|
||||
will be called by `interact` for you, which is in turn called by `attack_hand`
|
||||
(or `attack_self` for items). `ui_interact` is also called to update a UI (hence
|
||||
the `try_update_ui`), so we accept an existing UI to update. The `state` is a
|
||||
default argument so that a caller can overload it with named arguments
|
||||
(`ui_interact(state = overloaded_state)`) if needed.
|
||||
|
||||
Inside the `if(!ui)` block (which means we are creating a new UI), we choose our
|
||||
template, title, and size; we can also set various options like `style` (for
|
||||
themes), or autoupdate. These options will be elaborated on later (as will
|
||||
`ui_state`s).
|
||||
|
||||
After `ui_interact`, we need to define `ui_data`. This just returns a list of
|
||||
data for our object to use. Let's imagine our object has a few vars:
|
||||
|
||||
```dm
|
||||
/obj/machinery/my_machine/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["health"] = health
|
||||
data["color"] = color
|
||||
|
||||
return data
|
||||
```
|
||||
|
||||
The `ui_data` proc is what people often find the hardest about tgui, but its
|
||||
really quite simple! You just need to represent your object as numbers, strings,
|
||||
and lists, instead of atoms and datums.
|
||||
|
||||
Finally, the `ui_act` proc is called by the interface whenever the user used an
|
||||
input. The input's `action` and `params` are passed to the proc.
|
||||
|
||||
```dm
|
||||
/obj/machinery/my_machine/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
if(action == "change_color")
|
||||
var/new_color = params["color"]
|
||||
if(!(color in allowed_coors))
|
||||
return FALSE
|
||||
color = new_color
|
||||
. = TRUE
|
||||
update_icon()
|
||||
```
|
||||
|
||||
The `..()` (parent call) is very important here, as it is how we check that the
|
||||
user is allowed to use this interface (to avoid so-called href exploits). It is
|
||||
also very important to clamp and sanitize all input here. Always assume the user
|
||||
is attempting to exploit the game.
|
||||
|
||||
Also note the use of `. = TRUE` (or `FALSE`), which is used to notify the UI
|
||||
that this input caused an update. This is especially important for UIs that do
|
||||
not auto-update, as otherwise the user will never see their change.
|
||||
|
||||
### Frontend
|
||||
|
||||
Finally, let's make a React Component for your interface. This is also
|
||||
a source of confusion for new developers. If you got some basic javascript
|
||||
and HTML knowledge, that should ease the learning process, although we
|
||||
recommend getting yourself introduced to
|
||||
[React and JSX](https://reactjs.org/docs/introducing-jsx.html).
|
||||
|
||||
A React component is not a regular HTML template. A component is a
|
||||
javascript function, which accepts a `props` object (that contains
|
||||
properties passed to a component) and a `context` object (which is
|
||||
necessary to access UI data) as arguments, and outputs an HTML-like
|
||||
structure.
|
||||
|
||||
So let's create our first React Component. Create a file with a name
|
||||
`SampleInterface.js` (or any other name you want), and copy this code
|
||||
snippet (make sure component name matches the file name):
|
||||
|
||||
```jsx
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, LabeledList, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const SampleInterface = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
// Extract `health` and `color` variables from the `data` object.
|
||||
const {
|
||||
health,
|
||||
color,
|
||||
} = data;
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
<Section title="Health status">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Health">
|
||||
{health}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Color">
|
||||
{color}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Button">
|
||||
<Button
|
||||
content="Dispatch a 'test' action"
|
||||
onClick={() => act('test')}>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
Here are the key variables you get from a `useBackend(context)` function:
|
||||
|
||||
- `config` is part of core tgui. It contains meta-information about the
|
||||
interface and who uses it, BYOND refs to various objects, and so forth.
|
||||
You are rarely going to use it, but sometimes it can be used to your
|
||||
advantage when doing complex UIs.
|
||||
- `data` is the data returned from `ui_data` and `ui_static_data` procs in
|
||||
your DM code. Pretty straight forward.
|
||||
- Note, that javascript doesn't have associative arrays, so when you
|
||||
return an associative list from DM, it will be available in `data` as a
|
||||
javascript object instead of an array. You can use it normally
|
||||
like so: `object.key`, so it's not a problem if it's representing a
|
||||
data structure, but common `Array` methods, such as `array.map(item => ...)`,
|
||||
are not available on it. Always prefer returning clean arrays from your
|
||||
code, since arrays are easier to work with in javascript!
|
||||
- `act(name, params)` is a function, which you can call to dispatch an action
|
||||
to your DM code. It will be processed in `ui_act` proc. Action name will be
|
||||
available in `params["action"]`, mixed together with the rest of parameters
|
||||
you have passed in `params` object.
|
||||
|
||||
**Let's talk about the syntax.**
|
||||
|
||||
The syntax you're seeing here is called JSX - a very simple extension of the
|
||||
core javascript language. It's basically a pre-processor, that takes
|
||||
expressions that look like html, and turns them into function calls.
|
||||
|
||||
Take a look at this example:
|
||||
|
||||
```jsx
|
||||
<div className={'color-' + status}>
|
||||
You are in {status} condition!
|
||||
</div>
|
||||
```
|
||||
|
||||
After compiling the code above, this is what it becomes:
|
||||
|
||||
```js
|
||||
createElement('div',
|
||||
{ className: 'color-' + status },
|
||||
'You are in ', status, ' condition!');
|
||||
```
|
||||
|
||||
It is very important to remember, that JSX is just a javascript expression
|
||||
made out of `createElement` function calls. Naturally, this allows doing
|
||||
all sorts of stuff on these expressions, just like you would with anything
|
||||
else in javascript.
|
||||
|
||||
Take a look at these examples:
|
||||
|
||||
**Render an element inside of another element if `showProgress` is true.**
|
||||
|
||||
This example uses the `&&` operator (the logical AND). It returns
|
||||
the first operand if it evaluates to `false`, and returns the second operand
|
||||
if it evaluates to `true`.
|
||||
|
||||
If `showProgress` is `true`, the whole expression evaluates
|
||||
to a `<ProgressBar />` element. If `showProgress` is `false`, the whole
|
||||
expression evaluates to `false`, and `false` is not rendered by React.
|
||||
|
||||
```jsx
|
||||
<Box>
|
||||
{showProgress && (
|
||||
<ProgressBar value={progress} />
|
||||
)}
|
||||
</Box>
|
||||
```
|
||||
|
||||
You can also use the `||` operator (the logical OR), which works the same way,
|
||||
except it will return the second operand on `false` instead of `true`.
|
||||
|
||||
**Loop over the array to map every item to a corresponding React element.**
|
||||
|
||||
`Array.map()` is a method, that calls a function on every item in the array,
|
||||
and builds a new array based on what was returned by that function.
|
||||
|
||||
```jsx
|
||||
<LabeledList>
|
||||
{items.map(item => (
|
||||
<LabeledList.Item
|
||||
key={item.id}
|
||||
label={item.label}>
|
||||
{item.content}
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
```
|
||||
|
||||
If you need more examples of what you can do with React, see the
|
||||
[interface conversion guide](docs/converting-old-tgui-interfaces.md).
|
||||
|
||||
#### Splitting UIs into smaller, modular components
|
||||
|
||||
You interface will eventually get really, really big. The easiest thing
|
||||
you can do in this situation, is divide and conquer. Grab a chunk of your
|
||||
JSX code, and wrap it into a second, smaller React component:
|
||||
|
||||
```jsx
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, LabeledList, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const SampleInterface = (props, context) => {
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
<HealthStatus user="Jerry" />
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const HealthStatus = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
user,
|
||||
} = props;
|
||||
const {
|
||||
health,
|
||||
color,
|
||||
} = data;
|
||||
return (
|
||||
<Section title={"Health status of: " + user}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Health">
|
||||
{health}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Color">
|
||||
{color}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Copypasta
|
||||
|
||||
We all do it, even the best of us. If you just want to make a tgui **fast**,
|
||||
here's what you need (note that you'll probably be forced to clean your shit up
|
||||
upon code review):
|
||||
|
||||
```dm
|
||||
/obj/copypasta/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state) // Remember to use the appropriate state.
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "copypasta", name, 300, 300, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/obj/copypasta/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["var"] = var
|
||||
return data
|
||||
|
||||
/obj/copypasta/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
switch(action)
|
||||
if("copypasta")
|
||||
var/newvar = params["var"]
|
||||
// A demo of proper input sanitation.
|
||||
var = CLAMP(newvar, min_val, max_val)
|
||||
. = TRUE
|
||||
update_icon() // Not applicable to all objects.
|
||||
```
|
||||
|
||||
And the template:
|
||||
|
||||
```jsx
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, LabeledList, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const SampleInterface = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
// Extract `health` and `color` variables from the `data` object.
|
||||
const {
|
||||
health,
|
||||
color,
|
||||
} = data;
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
<Section title="Health status">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Health">
|
||||
{health}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Color">
|
||||
{color}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Button">
|
||||
<Button
|
||||
content="Dispatch a 'test' action"
|
||||
onClick={() => act('test')}>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
```
|
||||
Reference in New Issue
Block a user