Initial commit

Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com>
Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com>
Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com>
Co-Authored-By: Michael Cartner <32543275+michaelcartner@users.noreply.github.com>
This commit is contained in:
Eric Tuvesson
2023-09-05 12:08:55 +02:00
commit 53f0d6320e
2704 changed files with 76354 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
---
title: Building User Interfaces Basics
hide_title: true
---
import useBaseUrl from '@docusaurus/useBaseUrl'
# Building User Interfaces Basics
## What you will learn in this guide
In this guide you will learn how to place and group components in the visual canvas.
## Adding UI elements
To add UI controls and other UI elements, such as Texts, to the currently selected visual component you click the **[+]** icon at the top of the visual canvas. This brings up the **Node Picker**, here you can find yourself to the **UI Elements** section. Click the element you want to add and it is added to the currently selected component.
<div className="ndl-image-with-background xl">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/basics/add-ui-control.mp4")}/>
</div>
## Editing properties
To edit the properties of a UI element, you click the element in the visual canvas (or more commonly in the node graph editor) which will bring up the properties panel. Here you can edit all properties of the UI element.
<div className="ndl-image-with-background xl">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/basics/edit-properties.mp4")}/>
</div>
## The visual heirarchy
As you add UI elements to your component you will see the visual heirarchy being built in the node graph editor. All UI elements are depicted as blue nodes. Here are some nifty things to know about the visual hierarchy in the node editor.
- All components must have a **single root** UI Element, most ofthen this is a **Group** node.
- You can hover the UI elements in the node graph editor to reveal them in the visual canvas.
- You can manipulate the heirarchy much like you would a layer panel in other design tools.
- You can bring up the **Node picker** by **right clicking** in the node graph editor. If you want the newly created UI element to be placed as a child to another UI element simply right click the parent element to bring up the **Node Picker**.
<div className="ndl-image-with-background xl">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/basics/ui-heirarchy.mp4")}/>
</div>
## Grouping and Layouts
Now that you know how to place new components it's time to learn another important concept, grouping. This is how you group UI elements together and control the layout of the elements under a group. Use the **Node Picker** to create a new group, place it where you want it in the heirarchy, select, drag and drop the UI elements that you want under the group.
The **Group** node gives you a bunch of options for layouting user interfaces, learn more about the details of layouting and the group node in this [guide](/docs/guides/user-interfaces/layout)
<div className="ndl-image-with-background xl">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/basics/grouping.mp4")}/>
</div>

View File

@@ -0,0 +1,93 @@
---
title: Components
hide_title: true
---
import useBaseUrl from '@docusaurus/useBaseUrl'
# Components
## What you will learn in this guide
In this guide you will learn how to create visual components to make re-usable UI components. This is essential to create dynamic user interfaces connected to data that we will learn later.
## Creating visual components
Visual components are parts of a user interface that can be reused. You create new visual components using the **Component Panel**.
<div className="ndl-image-with-background xl">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/components/create-component.mp4")}/>
</div>
Next you give it a name and it will show up in the components list, as well as in the visual canvas. Newly created visual components only contain a single root **Group** node. Once you have your component created you can start working on it like you have already learnt, or you can cut and paste UI elements from another component that you want to make re-usable.
<div className="ndl-image-with-background xl">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/components/cut-n-paste.mp4")}/>
</div>
When you have your component all done, you can re-use it anywhere in your application user interface by simply dragging it from the component panel.
<div className="ndl-image-with-background xl">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/components/drag-component.mp4")}/>
</div>
If you prefer, you can also find your components in the node picker.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/components/create-from-node-picker.png)
</div>
## Component Inputs & Outputs
That's a pretty neat trick. Now we can create our own re-usable UI components. But it would be even more powerful if we could turn them into templates and change some things up for each new instance of the UI component we create. This is where **Component Inputs** come into play. This is a concept where you can expose certain inputs of some of your nodes in the component as inputs to the component instance. This is done with the **Component Inputs** node.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/components/component-inputs.png)
</div>
If you go ahead and edit the properties of this node you will see that you can add **ports** to it. Each port will become an input (and property) of your component instances when you use the component in your application. In this example, we create two **ports** one called **Label** and another called **Button Color**.
<div className="ndl-image-with-background m">
![](/docs/guides/user-interfaces/components/create-input.png)
</div>
After that is done, we can go ahead and make connections from the **Component Inputs** node to the inputs that we want each **Port** to connect to.
- So the **Label** port we want to connect to the **Text** input of the **Text** node.
- And the **Button Color** port we want to connect to the **Background Color** of the **Button** node.
<div className="ndl-image-with-background xl">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/components/connect-inputs.mp4")}/>
</div>
Now with your new component inputs in place, you will see that the ports show up as properties on your component instance and you can go ahead and customize them individually.
<div className="ndl-image-with-background xl">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/components/edit-inputs.mp4")}/>
</div>
This works in the same way for component outputs, you simply use the **Component Outputs** node instead. This is very useful when you are creating UI component that accept some sort of user intput via UI controls.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/components/component-outputs.png)
</div>
Component inputs and outputs, and connecting to these is not just important for making re-usable UI components. It's also a core concept in connecting data to your user interfaces and creating dynamic data driver applications. We will take a closer look at that in the [Working with data](/docs/guides/data/overview) section.
## Sheets
When your application grows you will be creating more and more components and after a while the visual canvas can become crowded. When this happends it's a good idea to orginise your application into sheets. Each sheet is a new empty visual canvas for you to place more components into. To create a new sheet
<div className="ndl-image-with-background m">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/components/create-sheet.mp4")}/>
</div>
You can move components between sheets by simply dragging from the components panel and dropping onto the sheet you want to move it to.

View File

