Kills old TGUI, hardsyncs TGUI to 3.0

This commit is contained in:
Artur
2020-04-23 13:31:48 +03:00
parent 321902c35a
commit fe26034410
431 changed files with 2381 additions and 34240 deletions
+62 -63
View File
@@ -54,32 +54,25 @@ Make sure to add new items to this list if you document new components.
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
components, such as [Box](#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:
**Event handlers.**
Event handlers are callbacks that you can attack to various element to
listen for browser events. Inferno supports camelcase (`onClick`) and
lowercase (`onclick`) event names.
- 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.
- 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 the lowercase `onclick` event.
Use the camel case `onClick` instead.
## `tgui/components`
@@ -87,7 +80,7 @@ event names.
This component provides animations for numeric values.
Props:
**Props:**
- `value: number` - Value to animate.
- `initial: number` - Initial value to use in animation when element
@@ -106,7 +99,7 @@ Just a block quote, just like this example in markdown:
> Here's an example of a block quote.
Props:
**Props:**
- See inherited props: [Box](#box)
@@ -138,15 +131,21 @@ This way, `Button` can pull out the `className` generated by the `Box`.
</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`).
**Box Units**
`Box` units, like width, height and margins can be defined in two ways:
- By plain numbers
- 1 unit equals `1rem` for width, height and positioning properties.
- 1 unit equals `0.5rem` for margins and paddings.
- By strings with proper CSS units
- For example: `100px`, `2em`, `1rem`, `100%`, etc.
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:
Default font size (`1rem`) is equal to `12px`.
**Props:**
- `as: string` - The component used for the root node.
- `color: string` - Applies an atomic `color-<name>` class to the element.
@@ -204,7 +203,7 @@ all available horizontal space.
Buttons allow users to take actions, and make choices, with a single click.
Props:
**Props:**
- See inherited props: [Box](#box)
- `fluid: boolean` - Fill all available horizontal space.
@@ -233,7 +232,7 @@ over the button.
A ghetto checkbox, made entirely using existing Button API.
Props:
**Props:**
- See inherited props: [Button](#button)
- `checked: boolean` - Boolean value, which marks the checkbox as checked.
@@ -242,7 +241,7 @@ Props:
A button with a an extra confirmation step, using native button component.
Props:
**Props:**
- See inherited props: [Button](#button)
- `confirmMessage: string` - Text to display after first click; defaults to "Confirm?"
@@ -254,7 +253,7 @@ 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:
**Props:**
- See inherited props: [Box](#box)
- `fluid`: fill availible horizontal space
@@ -297,7 +296,7 @@ Example (map):
It supports a full set of `Box` properties for layout purposes.
Props:
**Props:**
- See inherited props: [Box](#box)
- `params: any` - An object with parameters, which are directly passed to
@@ -309,7 +308,7 @@ in [BYOND controls and parameters guide](https://secure.byond.com/docs/ref/skinp
Displays contents when open, acts as a fluid button when closed. Click to
toggle, closed by default.
Props:
**Props:**
- See inherited props: [Box](#box)
- `children: any` - What is collapsed when closed
@@ -325,7 +324,7 @@ or for visually representing a color.
If you want to set a background color on an element, use a plain
[Box](#box) instead.
Props:
**Props:**
- See inherited props: [Box](#box)
- `color: string` - Color of the box.
@@ -336,7 +335,7 @@ Dims surrounding area to emphasize content placed inside.
Content is automatically centered inside the dimmer.
Props:
**Props:**
- See inherited props: [Box](#box)
@@ -345,7 +344,7 @@ Props:
Draws a horizontal or vertical line, dividing a section into groups.
Works like the good old `<hr>` element, but it's fancier.
Props:
**Props:**
- `vertical: boolean` - Divide content vertically.
- `hidden: boolean` - Divider can divide content without creating a dividing
@@ -356,7 +355,7 @@ line.
A simple dropdown box component. Lets the user select from a list of options
and displays selected entry.
Props:
**Props:**
- See inherited props: [Box](#box)
- `options: string[]` - An array of strings which will be displayed in the
@@ -397,7 +396,7 @@ to the left, and certain elements to the right:
Flex item with `grow` property serves as a "filler", to separate the other
two flex items as far as possible from each other.
Props:
**Props:**
- See inherited props: [Box](#box)
- `spacing: number` - Spacing between flex items, in integer units
@@ -444,7 +443,7 @@ when they overflow the line.
### `Flex.Item`
Props:
**Props:**
- See inherited props: [Box](#box)
- `order: number` - By default, flex items are laid out in the source order.
@@ -493,13 +492,13 @@ Example:
</Grid>
```
Props:
**Props:**
- See inherited props: [Table](#table)
### `Grid.Column`
Props:
**Props:**
- See inherited props: [Table.Cell](#tablecell)
- `size: number` (default: 1) - Size of the column relative to other columns.
@@ -517,7 +516,7 @@ 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:
**Props:**
- See inherited props: [Box](#box)
- `name: string` - Icon name.
@@ -534,7 +533,7 @@ 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:
**Props:**
- See inherited props: [Box](#box)
- `value: string` - Value of an input.
@@ -554,7 +553,7 @@ up and down.
Single click opens an input box to manually type in a number.
Props:
**Props:**
- See inherited props: [Box](#box)
- `animated: boolean` - Animates the value if it was changed externally.
@@ -614,13 +613,13 @@ to perform some sort of action), there is a way to do that:
</LabeledList>
```
Props:
**Props:**
- `children: LabeledList.Item` - Items to render.
### `LabeledList.Item`
Props:
**Props:**
- `label: string` - Item label.
- `color: string` - Sets the color of the text.
@@ -642,7 +641,7 @@ Example:
</LabeledList>
```
Props:
**Props:**
- `size: number` - Size of the divider.
@@ -653,7 +652,7 @@ 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:
**Props:**
- See inherited props: [Box](#box)
@@ -661,7 +660,7 @@ Props:
A notice box, which warns you about something very important.
Props:
**Props:**
- See inherited props: [Box](#box)
- `info: boolean` - Info box
@@ -674,7 +673,7 @@ Props:
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:
**Props:**
- `animated: boolean` - Animates the value if it was changed externally.
- `fluid: boolean` - Fill all available horizontal space.
@@ -720,7 +719,7 @@ Usage of `ranges` prop:
value={0.6} />
```
Props:
**Props:**
- `value: number` - Current progress as a floating point number between
`minValue` (default: 0) and `maxValue` (default: 1). Determines the
@@ -775,7 +774,7 @@ in precise values by dragging it left and right.
Single click opens an input box to manually type in a number.
Props:
**Props:**
- See inherited props: [Box](#box)
- `animated: boolean` - Animates the value if it was changed externally.
@@ -824,7 +823,7 @@ Example:
</Table>
```
Props:
**Props:**
- See inherited props: [Box](#box)
- `collapsing: boolean` - Collapses table to the smallest possible size.
@@ -833,7 +832,7 @@ Props:
A straight forward mapping to `<tr>` element.
Props:
**Props:**
- See inherited props: [Box](#box)
@@ -841,7 +840,7 @@ Props:
A straight forward mapping to `<td>` element.
Props:
**Props:**
- See inherited props: [Box](#box)
- `collapsing: boolean` - Collapses table cell to the smallest possible size,
@@ -891,7 +890,7 @@ Tabs also support a vertical configuration. This is usually paired with a
</Flex>
```
Props:
**Props:**
- See inherited props: [Box](#box)
- `vertical: boolean` - Use a vertical configuration, where tabs will be
@@ -903,7 +902,7 @@ stacked vertically.
An individual tab element. Tabs function like buttons, so they inherit
a lot of `Button` props.
Props:
**Props:**
- See inherited props: [Button](#button)
- `altSelection` - Whether the tab buttons select via standard select (color
@@ -932,7 +931,7 @@ Usage:
</Box>
```
Props:
**Props:**
- `position: string` - Tooltip position.
- `content/children: string` - Content of the tooltip. Must be a plain string.
@@ -958,7 +957,7 @@ Example:
</Window>
```
Props:
**Props:**
- `className: string` - Applies a CSS class to the element.
- `theme: string` - A name of the theme.
@@ -974,7 +973,7 @@ putting your content into [Window.Content](#windowcontent).
Canonical window content, which is usually the main target of window focus.
Can be scrollable.
Props:
**Props:**
- `className: string` - Applies a CSS class to the element.
- `scrollable: boolean` - Shows or hides the scrollbar.
+332
View File
@@ -0,0 +1,332 @@
# Converting old tgui interfaces to tgui-next
This guide is going to assume you already know roughly how tgui-next works, how to make new uis, etc. It's mostly aimed at helping translate concepts between tgui and tgui-next, and clarify some confusing parts of the transition.
## Backend
Backend in almost every case does not require any changes. In particularly heavy ui cases, something to be aware of is the new `ui_static_data()` proc. This proc allows you to split some data sent to the interface off into data that will only be sent on ui initialize and when manually updated by elsewhere in the code. Useful for things like cargo where you have a very large set of mostly identical code.
Keep in mind that for uis where *all* data doesn't need to be live updating, you can just toggle off autoupdate for the ui instead of messing with static data.
## Frontend
The very first thing to note is the name of the `ract` file containing the old interface. Whatever the name is (minus the extension) is going to be what the route key is going to be.
One thing I like to do before starting work on a conversion is screenshot what the old interface looks like so I have something to reference to make sure that the styling can line up as well.
## General syntax changes
Ractive has a fairly different templating syntax from React.
### `data`
You likely already know that React data inserts look like this
```jsx
{data.example_data}
```
Ractive looks very similar, the only real difference is that React uses one paranthesis instead of two.
```ractive
{{data.example_data}}
```
However, you may occasionally come across data inserts that instead of referencing the `data` var or things contained within it instead reference `adata`. `adata` was short for animated data, and was used for smooth number animations in interfaces. instead of having a seperate data structure for this. tgui-next instead uses a component, which is `AnimatedNumber`.
`AnimatedNumber` is used like this
```jsx
<AnimatedNumber value={data.example_data}/>
```
Make sure you don't forget to import it.
### Conditionals
Ractive conditionals look very different from React conditionals.
A ractive `if` (only render if result of expression is true) looks like this
```ractive
{{#if data.condition}}
<span>Example Render</span>
{{/if}}
```
The equivalent React would be
```jsx
{!!data.condition && (
<Fragment>Example Render</Fragment>
)}
```
This might look a bit intimidating compared to the reactive part but it's not as complicated as it seems:
1. A new jsx context is opened with `{}`
2. jsx contexts like this always render whatever the return value is, so we can use `&&` to return a value we want. `&&` returns the last true value (or not "falsey" because this is js).
3. jsx tags are never "falsey", so a conditioned paired with a jsx tag will mean the condition being true will continue on and return the tag. `()` is just used to contain the tag
4. The `!!` is not a special operator, it is a literal double negation. This is because most `false` values coming from byond are going to actually be `0`, which would be rendered if the condition is false. Negating `0` returns `true`, negating `true` returns `false`, which isn't rendered.
5. `Fragment` is actually a true "dead tag". It's similar to `span` in that it just contains things without providing functionality, but it's unwrapped before the final render and children of it are injected into its parent. In a case where you only need to render text without any styling, it's probably better to just return a string literal (`"Example Render"`), but this was just to illustrate that you can put any tag in this expression.
You don't really need to know all this to understand how to use it, but I find it helps with understanding when things go wrong.
Ractive conditionals can have an `else` as well
```ractive
{{#if data.condition}}
value
{{else}}
other value
{{/if}}
```
Similarly to the previous example, just add a `||` operator to handle the
"falsy" condition:
```jsx
{!!data.condition && (
<Fragment>
value
</Fragment>
) || (
<Fragment>
other value
</Fragment>
)}
```
There's also our good old friend - the ternary:
```jsx
{data.condition ? 'value' : 'other value'}
```
Keep in mind you can also use tags here like the conditional example,
and you can mix string literals, values, and tags as well.
```jsx
{data.is_robot ? (
<Button content="Robot Button"/>
) : 'Not a robot'}
```
### Loops
Ractive has loops for iterating over data and inserting something for each
member of an array or object
```
{{#each data.list_of_foo}}
foo {{number}} is here.
{{/each}}
```
This didn't care whether the data was an array or an object, and members of each entry of the loop were "unwrapped" so to say. `{{number}}` in that example is referring to the `{{number}}` value on the entry of the list for that iterate.
The React equivalent to this is going to be `map`.
_AN IMPORTANT DISTINCTION HERE IS THAT NOW WE CARE WHETHER THIS IS AN OBJECT OR AN ARRAY BEING ACTED ON._
Objects are represented by `{}`, arrays by `[]`
"How can I tell?" you may ask. It's fairly simple, associated lists on the byond side are going to be turned into objects when they get json converted, normal lists are going to be turned into arrays.
`list("bla", "blo")` would become `["bla", "blo"]` and `list("foo" = 1, "bar" = 2)` would become `{"foo": 1, "bar": 2}`
First things first, above the `return` of the function you're making the interface in, you're going to want to add something like this
```jsx
const things = data.things || [];
```
This ensures that you'll never be reading a null entry by mistake. Substitute `{}` for objects as appropriate.
If it's an array, you'll want to do this in the template
```jsx
{things.map(thing => (
<Fragment>
Thing {thing.number} is here!
</Fragment>
))}
```
`map` is a function that calls a passed function (a lambda) on each entry, and returns the value. You should already know that returned tags and values (except `false`) get rendered, so that's how it's rendering each time.
A lambda is what's known as an anonymous function, it's a function that doesn't have a name that's only used for a specific usage. `map` wants a function that has one parameter, so we define one parameter then use `=>` to say the parameter has to do with the following block.
`parameter => ()` is just a shorthand for `parameter => {return();}`
This is quite a bit higher concept than ractive's each statements, so feel free to look around and ~~copy paste~~ learn from how other interfaces use this.
Now for objects, there's a genuinely pretty gross syntax here. We apoligize, it's related to ie8 compatibility nonsense.
```jsx
{map((value, key) => (
<Fragment>
Key is {key}, value is {value}
</Fragment>
))(fooObject)}
```
Again, sorry for this syntax. `fooObject` would be the object being iterated on, value would be the value of the iterated entry on the list, and key would be the key. the naming of value and key isn't important here, but knowing that it goes `value`, `key` in that order is important.
It is sometimes better to preemptively convert an object to array before
the big return statement, like this:
```jsx
const fooArray = map((value, key) => {
return { key, value };
})(fooObject);
```
Or if you just want to discard all keys, this will also work nicely:
```jsx
const fooArray = toArray(fooObject);
```
Also occasionally you'd see an else:
```
{{#each data.potentially_empty_list}}
Thing "{{name}}" is in this list!
{{else}}
None found!
{{/each}}
```
This would iterate using the first contents each time, or display the second option if the list was empty.
To do a similar thing in JSX, just check if array is empty like this:
```jsx
{fooArray.length === 0 && 'fooArray is empty.'}
{fooArray.map(foo => (
<Fragment>
Foo is {foo}
</Fragment>
))}
```
### Extra Stuff
I'll put some extra stuff here when I think of it.
## Components
This will be a reference of tgui components and the tgui-next equivalent.
### `ui-display`
Equivalent of `<ui-display>` is `<Section>`
```
<ui-display title="Status">
Contents
</ui-display>
```
becomes
```jsx
<Section title="Status">
Contents
</Section>
```
A feature sometimes used is if `ui-display` has the `button` property, it will contain a `partial` command. This becomes the `buttons` property on `Section`:
```
<ui-display title="Status" button>
{{#partial button}}
<ui-button /> // lots more button bullshit here
{{/partial}}
Contents
</ui-display>
```
becomes
```jsx
<Section
title="Status"
buttons={(
<Button />
)}>
Contents
</Section>
```
### `ui-section`
Very important to note `ui-section` is NOT the equivalent of `Section`
`<ui-section>` does not have a direct equivalent, but the closest equivalent is `<LabeledList>`
```
<ui-section label="power">
No Power
</ui-section>
<ui-section label="connection">
No Connection
</ui-section>
```
becomes
```jsx
<LabeledList>
<LabeledList.Item label="power">
No Power
</LabeledList.Item>
<LabeledList.Item label="connection">
No Connection
</LabeledList.Item>
</LabeledList>
```
Important to note that `LabeledList.Item` has `buttons` as well.
Also good to know that if you need the contents of a `LabeledList.Item` to be colored, you can just set the `color` prop on it instead of putting a `span` inside it.
### `ui-notice`
`<ui-notice>` has a direct equivalent in `<NoticeBox>`
```
<ui-notice>
Notice stuff!
</ui-notice>
```
becomes
```jsx
<NoticeBox>
Notice stuff!
</NoticeBox>
```
### `ui-button`
The equivalent of `ui-button` is `Button` but it works quite a bit differently.
```
<ui-button
state='{{data.condition ? "disabled" : null}}'
action="ui_action"
params={param: value}>
Click
</ui-button>
```
becomes
```jsx
<Button
content="Click"
disabled={data.condition}
onClick={() => act('ui_action', {
param: value,
})}/>
```