-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added two new node types, Trigger and Smooth (#39)
* added a function generator component but not quite 100% there yet * trying with setInternal, since we seem to lack a global update loop * trying with setInternal, since we seem to lack a global update loop * trying with setInternal, since we seem to lack a global update loop * renamed Ugen to Oscillator, because it's easier to understand Ugen is too obscure... added some documentation notes to self * fixed two bugs, the breaks were missing in the switch statement and options were not automagically updated by the base class, so actual settings need to be obtained from this.options, not from the private object variables * added missing step suggested by xiduzo * Oscillator shows tips in node * added Trigger node * added new node type * added Trigger node * was missing the dropdown to choose the random generator * added new node Smooth * added a more complete documentation entry about how to create your own node in the documentation website * removed temporary documentation and placed the content where it belongs * added a configurable timeout to the Trigger so that it can be used, like a sustain in a ASDR envelope * added some OCD feedback * added some OCD feedback * chore: reactify --------- Co-authored-by: boringrgb <boringrgb@nordzee> Co-authored-by: Sander <mail@sanderboer.nl>
- Loading branch information
1 parent
d559f07
commit ef9726f
Showing
11 changed files
with
480 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
apps/electron-app/src/render/components/react-flow/nodes/Smooth.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import type { SmoothData, SmoothValueType } from '@microflow/components'; | ||
import { Position } from '@xyflow/react'; | ||
import { useEffect } from 'react'; | ||
import { Handle } from './Handle'; | ||
import { BaseNode, NodeContainer, useNode, useNodeSettingsPane } from './Node'; | ||
|
||
export function Smooth(props: Props) { | ||
return ( | ||
<NodeContainer {...props}> | ||
<Value /> | ||
<Settings /> | ||
<Handle type="target" position={Position.Left} id="signal" offset={-0.5} /> | ||
<Handle type="source" position={Position.Bottom} id="change" /> | ||
</NodeContainer> | ||
); | ||
} | ||
|
||
function Value() { | ||
const { data } = useNode<SmoothData>(); | ||
|
||
return <section className="tabular-nums">{data.attenuation.toFixed(3)}</section>; | ||
} | ||
|
||
function Settings() { | ||
const { pane, settings } = useNodeSettingsPane<SmoothData>(); | ||
|
||
useEffect(() => { | ||
if (!pane) return; | ||
|
||
pane.addBinding(settings, 'attenuation', { | ||
index: 0, | ||
min: 0.0, | ||
max: 1.0, | ||
step: 0.001, | ||
label: 'attenuation', | ||
}); | ||
}, [pane, settings]); | ||
|
||
return null; | ||
} | ||
|
||
type Props = BaseNode<SmoothData, SmoothValueType>; | ||
export const DEFAULT_SMOOTH_DATA: Props['data'] = { | ||
label: 'Smooth', | ||
attenuation: 0.995, | ||
}; |
72 changes: 72 additions & 0 deletions
72
apps/electron-app/src/render/components/react-flow/nodes/Trigger.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import type { TriggerData, TriggerValueType } from '@microflow/components'; | ||
import { Position } from '@xyflow/react'; | ||
import { useEffect } from 'react'; | ||
import { Handle } from './Handle'; | ||
import { BaseNode, NodeContainer, useNode, useNodeSettingsPane } from './Node'; | ||
import { Icons } from '@ui/index'; | ||
|
||
export function Trigger(props: Props) { | ||
return ( | ||
<NodeContainer {...props}> | ||
<Value /> | ||
<Settings /> | ||
<Handle type="target" position={Position.Left} id="signal" offset={-0.5} /> | ||
<Handle type="source" position={Position.Bottom} id="change" /> | ||
</NodeContainer> | ||
); | ||
} | ||
|
||
function Value() { | ||
const { data } = useNode<TriggerData>(); | ||
|
||
return ( | ||
<section className="flex flex-col text-center gap-1"> | ||
{data.behaviour === 'exact' && <Icons.Equal size={48} />} | ||
{data.behaviour === 'increasing' && <Icons.TrendingUp size={48} />} | ||
{data.behaviour === 'decreasing' && <Icons.TrendingDown size={48} />} | ||
<div className="text-muted-foreground text-xs">{data.threshold}</div> | ||
</section> | ||
); | ||
} | ||
|
||
function Settings() { | ||
const { pane, settings } = useNodeSettingsPane<TriggerData>(); | ||
|
||
useEffect(() => { | ||
if (!pane) return; | ||
|
||
pane.addBinding(settings, 'behaviour', { | ||
index: 0, | ||
view: 'list', | ||
label: 'behaviour', | ||
options: [ | ||
{ value: 'increasing', text: 'when increasing' }, | ||
{ value: 'exact', text: 'when exactly equal' }, | ||
{ value: 'decreasing', text: 'when decreasing' }, | ||
], | ||
}); | ||
|
||
pane.addBinding(settings, 'threshold', { | ||
index: 1, | ||
label: 'threshold value', | ||
}); | ||
|
||
pane.addBinding(settings, 'duration', { | ||
index: 2, | ||
min: 0.1, | ||
max: 1000, | ||
step: 0.1, | ||
label: 'duration', | ||
}); | ||
}, [pane, settings]); | ||
|
||
return null; | ||
} | ||
|
||
type Props = BaseNode<TriggerData, TriggerValueType>; | ||
export const DEFAULT_TRIGGER_DATA: Props['data'] = { | ||
label: 'Trigger', | ||
behaviour: 'exact', | ||
threshold: 0.5, | ||
duration: 250, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
--- | ||
title: How to add your own node | ||
--- | ||
|
||
Adding your own node requires a bit of boilerplate and manual work at the moment. | ||
|
||
|
||
## Step 1: creating your own component type | ||
|
||
Let's say you want to create your own node type called `MyNode`. First you need to create a file with it's own class in `packages/components/src/YourNode.ts` your type must extend the BaseComponent class. Here's an example declaration: | ||
|
||
``` | ||
export type MyNodeValueType = number; | ||
export class MyNode extends BaseComponent<MyNodeValueType> {} | ||
``` | ||
|
||
Let's now say that your node type will have a configuration panel where you can change some attributes, for now let's say the attributes are a drop-down that let's you choose between `happy` and `sad`, and a numeric value we call `joy`. To be able to contain the values of these attributes you will need a data type associated to your node. | ||
|
||
``` | ||
export type EmotionType = 'happy' | 'sad'; | ||
export type MyNodeData = { | ||
emotion: EmotionType; | ||
joy: number; | ||
}; | ||
type MyNodeOptions = BaseComponentOptions & MyNodeData; | ||
``` | ||
|
||
Add a constructor to your type that takes the attributes and passes them to the superclass. Like this: | ||
|
||
``` | ||
constructor(private readonly options: MyNodeOptions) { | ||
super(options, 0); | ||
} | ||
``` | ||
|
||
## Step 2: expose your new type in the components packages and refresh build | ||
|
||
- Include your newly created component in the `index.ts` file in `packages/components`. This will make your new components available in the `@microflow/components` package, so that they can be used later in the electron app. | ||
- run `yarn build` in the `microflow/packages/components` directory, you need to do this before you run yarn at the `app` level directories | ||
|
||
## Step 3: create a react wrapper in the electron app | ||
|
||
- Create a reactflow wrapper for your node type in `apps/electron-app/src/common/render/componenets/react-flow/nodes/YourNode.tsx` | ||
- Implement here your JSX | ||
|
||
``` | ||
export function MyNode(props: Props) { | ||
return ( | ||
<NodeContainer {...props}> | ||
<Value /> | ||
<Settings /> | ||
<Handle type="target" position={Position.Left} id="input" /> | ||
<Handle type="source" position={Position.Bottom} id="change" /> | ||
</NodeContainer> | ||
); | ||
} | ||
``` | ||
|
||
- Your config panel (@TODO link to documentation) | ||
|
||
``` | ||
function Settings() { | ||
const { pane, settings } = useNodeSettingsPane<MyNodeData>(); | ||
useEffect(() => { | ||
if (!pane) return; | ||
pane.addBinding(settings, 'emotion', { | ||
index: 0, | ||
view: 'list', | ||
label: 'validate', | ||
options: [ | ||
{ value: 'happy', text: 'Happy' }, | ||
{ value: 'sad', text: 'Sad' }, | ||
], | ||
}); | ||
pane.addBinding(settings, 'joy', { | ||
index: 1, | ||
min: 1, | ||
max: 100, | ||
step: 0.5, | ||
}); | ||
}, [pane, settings]); | ||
return null; | ||
} | ||
``` | ||
|
||
- And your panel setting defaults. | ||
|
||
``` | ||
type Props = BaseNode<MyNodeData, MyNodeValueType>; | ||
export const DEFAULT_MYNODE_DATA: Props['data'] = { | ||
label: 'MyNode', | ||
emotion: 'happy', | ||
joy: 95, | ||
}; | ||
``` | ||
|
||
- Add a reference in `apps/electron-app/src/common/nodes.ts` | ||
|
||
``` | ||
import { DEFAULT_MYNODE_DATA, MyNode } from '../render/components/react-flow/nodes/MyNode'; | ||
``` | ||
|
||
Add the correct entry to the `NODE_TYPES` list: | ||
``` | ||
export const NODE_TYPES = { | ||
... | ||
MyNode: MyNode, | ||
... | ||
};``` | ||
And last but not least i that same file, add an entry that specifies the default attribute values for the node: | ||
``` | ||
DEFAULT_MYNODE_DATA.set('MyNode', DEFAULT_MYNODE_DATA); | ||
``` | ||
- Add some JSX in `apps/electron-app/src/render/NewNodeProvider.tsx` so that it appears in the search menu. | ||
``` | ||
<CommandItem onSelect={selectNode('MyNode')}> | ||
MyNode | ||
<CommandShortcut> | ||
<Badge variant="outline">Custom</Badge> | ||
</CommandShortcut> | ||
</CommandItem> | ||
``` |
Oops, something went wrong.