@@ -0,0 +1,83 @@
---
title: Figma Plugin
hide_title: true
---
# Figma plugin
:::info
Download the plugin here: https://www.figma.com/community/plugin/1006908263044642341/Noodl
:::
If you're a Figma user you can use Figma documents to create nodes in Noodl.
Layers in Figma will be mapped to nodes in Noodl, and complex shapes will be exported as images.
The Noodl plugin can export:
- Shapes like circles, rectangles and lines with one fill or stroke. These will be mapped to the corresponding Noodl nodes.
- Complex shapes, like vectors paths and boolean operation, will be exported as images
- Text styles
- Positions and sizes will map to absolute layouts and fixed dimensions
- Entire layer hierarchies, with the correct order and parent/child relations.
The Noodl plugin can't currently export:
- Auto layout
- Constraints
- Prototype interactions
This guide uses the Mobile UI kit by Toni Gemayel — a design resource from the Figma community:
https://www.figma.com/community/file/836596421863073964/Mobile-UI-kit
## Export a layer
1. Select a layer
2. Open the Noodl plugin and click "Export". Make sure Noodl is running and have a project open.
3. Noodl will now become focused. Place the Noodl node in your node hierarchy. Assets, like images, will be automatically placed in your project directory.
<iframe width="560" height="315" style={{margin:'0 auto',display:'block'}} src="https://www.youtube-nocookie.com/embed/GsczhwfoyEE" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen></iframe><br/>
Note that the Ellipse in Figma was mapped to an `Image` node in Noodl, with the correct size, image asset and border.
:::info
Here's the same node duplicated in Noodl, with a different source and border radius
:::
![](/docs/guides/user-interfaces/figma-plugin/image2.png ':class=img-size-l')
## Export text and text styles
All text styles used by exported text layers will automatically be imported.
?> Figma uses the fonts you have installed on your system. In Noodl apps the required font files have to be defined in your projects. You can [import fonts via Google Fonts](https://www.youtube.com/watch?v=lgMZZC6XoAs) or [via font files](https://www.youtube.com/watch?v=P76v0Q38eKI)
<iframe width="560" height="315" style={{margin:'0 auto',display:'block'}} src="https://www.youtube-nocookie.com/embed/sZm0eBZvLaM" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen></iframe><br/>
?> Note that the text styles was added to the styles tab in Noodl ![](/docs/guides/user-interfaces/figma-plugin/text-styles.png ':class=img-size-l')
## Export complex shapes
Complex shapes that don't map to a visual node in Noodl, like this vector path, will be exported as images. You can use the "Default image size" option to specify what resolutions the images should be exported at.
<iframe width="560" height="315" style={{margin:'0 auto',display:'block'}} src="https://www.youtube-nocookie.com/embed/mqML1OL0SUk" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen></iframe><br/>
## Export larger layer structures
To get started quickly you can export entire structures, like a whole page. As you progress building the application and you can extract components and replace the static layouts with dynamic layouts and make the design data-driven.
<iframe width="560" height="315" style={{margin:'0 auto',display:'block'}} src="https://www.youtube-nocookie.com/embed/5miB0PD4z9k" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen></iframe><br/>
:::info
Another page exported all at once
:::
![](/docs/guides/user-interfaces/figma-plugin/whole-page.png ':class=img-size-l')
## Notes about workflow
Most application are more dynamic than what's typically designed in Figma. Text inputs are interactive, labels and values are data-driven, the amount of items in a list isn't fixed, groups can have padding, and so on. This means that there's some work left to do on the nodes that are exported.
- **Applications**: Export small parts, like a list item, a button, and build them up one by one in Noodl. Entire structures are harder to export as they often need to be restructured.
- **Prototypes**: Export entire pages, one by one, and build up the navigation structure in Noodl. Then start replacing the parts that require dynamic and interactive elements, and adjust the layout where it needs to be more flexible.

View File

@@ -0,0 +1,555 @@
---
title: Grouping & Layout
hide_title: true
---
import useBaseUrl from '@docusaurus/useBaseUrl'
import CopyToClipboardButton from '../../../src/components/copytoclipboardbutton';
# Grouping & Layout
## What you will learn in this guide
This guide will cover how to lay out visual elements. It's an important concept for creating responsive, dynamic interfaces for your apps.
The central node for defining a layout in Noodl is the [Group](/nodes/basic-elements/group/) node. This guide will walk through the most important Group properties for defining a layout.
### Overview
We will cover the following topics in this guide:
- The Group Node
- Layout Direction and position
- Size and Dimensions
- Alignment
- Margin and Padding
- Multi Line Layout
## The Group Node
The **Group** node is the most central node for doing a layout. It's the fundamental way of building visual hierarchies and structures in Noodl.
**Group** nodes are arranged in a hierarchy and can have other nodes as children. It controls the layout of its children and there are a number of properties that can be used to specify how the children of a **Group** node will be laid out. You can copy the various node examples used in this guide by clicking "Copy nodes" and then pasting into a Noodl project.
<div className="ndl-image-with-background xl">
<img src="/2.8/docs/guides/user-interfaces/layout/groups.png" />
<CopyToClipboardButton
json={{
nodes: [
{
id: '5a81ad6d-b8eb-16f8-e2b3-3533c0a05462',
type: 'Group',
label: 'Group',
x: 176,
y: 140.5,
parameters: { backgroundColor: '#FFFFFF' },
ports: [],
children: [
{
id: '999f7f8b-6678-ed24-5ea9-08b25e1b394e',
type: 'Group',
x: 196,
y: 186.5,
parameters: {
marginTop: { value: 20, unit: 'px' },
marginLeft: { value: 20, unit: 'px' },
marginRight: { value: 20, unit: 'px' },
marginBottom: { value: 20, unit: 'px' },
paddingTop: { value: 50, unit: 'px' },
paddingLeft: { value: 50, unit: 'px' },
paddingRight: { value: 50, unit: 'px' },
paddingBottom: { value: 50, unit: 'px' },
backgroundColor: '#DBDBDB',
},
ports: [],
children: [
{
id: 'ddde9610-f9a6-03bd-bb46-4a67d56a2180',
type: 'Group',
x: 216,
y: 232.5,
parameters: { backgroundColor: '#B5B5B5' },
ports: [],
children: [],
},
],
},
],
},
],
connections: [],
}}
/>
</div>
:::tip
Hovering on a node in Noodl with the mouse cursor will highlight the corresponding visual element in the preview window. This is a great way to debug your layout. For example you hover over a node and you cannot see it in the preview window, it's likely that it has a zero size or lies outside the screen.
:::
<div className="ndl-image-with-background xl">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/layout/hover.mp4")}/>
</div>
## Layout direction
By default the **Group** node will stack its children vertically. You can change the layout direction by editing the **Layout** property:
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/layout/layout-prop.png)
</div>
- `Vertical` Children are stacked vertically.
- `Horizontal` Children are stacked horizontally.
- `None` Children are _not_ stacked. You will have to position them yourselves, for example by using margins, x/y positions or various alginments (see below).
Here's an example you can copy into Noodl. Click the "Copy nodes" button next to the image and press Ctrl+V (Windows) or Cmd+V (macOS) to paste them into Noodl. Make sure you have a project open.
Change the **Layout** of the top level node to see how the direction of the child nodes change.
<div className="ndl-image-with-background xl">
<img src="/2.8/docs/guides/user-interfaces/layout/layout-dir.png"></img>
<CopyToClipboardButton
json={{
nodes: [
{
id: '9f88e7d6-959c-7946-f66d-3db6257d522b',
type: 'Group',
label: 'Layout node',
x: 290,
y: 395,
parameters: {
sizeMode: 'explicit',
backgroundColor: '#E6E6E6',
},
ports: [],
children: [
{
id: '8a0b2c8c-c534-bdcc-102d-2905b48d8885',
type: 'Group',
x: 310,
y: 456,
parameters: {
marginTop: { value: 5, unit: 'px' },
marginLeft: { value: 5, unit: 'px' },
marginRight: { value: 5, unit: 'px' },
marginBottom: { value: 5, unit: 'px' },
backgroundColor: '#C2C2C2',
width: { value: 100, unit: 'px' },
height: { value: 100, unit: 'px' },
},
ports: [],
children: [],
},
{
id: 'bcfa83ad-f05f-d7ff-13a4-f82ac19bd664',
type: 'Group',
x: 310,
y: 502,
parameters: {
marginTop: { value: 5, unit: 'px' },
marginLeft: { value: 5, unit: 'px' },
marginRight: { value: 5, unit: 'px' },
marginBottom: { value: 5, unit: 'px' },
backgroundColor: '#C2C2C2',
width: { value: 100, unit: 'px' },
height: { value: 100, unit: 'px' },
},
ports: [],
children: [],
},
{
id: '35c2039f-5f12-3dab-bc94-a7c630aa9698',
type: 'Group',
x: 310,
y: 548,
parameters: {
marginTop: { value: 5, unit: 'px' },
marginLeft: { value: 5, unit: 'px' },
marginRight: { value: 5, unit: 'px' },
marginBottom: { value: 5, unit: 'px' },
backgroundColor: '#C2C2C2',
width: { value: 100, unit: 'px' },
height: { value: 100, unit: 'px' },
},
ports: [],
children: [],
},
],
},
],
connections: [],
}}
/>
</div>
## Layout Position
As a child of a group you can also control how you will be layouted by setting the **Position** property:
- `In Layout` - This node is part of the parent node's layout. It will be stacked with its siblings depending on the parent node's layout settings as explained above.
- `Absolute` - Removes a node from the flow of a layout. Use margins and alignment to set the position.
- `Sticky` - Behaves like `In Layout`, except when the node is about the be scrolled outside the parent. It'll stick to an edge of the parent instead of scrolling away. Which edge can be controlled with the _Alignment_ input.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/layout/layout-absolute.png)
</div>
## Dimensions
The dimensions section control how the size of a **Group** will be calculated.
<div className="ndl-image-with-background">
![](/nodes/ui-elements/dimensions.png)
</div>
A **Group** can also get its dimensions from the size of its children. You use the icons at the top to change between the four modes (from right):
- **Explicit width & height** Specify both width and height explicitly in pixels (`px`), percentage of parent (`%`), or as a percentage of the viewport size (`vw` and `vh`).
- **Explicit width & content height** Specify the width explicitly but the height will be the total of the children heights and margins.
- **Explicit height & content width** Specify the height explicitly but the width will be the total of the children widths and margins.
- **Content width & height** The size of this group will be the total width and height of the children based on the layout.
You can copy the nodes below to an empty component in Noodl. The result is shown to the right. These nodes each use a different setting for the **Dimensions** icons.
<div className="ndl-image-with-background">
<img
src="/2.8/docs/guides/user-interfaces/layout/content-size-nodes.png"
className="ndl-image small"
></img>
<img
src="/2.8/docs/guides/user-interfaces/layout/content-size-example.png"
className="ndl-image small"
></img>
<CopyToClipboardButton
json={{
nodes: [
{
id: '3aad82a0-9636-d391-b31c-06f66be77bdc',
type: 'Group',
x: 243,
y: 178,
parameters: { backgroundColor: '#FFFFFF' },
ports: [],
children: [
{
id: '61757927-9c6c-3afb-a964-1492a5ff5a90',
type: 'Group',
label: 'Content height',
x: 263,
y: 224,
parameters: {
sizeMode: 'contentHeight',
backgroundColor: '#D6D6D6',
marginBottom: { value: 20, unit: 'px' },
width: { value: 100, unit: 'px' },
},
ports: [],
children: [
{
id: 'cefc770e-47c0-ddfd-0caa-b88831ce8e8f',
type: 'Circle',
x: 283,
y: 285,
parameters: {
fillColor: '#858585',
size: 50,
},
ports: [],
children: [],
},
],
},
{
id: 'ccdbfc6b-8138-cc99-6ffc-aaf6b5c4b56e',
type: 'Group',
label: 'Content size',
x: 263,
y: 331,
parameters: {
sizeMode: 'contentSize',
backgroundColor: '#D6D6D6',
marginBottom: { value: 20, unit: 'px' },
},
ports: [],
children: [
{
id: 'cef82662-86c6-43d7-5221-fa47869a5043',
type: 'Circle',
x: 283,
y: 392,
parameters: {
fillColor: '#858585',
size: 50,
},
ports: [],
children: [],
},
],
},
{
id: '000843d3-fdf9-7638-67e3-42617b66bf4f',
type: 'Group',
label: 'Content width',
x: 263,
y: 438,
parameters: {
sizeMode: 'contentWidth',
backgroundColor: '#D6D6D6',
height: { value: 100, unit: 'px' },
},
ports: [],
children: [
{
id: 'f94a589b-b042-9f9e-1d2f-0599a5fe2f41',
type: 'Circle',
x: 283,
y: 499,
parameters: {
fillColor: '#858585',
size: 50,
},
ports: [],
children: [],
},
],
},
],
},
],
connections: [],
}}
/>
</div>
### Fixed dimension
Sizes specified in `%` enables an additional set of dynamic size rules.
- The group will expand to fill the parent. Space is shared with siblings and the `%` value controls how much of the remaining space each node will get.
- If the content of a Group is larger than the Group itself, it'll expand to contain the children.
This dynamic sizing can be disabled by enabling _Fixed_.
Here's an example with two Groups, both set to 100% height. With _Fixed_ disabled they will both try to grow to the same size as the parent. As the rules above implied, the Groups final size will be 50% to make sure that both Groups have equal space and are shown on the screen.
Enabling _Fixed_ will force a Group to be exactly the size that's specified and disable dynamic sizing. With _Fixed_ enabled the first Group will cover the entire screen, and there will be no space left for the second Group that'll be pushed outside the screen.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/layout/fixed-height.gif)
</div>
### Clip content
A Group with dimensions set in `%` will expand to make sure it at least the same size as all of its content.
This can be changed be either enabling `Fixed` or as in this example, enable `Clip content`.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/layout/clip.gif)
</div>
## Alignment
You can use the alignment controls to pin the children to a specific edge, or how the remaining space should be distributed among the children. The padding of a group determines where that edge is.
A Group can control the alignment of its children using the _Align and justify content_ property.
<div className="ndl-image-with-background l">
<img src="/2.8/docs/guides/user-interfaces/layout/alignment.gif"></img>
<CopyToClipboardButton
json={{
nodes: [
{
id: '6bede195-ea0c-d5ca-a315-500079267560',
type: 'Group',
x: 198.0,
y: 196,
parameters: { backgroundColor: '#D6D6D6' },
children: [
{
id: 'b5beb893-c7ae-9467-da03-0032b8221ab6',
type: 'Circle',
x: 218.0,
y: 242,
parameters: { fillColor: '#858585' },
},
{
id: '0404c68a-525c-f975-60d3-8d0f7143abb7',
type: 'Circle',
x: 218.0,
y: 288,
parameters: { fillColor: '#858585' },
},
{
id: 'b2b85213-f75e-7d0b-050e-dbee3c312fd7',
type: 'Circle',
x: 218.0,
y: 334,
parameters: { fillColor: '#858585' },
},
],
},
],
}}
/>
</div>
Children can use the _alignment_ controls to override the parent's alignment settings.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/layout/align-props.png)
</div>
Children can use margins to offset the position from how it's aligned.
<div className="ndl-image-with-background l">
<img src="/2.8/docs/guides/user-interfaces/layout/align.gif"></img>
<CopyToClipboardButton
json={{
nodes: [
{
id: '8a968ff3-9099-fc23-eaef-b3f3c2f8a271',
type: 'Group',
label: 'Layout None',
x: -124.8300537163023,
y: 231.19983547495616,
parameters: {
backgroundColor: '#FFFFFF',
flexDirection: 'none',
},
ports: [],
children: [
{
id: '6c2df5ce-7596-06da-a5c5-335275501d64',
type: 'Circle',
x: -104.8300537163023,
y: 292.19983547495616,
parameters: {
fillColor: '#C9C9C9',
alignY: 'top',
alignX: 'left',
},
ports: [],
children: [],
},
],
},
],
connections: [],
}}
/>
</div>
## Margin and padding
Clicking on a node will select it and open the property panel. This panel has controls for setting the margin and padding.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/layout/margin-and-padding-props.png)
</div>
Here you can specify the _margin_, i.e. the distance between this node and its siblings in the layout. You can specify the margins in all four directions. You can also specify the _padding_, which is the distance from the borders of the **Group** to its children.
A good way to learn these concepts is to adjust and tweak the three nodes in this simple hierarchy. Try modifying the padding and margins and note the difference between them.
## Multiple lines
If the child nodes extend beyond the border of the parent you have a couple of options to control the behavior, wrapping or scrolling. This section will explain the wrapping options that are available.
Wrapping is controlled by the **Multi Line Wrap** option in the properties.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/layout/wrap.png)
</div>
You can choose any of these options:
- `Off` Children will extend beyond the borders of the parent node (default).
- `On` Children will be placed on multiple lines
- `On Reverse` The children will wrap but in the reverse direction.
Here's an example of a horizontal layout with **Multi Line Wrap** set to `On`.
<div className="ndl-image-with-background l">
<img src="/2.8/docs/guides/user-interfaces/layout/multi-line.gif"></img>
<CopyToClipboardButton
json={{
nodes: [
{
id: '4ea33114-1bd2-d35a-79b6-9c6daf4ba473',
type: 'Group',
x: 198.0,
y: 196.0,
parameters: {
backgroundColor: '#D6D6D6',
flexDirection: 'row',
flexWrap: 'wrap',
},
children: [
{
id: 'beeb24c5-2bf0-ca33-62ce-b448a10f1b09',
type: 'Circle',
x: 218.0,
y: 242.0,
parameters: { fillColor: '#858585' },
},
{
id: '1742b597-6ee5-dd00-0781-ec3c368b9bdc',
type: 'Circle',
x: 218.0,
y: 288.0,
parameters: { fillColor: '#858585' },
},
{
id: 'e4fc4976-3c13-4a5d-701c-ef3a72eb17b5',
type: 'Circle',
x: 218.0,
y: 334.0,
parameters: { fillColor: '#858585' },
},
{
id: '37441ff4-81ac-47ac-2882-8d3f5a4151b4',
type: 'Circle',
x: 251.53808416795107,
y: 210.45256017010956,
parameters: { fillColor: '#858585' },
},
{
id: '4d7a3378-7b80-1f6d-c0cd-285360f291f6',
type: 'Circle',
x: 251.53808416795107,
y: 256.45256017010956,
parameters: { fillColor: '#858585' },
},
{
id: 'b1a7b659-7519-e8ea-f4f5-96bba6954b81',
type: 'Circle',
x: 251.53808416795107,
y: 302.45256017010956,
parameters: { fillColor: '#858585' },
},
],
},
],
}}
/>
</div>

