Initial commit

Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com>
Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com>
Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com>
Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com>
Co-Authored-By: Johan  <4934465+joolsus@users.noreply.github.com>
Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com>
Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
This commit is contained in:
Michael Cartner
2024-01-26 11:52:55 +01:00
commit b9c60b07dc
2789 changed files with 868795 additions and 0 deletions

View File

@@ -0,0 +1,338 @@
# Interactive tutorials HOWTO
## Keyboard shortcuts
- `Shift + Cmd + T`: Restart tutorial
- `Shift + Cmd + R`: Reload lesson (and images). Doesn't restart tutorial
- `Shift + Cmd + N`: Jump to next step even if conditions aren't correct
## Tutorial file format
A tutorial is written in a .html file.
A step can be one of the following:
1. A modal popup. Intended to be used as the first step to explain the tutorial, but can be used anywhere during the tutorial.
An item that shows up in the "tutorial bar" at the bottom:
2. An item can have conditions, in which case the popup only is shown when the user clicks the item
3. An item without conditions will show the popup automatically
### Step with just a modal
A popup in middle of screen you have to dismiss to continue
```html
<div data-template="popup">... any html ...</div>
```
Example:
```html
<div data-template="popup">
<img src="cool-gif.gif" />
<h2>Welcome to this tutorial</h2>
<p>You're going to learn some awesome stuff</p>
</div>
```
### Step without conditions
It's recommended to use `<h2>` in the item
Format is as follows:
```html
<div>
<div data-template="item">
<header>...header text and stuff ...</header>
<h2>Some text</h2>
</div>
<div data-template="popup">... popup content ...</div>
</div>
```
Example:
```html
<div>
<div>
<div data-template="item">
<header>Info</header>
<h2>The node graph</h2>
</div>
<div data-template="popup">
<img src="node-graph.gif" />
<h2>The node graph</h2>
<p>Some text</p>
</div>
</div>
</div>
```
### Step with conditions
Format is same as above, with two additions. Conditions and a checkmark.
It's recommended to use `<h3>` in the item.
```html
<div data-conditions="[ list of conditions as json ]" }>
<div data-template="item">
<header>...header text and stuff ... <span class="lesson-checkmark"></span></header>
<h3>Some text</h3>
</div>
<div data-template="popup">... popup content ...</div>
</div>
```
Example:
```html
<div
data-conditions='[
{"path":"/App:%Group:0","exists":true}
]'
>
<div data-template="item">
<header>Task <span class="lesson-checkmark"></span></header>
<h3>Drag the text node back to make it visible again</h3>
</div>
<div data-template="popup">
<img src="drag-text-out.gif" />
</div>
</div>
```
### Step width
A step can have a custom width. Useful if the text doesnät fit the standard size:
```html
<div>
<div data-template="item" style="width: 300px">...</div>
<div data-template="popup">...</div>
</div>
```
## Conditions
A step can have conditions that are required to evaluate to true before the user can proceed to the next step.
### Paths
All conditions point to one or more node.
The path to a node is specified by using a path that starts with the component name, and then any of the following tokens:
- `#label`: Look for a node with a label that matches the specified label
- `%type`: Look for a node of a specific type
- `2`: Look for a child index (in this case child 2, the third child)
The first part of a path is always a component.
Second part looks for a root node that matches the token.
Third part will look for a child in the matched root node.
... and so on.
Example:
- `/Main:%Group`: "/Main" component -> Any root node with the type "Group". If there's more than one Group root node then the condition will just pick the first one it finds.
- `/Main:%Group:%Button`: "/Main" component -> Any root node with the type "Group" -> First child that with the type "Button".
- `/Main:#Some Label`: "/Main" component -> A root node with the label "Some Label"
- `/Main:#Some Label:0`: "/Main" component -> A root node with the label "Some Label -> First child
- `/Main:#Some Label:%Button`: "/Main" component -> A root node with the label "Some Label -> First child with the type "Button"
- `/#Sheet A/Comp A:JavaScriptFunction`: Sheet "Sheet A" component "Comp A" -> A root node with the type "Function" (check project.json for the node types if unsure)
### Check if a node exist or not at the specified path:
Check if there's a node at the specified path
```json
{"path":"/App:#root:1", "exists":true}]
```
Or the inverse, that there's no node at that path
```json
{"path":"/App:#root:1", "exists":false}]
```
### Check for connection
Check if there's a connection between two nodes between the specified ports.
```json
{
"from": "/App:#nodeLabelA",
"to": "/App:#nodeLabelB",
"hasconnection": "sourcePortName,targetPortName"
}
```
### Check if a node has a specific label
```json
{
"path": "/App:#root:1",
"haslabel": "Some label"
}
```
### Check if a node has a specific type
```json
{
"path": "/App:#root:1",
"hastype": "Group"
}
```
### Check if paramters are set on a node
Check if a node has any value set on one or more paramters.
```json
{
"path": "/App:#root:1",
"hasparams": "marginLeft,marginRight"
}
```
### Check parameters on a node
Check the value of one or more paramters. The values are the ones specified in the `project.json` file, so take a look there if unsure.
```json
{
"path": "/App:#root:1",
"paramseq": {
"color": "#000000",
"width": { "value": 300, "unit": "px" },
"alignX": "center",
"alignY": "center",
"sizeMode": "contentSize"
}
}
```
Example: Check for states in a state node
```json
{
"path": "/App:#root:1",
"paramseq": {
"paramseq": {
"states": "Visible,Hidden",
"values": "Opacity"
}
}
}
```
### Check if a port exists on a node
Check if a node has a certain port.
```json
{
"path": "/App:#some function node",
"hasport": "myInput"
}
}
```
## Actions from buttons
### Exiting a lesson from a click event
A `data-click` attribute can be added on an HTML tag to make it trigger certain actions in the editor.
### Exiting a lesson
```html
<button style="width: 150px" data-click="exitEditor">EXIT LESSON</button>
```
## Examples of common nodes with non-obvious names
### Check if an Object node has certain properties.
NOTE: the order the properties are created matters, so they should be done step by step instead of all at once
```json
{
"path": "/My component:%Model2",
"paramseq": {
"properties": "url,author"
}
}
```
### Check connection from Object node
```json
{
"from": "/My component:%Model2",
"to": "/My component::%Image",
"hasconnection": "prop-url,src"
}
```
### Check if a Page Router has certain pages
NOTE: the order the pages does <b>NOT</b> matters, so adding multiple pages in one step is fine.
```json
{
"path": "/App:%Group:%Router",
"paramseq": {
"pages": {
"routes": ["/Page 1", "/Page 2", "/Page 3"]
}
}
}
```
### Check if a Page Router has a specific start page
```json
{
"path": "/App:%Group:%Router",
"paramseq": {
"pages": {
"startPage": "/Page 2"
}
}
}
```
### Check if a Navigate node points to a specific target page
```json
{
"path": "/App:%RouterNavigate",
"paramseq": {
"target": "/Page 2"
}
}
```
### Check a Page Input for a specific input and if it's connected
NOTE: if there's more than one page paramter, then it behaves like an Object. It's a comma separated list, and the order is important.
```json
[
{
"path": "/Page 1:%PageInputs",
"paramseq": {
"pathParams": "myPageInput"
}
},
{
"from": "/Page 1:%PageInputs",
"to": "/Page 1:%Page:%Text",
"hasconnection": "pm-myPageInput,text"
}
]
```

