Skip to content

Commit

Permalink
Rename some things (#23)
Browse files Browse the repository at this point in the history
* Rename some things

* Try and document

* inputType as inputEditor

* minor name fix in example

* Restrict input types to that get rendered to HTML to not generate HTML errors

* Change theme to kind

* Revert "Restrict input types to that get rendered to HTML to not generate HTML errors"

This reverts commit 1a7d05f.
  • Loading branch information
sroussey authored Feb 8, 2024
1 parent 1a9f712 commit 64b74a2
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 145 deletions.
54 changes: 54 additions & 0 deletions DOCUMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Documentation

```mermaid
erDiagram
NodeType ||--o{ NodeInput : has
NodeType ||--o{ NodeOutput : has
NodeType ||--o{ NodeKind : has
NodeInput ||--|| ValueType : uses
NodeOutput ||--|| ValueType : uses
ValueType ||..|| InputEditor : uses
NodeInput{
string name
string id
ValueType valueType
boolean isArray
boolean isConstant
any defaultValue
string inputGroup
}
NodeOutput{
string name
string id
ValueType valueType
boolean isArray
}
NodeKind{
string name
string color
}
NodeType{
string name
string id
string description
NodeInput[] inputs
NodeOutput[] outputs
NodeKind kind
}
ValueType{
string name
string color
string inputEditor
any defaultValue
}
```

## NodeType

A node type describes a kind of node that can put in a graph. It has a name and a list of inputs and outputs that have handles to connect to other nodes via edges.

## NodeInput

A node input is a handle that can be connected to a node output via an edge.

14 changes: 7 additions & 7 deletions lib/components/NodeContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { NodeHeader } from './NodeHeader'
import {
GraphConfig,
NodeConfig,
NodeGroupConfig,
NodeKindConfig,
NodeInputConfig,
NodeOutputConfig,
} from '../config'
Expand All @@ -27,15 +27,15 @@ export function NodeContainer({
}: NodeContainerProps) {
const config = useGraphStore((store) => store.config)
const nodeConfig = config.getNodeConfig(node.type!)!
const nodeGroupConfig = config.getNodeGroupConfig(nodeConfig.group)
const nodeKindConfig = config.getNodeKindConfig(nodeConfig.kind)
const [collapsed, toggleCollapsed] = useNodeCollapsed()

if (collapsed) {
return (
<CollapsedNodeContainer
node={node}
nodeConfig={nodeConfig}
nodeGroupConfig={nodeGroupConfig}
nodeKindConfig={nodeKindConfig}
toggleCollapsed={toggleCollapsed}
/>
)
Expand All @@ -54,7 +54,7 @@ export function NodeContainer({
>
<NodeHeader
defaultTitle={nodeConfig.name}
color={nodeGroupConfig.color}
color={nodeKindConfig.color}
collapsed={false}
toggleCollapsed={toggleCollapsed}
/>
Expand All @@ -67,14 +67,14 @@ export function NodeContainer({
type CollapsedNodeContainerProps = {
node: Node
nodeConfig: NodeConfig
nodeGroupConfig: NodeGroupConfig
nodeKindConfig: NodeKindConfig
toggleCollapsed?: () => void
}

function CollapsedNodeContainer({
node,
nodeConfig,
nodeGroupConfig,
nodeKindConfig,
toggleCollapsed,
}: CollapsedNodeContainerProps) {
const config = useGraphStore((store) => store.config)
Expand All @@ -100,7 +100,7 @@ function CollapsedNodeContainer({
>
<NodeHeader
defaultTitle={nodeConfig.name}
color={nodeGroupConfig.color}
color={nodeKindConfig.color}
collapsed={true}
toggleCollapsed={toggleCollapsed}
/>
Expand Down
100 changes: 48 additions & 52 deletions lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ export interface IGraphConfig {
valueTypes: ValueTypes

/**
* Groups of nodes that can be used to organize the node palette. Also allows styling
* Groups of nodes that can be used to organize the node palette, allowing styling
* and configuring the colors of the nodes as a group.
*/
nodeGroups: NodeGroupTypes
nodeKinds: NodeKindTypes

/**
* The nodes types that are registered and can be created in the graph
*/
nodes: NodeTypes
nodeTypes: NodeTypes
}

export type KeyBindings = {
Expand All @@ -41,8 +41,8 @@ export type ValueTypes = {
[key: string]: ValueTypeConfig
}

export type NodeGroupTypes = {
[key: string]: NodeGroupConfig
export type NodeKindTypes = {
[name: string]: NodeKindConfig
}

export type NodeTypes = {
Expand All @@ -67,27 +67,27 @@ interface ValueTypeConfigBase {
}

export interface ValueTypeConfigOptions extends ValueTypeConfigBase {
inputType: 'options' | 'buttonGroup'
inputEditor: 'options' | 'buttonGroup'
options: Option[]
defaultValue: Option['value'] // Ensures defaultValue is one of the option values
}

interface ValueTypeConfigValue extends ValueTypeConfigBase {
inputType: 'value'
inputEditor: 'value'
defaultValue: string // Ensures defaultValue is a string
}

interface ValueTypeConfigCheckbox extends ValueTypeConfigBase {
inputType: 'checkbox'
inputEditor: 'checkbox'
defaultValue: boolean // Ensures defaultValue is a boolean
}

interface ValueTypeConfigCustom extends ValueTypeConfigBase {
inputType: string
inputEditor: string
}

interface ValueTypeConfigNoInput extends ValueTypeConfigBase {
inputType: null | undefined
inputEditor: null | undefined
}

export type ValueTypeConfig =
Expand All @@ -102,13 +102,13 @@ export interface Option {
value: string
}

export interface NodeGroupConfig {
export interface NodeKindConfig {
name: string
color: string
}

export interface NodeConfig {
group: keyof IGraphConfig['nodeGroups']
kind: keyof IGraphConfig['nodeKinds']
name: string
inputs?: NodeInputConfig[]
outputs?: NodeOutputConfig[]
Expand Down Expand Up @@ -136,7 +136,7 @@ export interface NodeInputConfig {
* any other inputs with this group name will be rendered under a collapsable
* accordion.
*/
group?: string
inputGroup?: string
}

export interface NodeOutputConfig {
Expand All @@ -149,19 +149,21 @@ export interface NodeOutputConfig {
function isValueTypeConfigOptions(
config: ValueTypeConfig,
): config is ValueTypeConfigOptions {
return config.inputType === 'options' || config.inputType === 'buttonGroup'
return (
config.inputEditor === 'options' || config.inputEditor === 'buttonGroup'
)
}

function isValueTypeConfigValue(
config: ValueTypeConfig,
): config is ValueTypeConfigValue {
return config.inputType === 'value'
return config.inputEditor === 'value'
}

function isValueTypeConfigCheckbox(
config: ValueTypeConfig,
): config is ValueTypeConfigCheckbox {
return config.inputType === 'checkbox'
return config.inputEditor === 'checkbox'
}

type WithType<T, K> = T & {
Expand All @@ -173,10 +175,10 @@ export type InputProps = BaseInputProps & NodeInputConfig & ValueTypeConfig
export class GraphConfig {
readonly valueTypes: ValueTypes = {}
readonly keybindings: KeyBindings
readonly nodeGroups: {
[key: string]: NodeGroupConfig
readonly nodeKinds: {
[key: string]: NodeKindConfig
} = {}
private readonly nodes: {
private readonly nodeTypes: {
[key: string]: NodeConfig
} = {}
private customNodes: {
Expand All @@ -198,24 +200,18 @@ export class GraphConfig {
this.valueTypes[ANY] = {
name: 'Any',
color: '#a1a1a1',
inputType: null,
inputEditor: null,
}
this.nodeGroups = props?.nodeGroups ?? this.nodeGroups
this.nodes = props?.nodes ?? this.nodes
this.nodeKinds = props?.nodeKinds ?? this.nodeKinds
this.nodeTypes = props?.nodeTypes ?? this.nodeTypes
for (const [key, value] of Object.entries(getBuiltinInputs())) {
this.inputs[key] = value
}
}

validate(): GraphConfig {
const errors: string[] = []
Object.values(this.nodes).forEach((node) => {
const groups = Object.keys(this.nodeGroups)
if (!this.nodeGroups[node.group]) {
errors.push(
`Node '${node.name}' belongs to a node group that does not exist: '${node.group}'. Available groups: ${groups.join(', ')}`,
)
}
Object.values(this.nodeTypes).forEach((node) => {
if (node.inputs) {
node.inputs.forEach((input) => {
if (!this.valueTypes[input.valueType]) {
Expand Down Expand Up @@ -244,14 +240,14 @@ export class GraphConfig {
registerCustomNode<T>(
name: string,
type: string,
group: string,
kind: string,
node: JSXElementConstructor<T>,
inputs: NodeInputConfig[],
outputs: NodeOutputConfig[],
) {
this.customNodes[type] = node
this.nodes[type] = {
group,
this.nodeTypes[type] = {
kind,
name,
inputs: inputs,
outputs: outputs,
Expand All @@ -263,12 +259,12 @@ export class GraphConfig {
registerInput(
type: string,
input: JSXElementConstructor<InputProps>,
valueType: Omit<ValueTypeConfig, 'inputType'>,
valueType: Omit<ValueTypeConfig, 'inputEditor'>,
) {
this.inputs[type] = input
this.valueTypes[type] = {
...valueType,
inputType: type,
inputEditor: type,
}
this.validate()
}
Expand All @@ -285,50 +281,50 @@ export class GraphConfig {
getInputComponent(
valueType: string,
): JSXElementConstructor<InputProps & { slots?: InputSlots }> | null {
const inputType = this.valueTypes[valueType]?.inputType
if (inputType == null) return null
return this.inputs[inputType]
const inputEditor = this.valueTypes[valueType]?.inputEditor
if (inputEditor == null) return null
return this.inputs[inputEditor]
}

nodeConfigs(): WithType<NodeConfig, string>[] {
return Object.entries(this.nodes).map(([type, value]) => ({
return Object.entries(this.nodeTypes).map(([type, value]) => ({
...value,
type,
}))
}

getNodeConfig(type: string): NodeConfig | null {
return this.nodes[type]
getNodeConfig(type: string): NodeConfig {
return this.nodeTypes[type]
}

nodeConfigsByGroup(group: string): WithType<NodeConfig, string>[] {
return Object.entries(this.nodes)
nodeConfigsByKind(kind: string): WithType<NodeConfig, string>[] {
return Object.entries(this.nodeTypes)
.map(([type, n]) => ({ type, ...n }))
.filter((n) => n.group === group)
.filter((n) => n.kind === kind)
}

nodeGroupConfigs(): WithType<NodeGroupConfig, string>[] {
return Object.entries(this.nodeGroups).map(([type, value]) => ({
nodeKindConfigs(): WithType<NodeKindConfig, string>[] {
return Object.entries(this.nodeKinds).map(([type, value]) => ({
...value,
type,
}))
}

getRegisteredNodeTypes() {
return Object.entries(this.nodeGroups).map(([type, group]) => ({
return Object.entries(this.nodeKinds).map(([type, kind]) => ({
type,
name: group.name,
nodes: this.nodeConfigsByGroup(type).map((node) => ({
name: kind.name,
nodes: this.nodeConfigsByKind(type).map((node) => ({
type: node.type,
name: node.name,
})),
}))
}

getNodeGroupConfig<T extends keyof this['nodeGroups']>(
getNodeKindConfig<T extends keyof this['nodeKinds']>(
nodeType: T,
): NodeGroupConfig {
return this.nodeGroups[nodeType as keyof NodeGroupTypes]
): NodeKindConfig {
return this.nodeKinds[nodeType as keyof NodeKindTypes]
}

valueType<T extends keyof this['valueTypes']>(type: T): ValueTypeConfig {
Expand Down Expand Up @@ -370,7 +366,7 @@ export class GraphConfig {
node: NodeConfig,
) => JSXElementConstructor<any>,
): Record<string, JSXElementConstructor<any>> {
return Object.entries(this.nodes)
return Object.entries(this.nodeTypes)
.map(([type, node]): [string, JSXElementConstructor<any>] => {
if (node.custom) {
return [type, this.customNode(type)]
Expand Down
Loading

0 comments on commit 64b74a2

Please sign in to comment.