View File

@@ -0,0 +1,56 @@
---
title: Modules and Prefabs
hide_title: true
---
# Modules and Prefabs in Noodl
Noodl has a lot of nodes available out of the box, but it can never include a node for everything. For that reason you can extend your project with specific modules or prefabs containing nodes and components to capture specfic functionality or UI controls.
The main difference between **Modules** and **Prefabs** is that **Modules** install new nodes to the editor, while **Prefabs** clones components built with the core nodes, and stores them as if you created them yourself. Prefabs can be imported as many times as you want, and each clone can be modified how you see fit.
You can see the list of modules [here](/library/modules/overview), and prefabs [here](/library/prefabs/overview). Make sure to check in often as the library keeps growing quickly.
## How to add modules and prefabs to your project
You add modules to your project by opening the project you want to use the module in and the bring up the **Node Picker**. You can do this either by **Right Clicking** in the node canvas or by clicking the `+` icon at the top left.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/modules/add-new-node.png)
</div>
In the node picker, the find the tab **Modules** and then click **Import** on the module you want to use. The module is added to your project.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/modules/browse-modules.png)
</div>
After importing the module you new nodes and components are now available under **External Libraries**.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/modules/external-libraries.png)
</div>
Adding a prefab is done via the **Prefabs** tab in the same way, click **Import**. Prefabs will add a component to your library of components. If you already have components with that name (maybe you have included the prefab before), you will be asked to overwrite.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/modules/browse-prefabs.png)
</div>
When the prefab is imported you will get one or more new components in your **default sheet**.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/modules/prefabs-imported.png)
</div>
Don't forget to read the documentation for the prefabs you intend to use, there you will find how to use them but also useful guides on how to customize and build upon them to your liking.