View File

@@ -0,0 +1,75 @@
# Create a Sidebar HOWTO
## Creating a sidebar
```tsx
import React, { useState } from 'react';
import { ReactView } from '../../../../shared/ReactView';
import { UndoQueue } from '@noodl-models/undo-queue-model';
import { PrimaryButton } from '@noodl-core-ui/inputs/PrimaryButton';
import { Container, ContainerDirection } from '@noodl-core-ui/layout/Container';
function UndoQueuePanel({}) {
const [queue, setQueue] = useState([]);
function refresh() {
const undoQueue: TSFixme[] = UndoQueue.instance.queue;
setQueue([...undoQueue]);
}
return (
<>
<Container hasXSpacing hasYSpacing>
<PrimaryButton label="Refresh" onClick={refresh} isGrowing />
</Container>
<Container hasXSpacing hasYSpacing direction={ContainerDirection.Vertical}>
{queue.map((item, index) => (
<PrimaryButton key={index} label={item.label} isGrowing />
))}
</Container>
</>
);
}
// NOTE: Once Frames component is rewritten to React we don't need to have the
// ReactView here.
export class UndoQueuePanelView extends ReactView<{}> {
constructor(args) {
super({});
}
protected renderReact(props: {}): JSX.Element {
return UndoQueuePanel({});
}
}
```
## Register the Sidebar
```ts
SidebarModel.instance.register({
hidden: false,
id: 'undo-queue',
name: 'Undo Queue',
order: 10,
icon: 'sidebar-toolbar-components-icon',
panel: (args) => new UndoQueuePanelView()
});
```
## Special Sidebar for a Node
Define the sidebar panel you want the node to have in the node definition.
This will open that sidebar panel when the node is selected.
```ts
{
panels: [
{
name: "PortEditor",
}
],
}
```