This can be exact same with the Deno/Node.js version. On browser, if you want to register interface you must also register the Sketch Interface.
// Define your node structure and place your code to handle the interface here// With compatibility in mind (can be imported for Browser/Node.js)Blackprint.registerNode('Example/Nodes/Button',classYourNodeNameextendsBlackprint.Node {// Define output ports for this nodestatic output = { Clicked: Function // This will be connected to other node's input port if exist };// Define input ports for this node// static input = { ThePortName: DataTypeHere };constructor(instance){super(instance);// Let's use 'BPIC/Nodes/Button interface for this nodelet iface =this.setInterface('BPIC/Nodes/Button');iface.title ="My Button"; // Custom node title }// Triggered from 'BPIC/Nodes/Button' interfaceclicked(ev){console.log('Example/Nodes/Button: got', ev);node.outputs.Clicked(ev); }});// Registering Interface is optional// If you think you don't need a custom interface// Then using registerNode is enough// Place your code that interacting with your library or Node.js API hereclassYourIFaceNameextendsBlackprint.Interface {constructor(node){super(node);this.yourCustomProperty ='...'; }clicked(ev){console.log("Engine: 'Trigger' button clicked, going to run the handler");// Event route: iface.clicked -> node.clicked -> node.outputs.Clickedthis.node.clicked &&this.node.clicked(ev); }};Blackprint.registerInterface('BPIC/Nodes/Button', YourIFaceName);// Register custom HTML interface for sketchBlackprint.Sketch.Interface('BPIC/Nodes/Button', { html:` <div class="node {{ type || 'general' }}" style="transform: translate({{ x }}px, {{ y }}px)"> <sf-template path="Blackprint/nodes/template/header.sf"></sf-template> <div class="content"> <div class="left-port"> <sf-template path="Blackprint/nodes/template/input-port.sf"></sf-template> </div> <div style="display: inline-block; color: yellow"> {{ yourCustomProperty }} </div> <div class="right-port"> <sf-template path="Blackprint/nodes/template/output-port.sf"></sf-template> </div> </div> </div>`}, YourIFaceName);// --- How to use it ---var sketch =newBlackprint.Sketch();let button =sketch.createNode('Example/Nodes/Button', {x:100, y:200});// button is instance of (class YourIFaceName extends Blackprint.Interface)button.clicked("'An event'"); // == iface.clicked(...)// --- Console output ---//> Engine: 'Trigger' button clicked, going to run the handler//> Example/Nodes/SimpleButton: got 'An event'Blackprint is registered on window object
Blackprint is registered on window object
If you're using TypeScript, you can also use Blackprint.registerInterface and Blackprint.registerNode as a decorator for your class.
This can be exact same with the browser version. You don't need to register Blackprint.Sketch.registerInterface because Node.js and Deno doesn't need to user Sketch Interface.
// For Denoimport { Engine } from'https://cdn.skypack.dev/@blackprint/engine@0.6';// For Node.jsconst { Engine } =require('@blackprint/engine');// Place your code that interacting with your library or Node.js API hereBlackprint.registerInterface('BPIC/Nodes/Button',classYourIFaceNameextendsBlackprint.Interface {constructor(node){super(node);this.yourCustomProperty ='...'; }clicked(ev){console.log("Engine: 'Trigger' button clicked, going to run the handler");// Event route: iface.clicked -> node.clicked -> node.outputs.Clickedthis.node.clicked &&this.node.clicked(ev); }});// Define your node structure and place your code to handle the interface here// With compatibility in mind (can be imported for Browser/Node.js)Blackprint.registerNode('Example/Nodes/Button',classYourNodeNameextendsBlackprint.Node {// Define output ports for this nodestatic outputs = { Clicked: Function // This will be connected to other node's input port if exist };// Define input ports for this node// static input = { ThePortName: DataTypeHere };constructor(instance){super(instance);// Let's use 'BPIC/Nodes/Button interface for this nodelet iface =this.setInterface('BPIC/Nodes/Button');iface.title ="My Button"; // Custom node title }// Triggered from 'BPIC/Nodes/Button' interfaceclicked(ev){console.log('Example/Nodes/Button: got', ev);node.outputs.Clicked(ev); }});// --- How to use it ---var instance =newEngine();var button =instance.createNode("Example/Nodes/Button");// button is instance of (class YourIFaceName extends Blackprint.Interface)button.clicked("'An event'"); // == iface.clicked(...)// --- Console output ---//> Engine: 'Trigger' button clicked, going to run the handler//> Example/Nodes/Button: got 'An event'
When registering with Namespace, the default root namespace is always "BPNode". Please name your nodes namespace along with your library name to avoid conflict with other library.
Entrypoint
require_once('../vendor/autoload.php');// This can be called on different PHP libraries\Blackprint\registerNamespace(__DIR__.'/BPNode');
Different file
// file: ./BPNode/Example/Nodes/Hello.phpnamespace\BPNode\Example\Nodes;use\Blackprint\{Engine,Types,};// The class name must match with the file name// This will be registered as Node definitionclassHelloextends\Blackprint\Node {function__construct($instance){// Call the parent constructor first, passing the $instance (Blackprint\Engine)parent::__construct($instance);// Set the Interface, let it empty if you want// to use default empty interface "setInterface()" $iface =$this->setInterface('BPIC/Nodes/Button'); $iface->title ="Button"; // Set the title for debugging// Please remember to capitalize the port name// Set the output port structure for your node (Optional)$this->output = ['Clicked'=>Blackprint\Types::Function, ];// Set the input port structure for your node (Optional)// $this->input = [ 'PortName' => TypeData ]; }// Proxy event object from: iface.clicked -> node.clicked -> outputs.Clickedclicked($ev){colorLog("Nodes/Hello: got $ev"); $node->outputs['Clicked']($ev); }}// Your Interface namespace must use "BPIC" as the prefix\Blackprint\registerInterface('BPIC\Nodes\Button',HelloIFace::class);classHelloIFaceextends\Blackprint\Interfaces {function__construct($node){// Call the parent constructor first, passing the $node (Blackprint\Node)parent::__construct($node);// $this->node => Blackprint\Node }clicked($ev){colorLog("Engine: 'Trigger' button clicked, going to run the handler");isset($iface->node->clicked)&& ($iface->node->clicked)($ev); }}$instance =newEngine;// --- How to use it ---$button = instance.createNode("Example/Nodes/Button");echo"\n\n>> I'm clicking the button";($button->clicked)("'An event'");// --- Console output ---//> Engine: 'Trigger' button clicked, going to run the handler//> Example/Nodes/Button: got 'An event'
# Work in progress
Before we get started, it would be better if you understand how we will implement the nodes.
There are 2 type of registration:
Below is the flow visualization and description:
Blackprint Engine will load JSON and create every Node from it by using the registered node (registerNode) in the engine. The created Node will just act like an object for storing data, validating, or do some data processing. The Node will search for it's interface assigned in iface.interface that was registered (registerInterface) on the engine, if it was undefined the default node interface will be used.
The IFace will bind itself with the Node, and both of them can interact from the object reference. Blackprint Engine will handle the data flow for every Node, and the custom IFace will handle the data events from both System or Node's data changes.
Maybe it's better to try with some example or experimenting if you still confused.
Alright, let's clone these repository.
$gitclone--depth1https://github.com/Blackprint/Blackprint.git.# Create symbolic link to Blackprint's dist folder# (Windows: may need administrator privileges)$mklink/D".\example\dist""..\dist"# For Windows# For linux$ln-sdist./example# Install the dependency from ./Blackprint/package.json$npmi
Starting the server and the compiler
$npmstart# The compiled engine-js and nodes will be placed on ./dist folder# You will also need to add your new custom node's .js, .css on /example/public/index.html
Modifying the code
You can modify /gulpfile.js for customize the compiler. Currently I'm figuring a better way to make the development more easier, so this may be changed.
The /example/public folder have default index.html for getting started, and your css and js should be written into /example/src or /src folder and your browser should being refreshed automatically.
The compiler already have file timestamp versioning to avoid browser cache, so you don't need to press CTRL+F5 every time you modify your code in /src.
Compiling the code
The compilation process will minify your code and also run Babel transpiler to support low end browser.