View File

@@ -0,0 +1,18 @@
---
title: Building User Interfaces
hide_title: true
---
# Building User Interfaces in Noodl
The user interface in your Noodl App are all the things that your users will interact with on your screen. Lists, buttons, checkboxes, etc.
In Noodl, you build these using **Visual Nodes**, i.e. the blue nodes in your node graph. Visual nodes represents something that can be seen on a screen. In contrast, logical nodes, for example representing a query to the database or a condition to be evaluated are not directly visible on the screen.
The visual nodes can also be grouped together in hierarchies mainly to define how they are relate to each other in the layout on the screen. By putting two **Buttons** as children of the same **Group** you can for example hide both these **Buttons** by hiding the **Group**. The order of the visual nodes also generally tells which order they will be rendered on the screen. Nodes later in the tree will be rendered on top of nodes earlier in the tree, if they happen to occupy the same space in the tree. (However this can be overridden using the **zIndex** property.)
As you can understand, for a large App, the visual tree can quickly become very large. That's why you typically create **Components** of subtrees that capture a specific functionality. The components can in themselves contain more components. Components are also a great way to re-use UI in many places in your app and create a design system greatly reducing time to build UI.
The visual nodes can also be styled and customized heavily from a visual perspective. Also the visualization aspect of a visual node can be saved and re-used using the style variants system. Many visual nodes also have multiple visual states. For example a Button will typically change appearance when the user hovers over it, clicks it or if the button is disabled. Using the visual states system or the **States** node, you can finetune in detail how you UI will react visibly to ser interactions.
All of this and more is covered in this part of the guide.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,270 @@
---
title: Scrolling Content
hide_title: true
---
import ImportButton from '../../../src/components/importbutton'
import ReactPlayer from 'react-player'
# Scrolling Content
## What you will learn in this guide
This guide will teach you the basics on how to make content larger than its container visible through scrolling. We will mainly make use of the **Scrolling** property on the [Group](/nodes/basic-elements/group) node and go into detail on how the layout engine works in relation to scrolling. We will also look at the built in scrolling functionality in the [Page Router](/nodes/navigation/page-router) that makes it easy to build pages with scrollable content.
## Overview
The guides will walk through the following topics
- How the **Group** node gets it size
- Scrolling in the **Group** node
- Scrolling in the **Page Router** node.
- `Sticky` Layout Position in a scrolling **Group**
This guide will use the [Repeater](/nodes/ui-controls/repeater) node to create lists and also touch on navigation using the [Page Router](/nodes/navigation/page-router). So it's recommended to check out the guides for them either before or after following this guide.
- [List Basics Guide](/docs/guides/data/list-basics)
- [Web Navigation Guide](/docs/guides/navigation/basic-navigation)
You should also be familiar with layout basics so check out [this](/docs/guides/user-interfaces/layout) guide before you start this guide, if you haven't already.
## Scrolling in the Group node
The **Group** node is the fundamental node to create a layout. So what happens if the children of a **Group** node takes up more space than what's available?
Well, it depends on how you set up its sizing.
### The size of the Group node
On a high level there are two options (in both horizontal and vertical direction):
1. Size is explicitly set. The size can then be given in % of available space, pixels or vw
2. Size is decided by the total size of its children
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/scrolling-content/dims-1.png)
</div>
So we only need to worry about case 1), what happens if the children takes up more than the space that you give them in the Group?
Let's try it. Start a new project, for example using the "Hello World" template. Replace the current nodes with the ones below.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/scrolling-content/step-1.png)
</div>
Fill up the **Static Array** node with some data. Make sure its set to `JSON` format.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/scrolling-content/static-array-1.png)
</div>
Then copy and paste the data below into the **Static Array**.
```json
[
{ "label": "item 1" },
{ "label": "item 2" },
{ "label": "item 3" },
{ "label": "item 4" },
{ "label": "item 5" },
{ "label": "item 6" },
{ "label": "item 7" },
{ "label": "item 8" },
{ "label": "item 9" },
{ "label": "item 10" },
{ "label": "item 11" },
{ "label": "item 12" },
{ "label": "item 13" },
{ "label": "item 14" },
{ "label": "item 15" },
{ "label": "item 16" },
{ "label": "item 17" },
{ "label": "item 18" },
{ "label": "item 19" },
{ "label": "item 20" }
]
```
Then create a new visual component and call it "List Item".
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/scrolling-content/new-component.png)
</div>
And create the following node structure:
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/scrolling-content/list-item-1.png)
</div>
Make sure the **Object** node has a property called `label` connected to the **Text** node. Also set the size of the **Group** node to 100% width, and 45 px height. Give it a 5 px margin at the bottom. Also give it a outline so we can see it properly.
Center align the **Text** node vertically.
Finally go back to the "App" component and select the newly created list item as the **Template**.
You will now have something that looks like below. (You can also import the project directly by clicking "Import" below).
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/scrolling-content/result-1.png)
<ImportButton zip="/docs/guides/user-interfaces/scrolling-content/step-1.zip" name="Scrolling Part 1" thumb="/docs/guides/user-interfaces/scrolling-content/result-1.png" />
</div>
As you can see, the list takes up the full screen and unless you have a veeeeery long screen the items will flow outside the screen. You are not able to scroll the content.
Also try to change the vertical size of the **Group** node in the main App to `50%`.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/scrolling-content/50-percent.png)
</div>
You might expect that only half the screen would be covered by the list, but you will see no change. That's because the default behavior of a **Group** with explicit size set is that _if the size of the children are larger than the size of the Group, the size will grow to fit the children_.
You can change that behavior and tell the **Group** to clip the content instead of growing. Check the **Clip Content** property of the group.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/scrolling-content/clip.png)
</div>
### Getting the Group to Scroll
Another way to do it is to tell the **Group** that it allows scrolling. Uncheck the **Clip Content** and instead check **Enable Scroll**. You will now have a scrollable list.
<ReactPlayer playing autoplay muted loop url="scrolling-content/scroll1.mp4" />
As you can see there is an extra option **Use native scroll**. Generally you should have this checked, unless you are doing a very custom scroll interaction. We will not cover that in this guide.
### Structuring Scrolling Content
Change back the size to `100%` on the **Group** node.
Now let's add a title and and a footer. We can't add it to our **Group** node, because then they will scroll away with the list. So we have to restructure our layout a little.
Create a new **Group** node. Make the existing **Group** node a child of the new **Group** node.
<ReactPlayer playing autoplay muted loop url="scrolling-content/regroup.mp4" />
Now we can add a header and a footer to the highest level **Group** and they will not be affected by the scrolling.
The header and footer could for example be a **Group** node with content height and 100% width. Then add a **Text** with a larger font. Perhaps add a bit of padding in the **Group** containing the text.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/scrolling-content/header-footer.png)
</div>
Now you can see that the footer and header are allowed to take its space while the scrolling content takes whatever is left and lets its content scroll if it doesn't fit.
<ReactPlayer
playing
autoplay
muted
loop
url="scrolling-content/hf-scroll.mp4"
/>
## Scrolling in Page Routers
The **Page Router** is one of the main nodes to implement navigation in Noodl. In short, the **Page Router** contains the [Pages](/nodes/navigation/page/) you navigate to. In that sense, the **Page Router** is quite similar to a **Group** node. That's why a **Page Router** also have the options to make it's content scrollable by chosing **Clip Behavior** and setting **Scrolling**.
<div className="ndl-image-with-background m">
![](/docs/guides/user-interfaces/scrolling-content/page-router-props.png)
</div>
Lets try it out!
Create a new visual component. Call it "App 2". Select "Make Home" to make it the new home component (i.e it will be shown in the viewer).
<div className="ndl-image-with-background m">
![](/docs/guides/user-interfaces/scrolling-content/make-home.png)
</div>
Add a **Page Router** as the first child of the root **Group**
<div className="ndl-image-with-background m">
![](/docs/guides/user-interfaces/scrolling-content/page-router-1.png)
</div>
Click on the **Page Router** and click `Add new Page` and add a new **Page** called `Page 1`.
We can now simplify the previous structure. Since the **Page Router** will work as the **Group** we can move the **Repeater** (and the **Static Array** that provides the items) to right under the **Page** node in our newly created **Page** component.
<div className="ndl-image-with-background m">
![](/docs/guides/user-interfaces/scrolling-content/page-1.png)
</div>
Make sure to set the **Clip Behavior** to **Scrolling** on the **Page Router** and you now have scrolling content in your **Page**. Try it out.
### Sticky Layout Position option
There is another way to get things like headers and footers to not be scrolled out of the **Group** node even if scrolling is enabled, and we will try it out on our **Page**. Instead of creating a new structure we can add our header and footer to be direct children to the **Page** node. So copy and paste them so they end up before and after our **Repeater**.
<div className="ndl-image-with-background m">
![](/docs/guides/user-interfaces/scrolling-content/page-2.png)
</div>
Also make sure the header is aligned to the top and the footer aligned to the bottom.
If you try scrolling now, they will be scrolled in and out of the screen. However if you change the **Position** setting for the header and footer **Group** to `Sticky` you will see that they will stay on screen.
<div className="ndl-image-with-background m">
![](/docs/guides/user-interfaces/scrolling-content/sticky.png)
</div>
The only issue is that you can see the list scroll behind the header and footer. This is probably not what you want.
<ReactPlayer playing autoplay muted loop url="scrolling-content/sticky1.mp4" />
You fix that by setting the background color to non-transparent white. Also since Noodl by default renders everything in the order as it's in the tree (nodes later in the tree will be rendered on top of nodes earlier in the tree if they overlap), we also need to change the **zIndex** of the header to `1` to stay on top of the **Repeater** that's below it in the tree.
<div className="ndl-image-with-background m">
![](/docs/guides/user-interfaces/scrolling-content/zindex-1.png)
</div>
You now have a working scrolling list with a sticky header and footer.
<ReactPlayer playing autoplay muted loop url="scrolling-content/sticky2.mp4" />
If you want to try out the full project, import the project below.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/scrolling-content/result-2.png)
<ImportButton zip="/docs/guides/user-interfaces/scrolling-content/step-2.zip" name="Scrolling Part 2" thumb="/docs/guides/user-interfaces/scrolling-content/result-2.png" />
</div>

