mirror of
https://github.com/fluxscape/fluxscape.git
synced 2026-01-11 14:52:54 +01:00
refactor(editor): useNodeReferences to React context (#64)
This commit is contained in:
@@ -0,0 +1,133 @@
|
|||||||
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
import { type ComponentModel } from '@noodl-models/componentmodel';
|
||||||
|
import { type NodeGraphNode } from '@noodl-models/nodegraphmodel';
|
||||||
|
import { type NodeLibraryNodeType } from '@noodl-models/nodelibrary';
|
||||||
|
import { type Slot } from '@noodl-core-ui/types/global';
|
||||||
|
import { ProjectModel } from '@noodl-models/projectmodel';
|
||||||
|
import { EventDispatcher } from '../../../../shared/utils/EventDispatcher';
|
||||||
|
|
||||||
|
export type NodeReference = {
|
||||||
|
type: NodeLibraryNodeType;
|
||||||
|
displayName: string;
|
||||||
|
referenaces: {
|
||||||
|
displayName: string;
|
||||||
|
node?: NodeGraphNode;
|
||||||
|
component: ComponentModel;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface NodeReferencesContext {
|
||||||
|
nodeReferences: NodeReference[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeReferencesContext = createContext<NodeReferencesContext>({
|
||||||
|
nodeReferences: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface NodeReferencesContextProps {
|
||||||
|
children: Slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NodeReferencesContextProvider({ children }: NodeReferencesContextProps) {
|
||||||
|
const [group] = useState({});
|
||||||
|
const [nodeReferences, setNodeReferences] = useState<NodeReference[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function updateIndex() {
|
||||||
|
const types: { [key: string]: NodeReference['type'] } = {};
|
||||||
|
const references = new Map<string, NodeReference['referenaces']>();
|
||||||
|
|
||||||
|
function handleComponent(component: ComponentModel) {
|
||||||
|
component.forEachNode((node: NodeGraphNode) => {
|
||||||
|
const name = node.type.name;
|
||||||
|
|
||||||
|
// Add the reference
|
||||||
|
references.set(name, [
|
||||||
|
...(references.get(name) || []),
|
||||||
|
{
|
||||||
|
displayName: component.displayName || component.name,
|
||||||
|
node,
|
||||||
|
component
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Repeater
|
||||||
|
if (name === 'For Each' && node.parameters.template) {
|
||||||
|
const templateComponent = ProjectModel.instance.getComponentWithName(node.parameters.template);
|
||||||
|
|
||||||
|
if (templateComponent) {
|
||||||
|
references.set(templateComponent.fullName, [
|
||||||
|
...(references.get(templateComponent.fullName) || []),
|
||||||
|
{
|
||||||
|
displayName: component.displayName || component.name,
|
||||||
|
node,
|
||||||
|
component
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
handleComponent(templateComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some metadata for this node if we dont have it yet.
|
||||||
|
if (!types[name]) {
|
||||||
|
types[name] = node.type;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop all the nodes in the project
|
||||||
|
ProjectModel.instance.forEachComponent(handleComponent);
|
||||||
|
|
||||||
|
// Combine the result to look a little better.
|
||||||
|
const results: NodeReference[] = Array.from(references.keys())
|
||||||
|
.map((key) => ({
|
||||||
|
type: types[key],
|
||||||
|
displayName: types[key]?.displayName || key,
|
||||||
|
referenaces: references.get(key)
|
||||||
|
}))
|
||||||
|
.sort((a, b) => b.referenaces.length - a.referenaces.length);
|
||||||
|
|
||||||
|
setNodeReferences(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIndex();
|
||||||
|
|
||||||
|
EventDispatcher.instance.on(
|
||||||
|
[
|
||||||
|
'Model.nodeAdded',
|
||||||
|
'Model.nodeRemoved',
|
||||||
|
'Model.componentAdded',
|
||||||
|
'Model.componentRemoved',
|
||||||
|
'Model.componentRenamed'
|
||||||
|
],
|
||||||
|
updateIndex,
|
||||||
|
group
|
||||||
|
);
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
EventDispatcher.instance.off(group);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NodeReferencesContext.Provider
|
||||||
|
value={{
|
||||||
|
nodeReferences,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</NodeReferencesContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useNodeReferencesContext() {
|
||||||
|
const context = useContext(NodeReferencesContext);
|
||||||
|
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useNodeReferencesContext must be a child of NodeReferencesContextProvider');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './NodeReferencesContext';
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import { NodeGraphContextProvider } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
import { NodeGraphContextProvider } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
||||||
|
import { NodeReferencesContextProvider } from '@noodl-contexts/NodeReferencesContext';
|
||||||
|
import { PluginContextProvider } from '@noodl-contexts/PluginContext';
|
||||||
import { ProjectDesignTokenContextProvider } from '@noodl-contexts/ProjectDesignTokenContext';
|
import { ProjectDesignTokenContextProvider } from '@noodl-contexts/ProjectDesignTokenContext';
|
||||||
import { useKeyboardCommands } from '@noodl-hooks/useKeyboardCommands';
|
import { useKeyboardCommands } from '@noodl-hooks/useKeyboardCommands';
|
||||||
import { useModel } from '@noodl-hooks/useModel';
|
import { useModel } from '@noodl-hooks/useModel';
|
||||||
@@ -43,7 +45,6 @@ import { BaseWindow } from '../../views/windows/BaseWindow';
|
|||||||
import { whatsnewRender } from '../../whats-new';
|
import { whatsnewRender } from '../../whats-new';
|
||||||
import { IRouteProps } from '../AppRoute';
|
import { IRouteProps } from '../AppRoute';
|
||||||
import { useSetupSettings } from './useSetupSettings';
|
import { useSetupSettings } from './useSetupSettings';
|
||||||
import { PluginContextProvider } from '@noodl-contexts/PluginContext';
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const ImportOverwritePopupTemplate = require('../../templates/importoverwritepopup.html');
|
const ImportOverwritePopupTemplate = require('../../templates/importoverwritepopup.html');
|
||||||
@@ -223,6 +224,7 @@ export function EditorPage({ route }: EditorPageProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeGraphContextProvider>
|
<NodeGraphContextProvider>
|
||||||
|
<NodeReferencesContextProvider>
|
||||||
<ProjectDesignTokenContextProvider>
|
<ProjectDesignTokenContextProvider>
|
||||||
<PluginContextProvider>
|
<PluginContextProvider>
|
||||||
<BaseWindow>
|
<BaseWindow>
|
||||||
@@ -245,6 +247,7 @@ export function EditorPage({ route }: EditorPageProps) {
|
|||||||
</BaseWindow>
|
</BaseWindow>
|
||||||
</PluginContextProvider>
|
</PluginContextProvider>
|
||||||
</ProjectDesignTokenContextProvider>
|
</ProjectDesignTokenContextProvider>
|
||||||
|
</NodeReferencesContextProvider>
|
||||||
</NodeGraphContextProvider>
|
</NodeGraphContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { NodeGraphContextTmp } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
import { NodeGraphContextTmp } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
||||||
|
import { type NodeReference, useNodeReferencesContext } from '@noodl-contexts/NodeReferencesContext';
|
||||||
import { useFocusRefOnPanelActive } from '@noodl-hooks/useFocusRefOnPanelActive';
|
import { useFocusRefOnPanelActive } from '@noodl-hooks/useFocusRefOnPanelActive';
|
||||||
import { useNodeLibraryLoaded } from '@noodl-hooks/useNodeLibraryLoaded';
|
import { useNodeLibraryLoaded } from '@noodl-hooks/useNodeLibraryLoaded';
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { INodeColorScheme } from '@noodl-types/nodeTypes';
|
import { INodeColorScheme } from '@noodl-types/nodeTypes';
|
||||||
import { ComponentModel } from '@noodl-models/componentmodel';
|
import { NodeLibrary } from '@noodl-models/nodelibrary';
|
||||||
import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
|
|
||||||
import { NodeLibrary, NodeLibraryNodeType } from '@noodl-models/nodelibrary';
|
|
||||||
import { BasicNodeType } from '@noodl-models/nodelibrary/BasicNodeType';
|
import { BasicNodeType } from '@noodl-models/nodelibrary/BasicNodeType';
|
||||||
import { ProjectModel } from '@noodl-models/projectmodel';
|
|
||||||
|
|
||||||
import { EditorNode } from '@noodl-core-ui/components/common/EditorNode';
|
import { EditorNode } from '@noodl-core-ui/components/common/EditorNode';
|
||||||
import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
|
import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
|
||||||
@@ -26,113 +24,17 @@ import { Section, SectionVariant } from '@noodl-core-ui/components/sidebar/Secti
|
|||||||
import { Label } from '@noodl-core-ui/components/typography/Label';
|
import { Label } from '@noodl-core-ui/components/typography/Label';
|
||||||
|
|
||||||
import { NodeReferencesPanel_ID } from '.';
|
import { NodeReferencesPanel_ID } from '.';
|
||||||
import { EventDispatcher } from '../../../../../shared/utils/EventDispatcher';
|
|
||||||
|
|
||||||
type ResultItem = {
|
|
||||||
type: NodeLibraryNodeType;
|
|
||||||
displayName: string;
|
|
||||||
referenaces: {
|
|
||||||
displayName: string;
|
|
||||||
node?: NodeGraphNode;
|
|
||||||
component: ComponentModel;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
function useNodeReferences() {
|
|
||||||
const [group] = useState({});
|
|
||||||
const [result, setResult] = useState<ResultItem[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function updateIndex() {
|
|
||||||
const types: { [key: string]: ResultItem['type'] } = {};
|
|
||||||
const references = new Map<string, ResultItem['referenaces']>();
|
|
||||||
|
|
||||||
function handleComponent(component: ComponentModel) {
|
|
||||||
component.forEachNode((node: NodeGraphNode) => {
|
|
||||||
const name = node.type.name;
|
|
||||||
|
|
||||||
// Add the reference
|
|
||||||
references.set(name, [
|
|
||||||
...(references.get(name) || []),
|
|
||||||
{
|
|
||||||
displayName: component.displayName || component.name,
|
|
||||||
node,
|
|
||||||
component
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Repeater
|
|
||||||
if (name === 'For Each' && node.parameters.template) {
|
|
||||||
const templateComponent = ProjectModel.instance.getComponentWithName(node.parameters.template);
|
|
||||||
|
|
||||||
if (templateComponent) {
|
|
||||||
references.set(templateComponent.fullName, [
|
|
||||||
...(references.get(templateComponent.fullName) || []),
|
|
||||||
{
|
|
||||||
displayName: component.displayName || component.name,
|
|
||||||
node,
|
|
||||||
component
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
handleComponent(templateComponent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add some metadata for this node if we dont have it yet.
|
|
||||||
if (!types[name]) {
|
|
||||||
types[name] = node.type;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop all the nodes in the project
|
|
||||||
ProjectModel.instance.forEachComponent(handleComponent);
|
|
||||||
|
|
||||||
// Combine the result to look a little better.
|
|
||||||
const results: ResultItem[] = Array.from(references.keys())
|
|
||||||
.map((key) => ({
|
|
||||||
type: types[key],
|
|
||||||
displayName: types[key]?.displayName || key,
|
|
||||||
referenaces: references.get(key)
|
|
||||||
}))
|
|
||||||
.sort((a, b) => b.referenaces.length - a.referenaces.length);
|
|
||||||
|
|
||||||
setResult(results);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateIndex();
|
|
||||||
|
|
||||||
EventDispatcher.instance.on(
|
|
||||||
[
|
|
||||||
'Model.nodeAdded',
|
|
||||||
'Model.nodeRemoved',
|
|
||||||
'Model.componentAdded',
|
|
||||||
'Model.componentRemoved',
|
|
||||||
'Model.componentRenamed'
|
|
||||||
],
|
|
||||||
updateIndex,
|
|
||||||
group
|
|
||||||
);
|
|
||||||
|
|
||||||
return function () {
|
|
||||||
EventDispatcher.instance.off(group);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return [result];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NodeReferencesPanel() {
|
export function NodeReferencesPanel() {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [includeCoreNodes, setIncludeCoreNodes] = useState(false);
|
const [includeCoreNodes, setIncludeCoreNodes] = useState(false);
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const [result] = useNodeReferences();
|
const { nodeReferences } = useNodeReferencesContext();
|
||||||
const nodeLibraryLoaded = useNodeLibraryLoaded();
|
const nodeLibraryLoaded = useNodeLibraryLoaded();
|
||||||
|
|
||||||
useFocusRefOnPanelActive(inputRef, NodeReferencesPanel_ID);
|
useFocusRefOnPanelActive(inputRef, NodeReferencesPanel_ID);
|
||||||
|
|
||||||
function searchFilter(x: ResultItem) {
|
function searchFilter(x: NodeReference) {
|
||||||
if (x.displayName.toLowerCase().includes(searchTerm)) {
|
if (x.displayName.toLowerCase().includes(searchTerm)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -144,7 +46,7 @@ export function NodeReferencesPanel() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let filteredResult = result.filter(searchFilter);
|
let filteredResult = nodeReferences.filter(searchFilter);
|
||||||
if (!includeCoreNodes) {
|
if (!includeCoreNodes) {
|
||||||
filteredResult = filteredResult.filter((x) => x.displayName.startsWith('/'));
|
filteredResult = filteredResult.filter((x) => x.displayName.startsWith('/'));
|
||||||
}
|
}
|
||||||
@@ -185,7 +87,7 @@ export function NodeReferencesPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ItemProps {
|
interface ItemProps {
|
||||||
entry: ResultItem;
|
entry: NodeReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Item({ entry }: ItemProps) {
|
function Item({ entry }: ItemProps) {
|
||||||
@@ -245,8 +147,8 @@ function Item({ entry }: ItemProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ItemReferenceProps {
|
interface ItemReferenceProps {
|
||||||
entry: ResultItem;
|
entry: NodeReference;
|
||||||
referenace: ResultItem['referenaces'][0];
|
referenace: NodeReference['referenaces'][0];
|
||||||
colors: INodeColorScheme;
|
colors: INodeColorScheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user