View File

@@ -0,0 +1,525 @@
---
title: States Node
hide_title: true
---
import useBaseUrl from '@docusaurus/useBaseUrl'
import CopyToClipboardButton from '../../../src/components/copytoclipboardbutton';
# States Node
A common concept in Noodl is the use of different visual states. In this guide we will cover states with an example of how to create a simple reusable switch component, and an example of hovering effects.
<div className="ndl-image-with-background s">
![](/docs/guides/user-interfaces/states/switch.gif)
</div>
The examples will cover how to use a **States** node for handling transitions and interactions.
## The visuals
First let's start with the basic visual nodes that make up the switch. This is simply a **Group** and a **Circle** with proper styling. You can copy the nodes below and paste into your own project.
<div className="ndl-image-with-background xl">
<img
src="/2.8/docs/guides/user-interfaces/states/switch-nodes.png"
className="ndl-image med"
></img>
<CopyToClipboardButton
json={{
nodes: [
{
id: 'beeb6388-461b-f1fe-d64e-be798e4b1b4d',
type: 'Group',
x: 441.71346128847347,
y: 300.98375737190554,
parameters: { backgroundColor: '#FFFFFF' },
ports: [],
children: [
{
id: '9677d929-9a49-7fc5-ae14-27a1b48e883f',
type: 'Group',
x: 461.71346128847347,
y: 346.98375737190554,
parameters: {
width: { value: 80, unit: 'px' },
height: { value: 40, unit: 'px' },
backgroundColor: '#F0F0F0',
borderRadius: 20,
alignX: 'center',
marginTop: { value: 20, unit: 'px' },
},
ports: [],
children: [
{
id: 'aeda33bf-10cb-1e76-07b2-4a368140ca65',
type: 'Circle',
x: 481.71346128847347,
y: 392.98375737190554,
parameters: {
fillColor: '#E8E8E8',
size: 40,
strokeWidth: 2,
strokeColor: '#454545',
strokeEnabled: true,
},
ports: [],
children: [],
},
],
},
],
},
],
connections: [],
}}
/>
</div>
Below you can see the styling of the two nodes. The first image shows the Circle node's properties and the second image shows the Group node's properties. Don't forget that you can hover the nodes in the graph to see their placement in the preview.
<div className="ndl-image-with-background l">
<img
src="/2.8/docs/guides/user-interfaces/states/style-props1.png"
className="ndl-image small"
></img>
<img
src="/2.8/docs/guides/user-interfaces/states/style-props2.png"
className="ndl-image small"
></img>
</div>
## The states node
A very common pattern is to represent states visually. In this example we want our switch to have two states **On** and **Off**, and we want it to have different visuals for these states. This is achieved with the **States** node. Start by creating a new [States](/nodes/utilities/logic/states) node.
Next, create the two states.
<div className="ndl-image-with-background l">
<img
src="/2.8/docs/guides/user-interfaces/states/create-states.gif"
className="ndl-image med"
></img>
</div>
The **States** node will be in one of the states that we just defined. It starts in the state specified by the **State** property that shows up when you add states. Later we will switch state by connecting a signal.
We will also specify **Values** for each of our states. In the same way you added states, add a single value and call it **Knob X**. It doesn't really matter what you call it, as long as you know what it is for. In this case it's for moving the knob's (the Circle node) X position to its correct position for the two states.
<div className="ndl-image-with-background">
<img
src="/2.8/docs/guides/user-interfaces/states/values-1.png"
className="ndl-image med"
></img>
</div>
Specify the value for each state. The X position should be **40** when the switch is in the **On** state and **0** when the switch is in the **Off** state. Enter these values in the **States** node properties.
<div className="ndl-image-with-background">
<img
src="/2.8/docs/guides/user-interfaces/states/on-values.png"
className="ndl-image med"
></img>
<img
src="/2.8/docs/guides/user-interfaces/states/off-values.png"
className="ndl-image med"
></img>
</div>
Connect the **Knob X** output of the **States** node to the **Pos X** input of the **Circle** node.
<div className="ndl-image-with-background xl">
<img src="/2.8/docs/guides/user-interfaces/states/knob-x-connected.png"></img>
</div>
As you can see the **States** node will get an output corresponding to each value that is defined under **Values**. This output will transition to the specified values when the **States** node changes state. One way to see the different states and transitions is to play with the **State** dropdown menu in the properties panel.
<div className="ndl-image-with-background xl">
<video width="100%" autoPlay muted loop src={useBaseUrl("/docs/guides/user-interfaces/states/change-state.mp4")}/>
</div>
If you hover over the output connection of the **States** node a small popup will show the current value that is being outputted. If you click this small popup, you pin the popup so that it stays visible. Now you can see the output value change as it transitions from one state to another.
You can connect the outputs of the **States** node to anything you like. In this example we connected the output to an input where they were both of the type **Number**. You can use different types than numbers. Add a new **Value** to the **States** node. Call it "Background Color" and give it a **Color** type instead of the default **Number** type. Then choose two different colors for the two states and connect the output to the **Background Color** of the **Group** node containing the **Circle**.
<div className="ndl-image-with-background">
<img
src="/2.8/docs/guides/user-interfaces/states/color-type.png"
className="ndl-image med"
></img>
</div>
Finally, we can make the **States** node toggle state when the switch **Group** node is clicked. This is achieved by connecting the **Click** signal from the **Group** to the **Toggle** input of the **States** node. This will make the **States** node jump to the next state in the list and when the last one is reached it will jump to the first one again. In this case we only have two states, so it will toggle between **On** and **Off**.
<div className="ndl-image-with-background xl">
<img src="/2.8/docs/guides/user-interfaces/states/click-toggle.png"></img>
<CopyToClipboardButton
json={{
nodes: [
{
id: 'f0d8169f-90ca-9cd9-bc88-af4c2ed631d0',
type: 'Group',
x: 441.71346128847347,
y: 300.98375737190554,
parameters: { backgroundColor: '#FFFFFF' },
ports: [],
children: [
{
id: 'f2461a9d-49c6-f184-1ac1-a75450a7c856',
type: 'Group',
x: 461.71346128847347,
y: 346.98375737190554,
parameters: {
width: { value: 80, unit: 'px' },
height: { value: 40, unit: 'px' },
backgroundColor: '#F0F0F0',
borderRadius: 20,
alignX: 'center',
marginTop: { value: 20, unit: 'px' },
},
ports: [],
children: [
{
id: 'c66997cc-19d2-630c-692e-0caafdf37dd7',
type: 'Circle',
x: 481.71346128847347,
y: 448.98375737190554,
parameters: {
fillColor: '#E8E8E8',
size: 40,
strokeWidth: 2,
strokeColor: '#454545',
strokeEnabled: true,
},
ports: [],
children: [],
},
],
},
],
},
{
id: '5d631656-dea2-b5d7-f5ee-ee27e220463b',
type: 'States',
x: 232.07664638676232,
y: 376.3848345864951,
parameters: {
states: 'On,Off',
values: 'Knob X,Background Color',
'type-Background Color': 'color',
'value-On-Knob X': 40,
'value-Off-Knob X': 0,
'value-Off-Background Color': '#F0F0F0',
'value-On-Background Color': '#CCE6CE',
startState: 'Off',
},
ports: [],
children: [],
},
],
connections: [
{
fromId: '5d631656-dea2-b5d7-f5ee-ee27e220463b',
fromProperty: 'Knob X',
toId: 'c66997cc-19d2-630c-692e-0caafdf37dd7',
toProperty: 'transformX',
},
{
fromId: '5d631656-dea2-b5d7-f5ee-ee27e220463b',
fromProperty: 'Background Color',
toId: 'f2461a9d-49c6-f184-1ac1-a75450a7c856',
toProperty: 'backgroundColor',
},
{
fromId: 'f2461a9d-49c6-f184-1ac1-a75450a7c856',
fromProperty: 'onClick',
toId: '5d631656-dea2-b5d7-f5ee-ee27e220463b',
toProperty: 'toggle',
},
],
}}
/>
</div>
## Transitions
For _number_ and _color_ types the **States** node will try to smoothly transition between the states. Sometimes this is not the desired behaviour, and sometimes you want to change how the transition behaves. Noodl let's you edit the transition curves to change the transition behaviour. Take a look at the example below. You can copy the nodes and replace the old nodes in your project or create a new component.
<div className="ndl-image-with-background xl">
<img
src="/2.8/docs/guides/user-interfaces/states/hover-fx-nodes.png"
className="ndl-image med"
></img>
<CopyToClipboardButton
json={{
nodes: [
{
id: '4c6ec6bd-2ba3-807a-9177-da451a835ec2',
type: 'Group',
x: 410.63681490171115,
y: 392,
parameters: { backgroundColor: '#FFFFFF' },
ports: [],
children: [
{
id: '56e1ec26-a280-b297-baf3-f5365f6a6124',
type: 'Group',
x: 430.63681490171115,
y: 438,
parameters: {
width: { value: 80, unit: 'px' },
height: { value: 80, unit: 'px' },
backgroundColor: '#E6DD39',
borderRadius: 20,
alignX: 'center',
marginTop: { value: 70, unit: 'px' },
},
ports: [],
children: [],
},
],
},
{
id: '34a39ad5-0999-133a-5f7e-6943ebd8d899',
type: 'States',
label: 'Hover',
x: 177.3701941946174,
y: 432.9317475959747,
parameters: {
states: 'Yes,No',
startState: 'No',
values: 'Size,Color',
'type-Color': 'color',
'value-Yes-Color': '#D4CE35',
'value-No-Color': '#323975',
'value-Yes-Size': 1.5,
'value-No-Size': 1,
},
ports: [],
children: [],
},
],
connections: [
{
fromId: '56e1ec26-a280-b297-baf3-f5365f6a6124',
fromProperty: 'hoverStart',
toId: '34a39ad5-0999-133a-5f7e-6943ebd8d899',
toProperty: 'to-Yes',
},
{
fromId: '56e1ec26-a280-b297-baf3-f5365f6a6124',
fromProperty: 'hoverEnd',
toId: '34a39ad5-0999-133a-5f7e-6943ebd8d899',
toProperty: 'to-No',
},
{
fromId: '34a39ad5-0999-133a-5f7e-6943ebd8d899',
fromProperty: 'Color',
toId: '56e1ec26-a280-b297-baf3-f5365f6a6124',
toProperty: 'backgroundColor',
},
{
fromId: '34a39ad5-0999-133a-5f7e-6943ebd8d899',
fromProperty: 'Size',
toId: '56e1ec26-a280-b297-baf3-f5365f6a6124',
toProperty: 'transformScale',
},
],
}}
/>
</div>
This simple graph has a **States** node that controls the hover state of a **Group**. As you can see the **Group** sends a signal when it is hovered which transitions the **States** node to the **Yes** state. A different signal is sent when the mouse leaves (you stop hovering), which changes the **States** node to the **No** state. The **States** node changes the **Scale** and **Color** of the **Group** node when it goes between its states. The effect is shown below:
<div className="ndl-image-with-background">
<img
src="/2.8/docs/guides/user-interfaces/states/hover-fx1.gif"
className="ndl-image small"
></img>
</div>
Both transitions (color and size) have the default transition curves. You can edit the transition curves in the properties of the **States** node. Look for the transitions when moving to the **Yes** state (i.e. the hovered state).
<div className="ndl-image-with-background">
<img
src="/2.8/docs/guides/user-interfaces/states/transition-props.png"
className="ndl-image med"
></img>
</div>
The **Default** property is a transition curve that is used for all values if they don't have a specific transition curve set for themselves. If you want to change the curve for all transitions use this.
In this case we want to change the curve for the **Size** transition. You can edit the curve via the curve editor.
<div className="ndl-image-with-background l">
<img
src="/2.8/docs/guides/user-interfaces/states/change-size-curve.gif"
className="ndl-image med"
></img>
</div>
You can play with different settings for the curves and see them working when you hover the rectangle in the preview. Here is one example with a little bounce in the **Size** transition and a linear **Color** transition. Don't forget that you can use the preset icons on the right in the curve editor to choose between four presets:
- **Ease In** Slower in the beginning and accelerating towards the end.
- **Ease Out** Decelerates towards the end.
- **Ease In and Out** Smooth/slower start and end.
- **Linear** Linear animation, starts instantly and keeps constant speed.
<div className="ndl-image-with-background">
<img
src="/2.8/docs/guides/user-interfaces/states/hover-fx2.gif"
className="ndl-image small"
></img>
</div>
## Chaining States
You can also connect several **States** nodes together to create more complex types of animations. In the node graph below we have a second **States** node that expands the **Group** node (changes the width). The second **States** node labeled **Expand** changes the width between the states **Yes** and **No**. The neat thing is that it is triggered when the first states node (the hover states) has reached the **Yes** state. It is returned to **No** when the mouse leaves (the hover end), just like the first node.
<div className="ndl-image-with-background xl">
<img src="/2.8/docs/guides/user-interfaces/states/chaining-states.png"></img>
<CopyToClipboardButton
json={{
nodes: [
{
id: '31b24415-e1ef-7480-db67-f8c3d9df8965',
type: 'Group',
x: 410.63681490171115,
y: 392,
parameters: { backgroundColor: '#FFFFFF' },
ports: [],
children: [
{
id: '2255f3f8-c7c6-a195-9e20-22204968c916',
type: 'Group',
x: 430.63681490171115,
y: 438,
parameters: {
width: { value: 80, unit: 'px' },
height: { value: 80, unit: 'px' },
backgroundColor: '#E6DD39',
borderRadius: 20,
alignX: 'center',
marginTop: { value: 70, unit: 'px' },
},
ports: [],
children: [],
},
],
},
{
id: 'dadabea3-2a7f-ca23-a71d-79fae0cfff6e',
type: 'States',
label: 'Hover',
x: 173.3701941946174,
y: 343.9317475959747,
parameters: {
states: 'Yes,No',
startState: 'No',
values: 'Size,Color',
'type-Color': 'color',
'value-Yes-Color': '#D4CE35',
'value-No-Color': '#323975',
'value-Yes-Size': 1.5,
'value-No-Size': 1,
'transition-Yes-Size': {
curve: [0, 0, 0.39, 2.31],
dur: 300,
delay: 0,
},
'transition-Yes-Color': {
curve: [0, 0, 1, 1],
dur: 300,
delay: 0,
},
'transition-No-Color': {
curve: [0, 0, 1, 1],
dur: 300,
delay: 0,
},
},
ports: [],
children: [],
},
{
id: '82a0d318-ac00-eb50-653e-bf2aa4344425',
type: 'States',
label: 'Expand',
x: 181.5019645647742,
y: 554.7158737979873,
parameters: {
states: 'Yes,No',
values: 'Width',
'value-Yes-Width': 200,
'value-No-Width': 90,
startState: 'No',
'transition-Yes-Width': {
curve: [0, 0, 0.58, 1],
dur: 500,
delay: 0,
},
},
ports: [],
children: [],
},
],
connections: [
{
fromId: '2255f3f8-c7c6-a195-9e20-22204968c916',
fromProperty: 'hoverStart',
toId: 'dadabea3-2a7f-ca23-a71d-79fae0cfff6e',
toProperty: 'to-Yes',
},
{
fromId: '2255f3f8-c7c6-a195-9e20-22204968c916',
fromProperty: 'hoverEnd',
toId: 'dadabea3-2a7f-ca23-a71d-79fae0cfff6e',
toProperty: 'to-No',
},
{
fromId: 'dadabea3-2a7f-ca23-a71d-79fae0cfff6e',
fromProperty: 'Color',
toId: '2255f3f8-c7c6-a195-9e20-22204968c916',
toProperty: 'backgroundColor',
},
{
fromId: 'dadabea3-2a7f-ca23-a71d-79fae0cfff6e',
fromProperty: 'Size',
toId: '2255f3f8-c7c6-a195-9e20-22204968c916',
toProperty: 'transformScale',
},
{
fromId: '82a0d318-ac00-eb50-653e-bf2aa4344425',
fromProperty: 'Width',
toId: '2255f3f8-c7c6-a195-9e20-22204968c916',
toProperty: 'width',
},
{
fromId: 'dadabea3-2a7f-ca23-a71d-79fae0cfff6e',
fromProperty: 'reached-Yes',
toId: '82a0d318-ac00-eb50-653e-bf2aa4344425',
toProperty: 'to-Yes',
},
{
fromId: '2255f3f8-c7c6-a195-9e20-22204968c916',
fromProperty: 'hoverEnd',
toId: '82a0d318-ac00-eb50-653e-bf2aa4344425',
toProperty: 'to-No',
},
],
}}
/>
</div>
This will result in the behaviour shown below. As you can see the first **States** node triggers the bouncy size change, which is immediately followed by the second **Expand** states node that changes the state to **Yes** and transitions the width to the expanded state.
<div className="ndl-image-with-background">
<img
src="/2.8/docs/guides/user-interfaces/states/hover-fx3.gif"
className="ndl-image small"
></img>
</div>
The **States** node is one of the most commonly used nodes in Noodl apps, and mastering it will let you create many great interactions. Have fun!

View File

@@ -0,0 +1,162 @@
---
title: Style Variants
hide_title: true
---
# Style Variants
## What you will learn in this guide
When building apps its very common that we need the same styling in multiple places. This guide will take you through the most common method of reusing styles, using the Style Variants feature.
## Overview
We will go through the following steps in this guide:
- Create a Style Variant
- Learn the differences between overrides and variants
- Edit a Style Variant
There is also a video walking through the guide.
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/zFF8hoC-JM8" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen></iframe>
## The quick and dirty method
We could just copy and paste a node we styled, but that comes with a couple of downsides.
First of all, we would have to search through all of our pages and components looking for the styled visual node we want whenever we need to include it in a new place.
Similarly, if we would find that the design needs to be tweaked, we would have to go through all pages and components, manually updating every instance of this copied node.
The better way to achieve reusability and maintainability is by using Style Variants.
## Creating a Style Variant
Lets see it in action. In our design system we have a “Primary” button used to indicate the main actions, and a “Secondary” button that is a bit more discreet. Lets build them, and use them in our app.
We start by creating a button in the node tree.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/style-variants/create-button.png)
</div>
We will add a bit more left and right padding, increase the height and make the corners rounder.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/style-variants/button.png)
</div>
That looks good. Now lets create another button.
<div className="ndl-image-with-background s">
![](/docs/guides/user-interfaces/style-variants/second-button.png)
</div>
Oh, wait. Why does the new button not have any of the changes that we made? This is because we didnt save the changes as a variant, so they became overrides on this specific instance of the button, instead of being attached to all the Button nodes.
Lets fix this and save our changes as a variant so that we can start reusing it.
At the top of our restyled buttons Property Panel, click the “plus”, “Create new variant”, give it a name and confirm.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/style-variants/create-variant.png)
</div>
This will take all the overrides, remove them from the current instance and save them as a new variant together with all other non-overridden properties.
We can now select the second button and set it to use the "Primary" variant.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/style-variants/set-variant.png)
</div>
<div className="ndl-image-with-background s">
![](/docs/guides/user-interfaces/style-variants/styled-buttons.png)
</div>
## Editing a Style Variant
Now that we have the “Primary” button, lets create the “Secondary” variant.
For demonstrational purposes I will show you another workflow so that you can pick the one you like the best. This method will also cover how to edit variants.
Since the “Secondary” variant is very similar to the “Primary” one we will create a button and set it to use the “Primary” variant. This way we dont have to repeat any of the styling weve already done.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/style-variants/set-variant.png)
</div>
<div className="ndl-image-with-background s">
![](/docs/guides/user-interfaces/style-variants/styled-buttons.png)
</div>
This time we will not override any styles though. Instead well immediately create a new variant and name it “Secondary”.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/style-variants/new-variant.png)
![](/docs/guides/user-interfaces/style-variants/name-new-variant.png)
</div>
We now have a “Secondary” variant that is identical to the “Primary” one.
As you already know, if we change any properties now it will count as overrides, and only be saved to this particular instance of the Button node. So, in order to edit the Variant and not the Instance, we press the “Edit variant” button.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/style-variants/edit-variant.png)
</div>
Notice how the borders of the Property Panel become a helpful teal color. This is so that we remember that we are in the Variant editor.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/style-variants/helpful-teal.png)
</div>
Lets give this variant a “Primary” colored text, a transparent background and a “Primary” colored border.
<div className="ndl-image-with-background s">
![](/docs/guides/user-interfaces/style-variants/two-buttons.png)
</div>
In the “Visual States” guide I got some feedback from my designer friend that I forgot to style the hover state, so lets make them happy and update the hover styles this time. Lets make the background a nice “Primary Light” color. All of the visual states are tied to a Variant and saved on it.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/style-variants/visual-state.png)
</div>
All changes done when editing a Variant are saved automatically, so we dont have to do anything else now that were done with the styling, but if we for some reason want to go back to editing the instance, we can click the “Close” button to exit the Variant editor.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/style-variants/close.png)
</div>
We can now populate our whole app with identical buttons, making sure that they always follow the styling defined in our brand guidelines. And if the guidelines would change, we can quickly edit a variant and have it be updated across our whole app.

View File

@@ -0,0 +1,70 @@
---
title: Visual States
hide_title: true
---
# Working with Visual States
## What you will learn in this guide
This guide will teach you how to style **Visual nodes** differently based on user interaction.
## Overview
We will go through the following steps in this guide:
- Create a button
- Style its `neutral` state
- Style it differently in its `hover` state
There is also a video walking through the guide.
<iframe width="560" height="315" src="https://www.youtube.com/embed/ATyqeK_deu4" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen></iframe>
## Let's go!
When working with Visual nodes we often find ourselves in situations where we want to style the node differently based on the user interaction. We might want to have a different background color when hovering a button, or indicating that a checkbox has been checked. This is done using **Visual States** in the nodes settings in the **Property Panel**.
Lets create a button in our node tree, and give it a quick styling.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/visual-states/node-created.png)
</div>
Our brand guidelines tell us that all buttons need to be the Dark color and that they shouldn't have any rounded corners.
<div className="ndl-image-with-background s">
![](/docs/guides/user-interfaces/visual-states/wrong-hover.gif)
</div>
Ok, so, it looks good now, but when I sent it to my designer friend they told me that while the button looked good in the neutral state, it was all wrong when hovered. The button is supposed to be the Primary Light color, and the text should be Dark. Lets fix that by modifying the Hover state.
With the Button node still selected, lets move over to the Visual State selector and click the Hover option.
<div className="ndl-image-with-background">
![](/docs/guides/user-interfaces/visual-states/states.png)
</div>
We will see the Property Panel shift around a bit. Some properties just don't make any sense to change in an interaction state, so Noodl will hide those properties for us.
Lets scroll down to the properties for Label and Background Color and update them.
<div className="ndl-image-with-background l">
![](/docs/guides/user-interfaces/visual-states/update-states.png)
</div>
And voila - we are now following the brand guidelines.
<div className="ndl-image-with-background s">
![](/docs/guides/user-interfaces/visual-states/right-hover.gif)
</div>