فهرست منبع

feat(free-demo): register node panel plugin & node panel component

liuyangxing 10 ماه پیش
والد
کامیت
6a39245cc7

+ 48 - 0
apps/demo-free-layout/src/components/node-panel/index.tsx

@@ -0,0 +1,48 @@
+import { FC } from 'react';
+
+import { NodePanelRenderProps } from '@flowgram.ai/free-node-panel-plugin';
+import { Popover } from '@douyinfe/semi-ui';
+
+import { NodePlaceholder } from './node-placeholder';
+import { NodeList } from './node-list';
+
+export const NodePanel: FC<NodePanelRenderProps> = (props) => {
+  const { onSelect, position, onClose, panelProps } = props;
+  const { enableNodePlaceholder } = panelProps;
+
+  return (
+    <Popover
+      trigger="click"
+      visible={true}
+      onVisibleChange={(v) => (v ? null : onClose())}
+      content={<NodeList onSelect={onSelect} />}
+      placement="right"
+      popupAlign={{ offset: [30, 0] }}
+      overlayStyle={{
+        padding: 0,
+      }}
+    >
+      <div
+        style={
+          enableNodePlaceholder
+            ? {
+                position: 'absolute',
+                top: position.y - 61.5,
+                left: position.x,
+                width: 300,
+                height: 123,
+              }
+            : {
+                position: 'absolute',
+                top: position.y,
+                left: position.x,
+                width: 0,
+                height: 0,
+              }
+        }
+      >
+        {enableNodePlaceholder && <NodePlaceholder />}
+      </div>
+    </Popover>
+  );
+};

+ 83 - 0
apps/demo-free-layout/src/components/node-panel/node-list.tsx

@@ -0,0 +1,83 @@
+import React, { FC } from 'react';
+
+import styled from 'styled-components';
+import { NodePanelRenderProps } from '@flowgram.ai/free-node-panel-plugin';
+import { useClientContext } from '@flowgram.ai/free-layout-editor';
+
+import { FlowNodeRegistry } from '../../typings';
+import { nodeRegistries } from '../../nodes';
+
+const NodeWrap = styled.div`
+  width: 100%;
+  height: 32px;
+  border-radius: 5px;
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  font-size: 19px;
+  padding: 0 15px;
+  &:hover {
+    background-color: hsl(252deg 62% 55% / 9%);
+    color: hsl(252 62% 54.9%);
+  },
+`;
+
+const NodeLabel = styled.div`
+  font-size: 12px;
+  margin-left: 10px;
+`;
+
+interface NodeProps {
+  label: string;
+  icon: JSX.Element;
+  onClick: React.MouseEventHandler<HTMLDivElement>;
+  disabled: boolean;
+}
+
+function Node(props: NodeProps) {
+  return (
+    <NodeWrap
+      onClick={props.disabled ? undefined : props.onClick}
+      style={props.disabled ? { opacity: 0.3 } : {}}
+    >
+      <div style={{ fontSize: 14 }}>{props.icon}</div>
+      <NodeLabel>{props.label}</NodeLabel>
+    </NodeWrap>
+  );
+}
+
+const NodesWrap = styled.div`
+  max-height: 500px;
+  overflow: auto;
+  &::-webkit-scrollbar {
+    display: none;
+  }
+`;
+
+interface NodeListProps {
+  onSelect: NodePanelRenderProps['onSelect'];
+}
+
+export const NodeList: FC<NodeListProps> = (props) => {
+  const { onSelect } = props;
+  const context = useClientContext();
+  const handleClick = (e: React.MouseEvent, registry: FlowNodeRegistry) => {
+    onSelect({
+      nodeType: registry.type as string,
+      selectEvent: e,
+    });
+  };
+  return (
+    <NodesWrap style={{ width: 80 * 2 + 20 }}>
+      {nodeRegistries.map((registry) => (
+        <Node
+          key={registry.type}
+          disabled={!(registry.canAdd?.(context) ?? true)}
+          icon={<img style={{ width: 10, height: 10, borderRadius: 4 }} src={registry.info.icon} />}
+          label={registry.type as string}
+          onClick={(e) => handleClick(e, registry)}
+        />
+      ))}
+    </NodesWrap>
+  );
+};

+ 48 - 0
apps/demo-free-layout/src/components/node-panel/node-placeholder.tsx

@@ -0,0 +1,48 @@
+export const NodePlaceholder = () => (
+  <svg width="300" height="123" viewBox="0 0 300 123" xmlns="http://www.w3.org/2000/svg">
+    <g id="g1">
+      <path
+        id="path1"
+        fill="#ffffff"
+        stroke="none"
+        d="M 0 13.795815 C 0 6.856102 5.625739 1.23037 12.565445 1.23037 L 287.43454 1.23037 C 294.37381 1.23037 300 6.856102 300 13.795815 L 300 109.60733 C 300 116.547066 294.37381 122.172775 287.43454 122.172775 L 12.565445 122.172775 C 5.625754 122.172775 0 116.547066 0 109.60733 L 0 13.795815 Z"
+      />
+      <path
+        id="path2"
+        fill="none"
+        stroke="#060709"
+        strokeWidth="1.570681"
+        strokeOpacity="0.1"
+        d="M 0.78534 13.795815 C 0.78534 7.289825 6.059466 2.015709 12.565445 2.015709 L 287.43454 2.015709 C 293.940308 2.015709 299.214661 7.289825 299.214661 13.795815 L 299.214661 109.60733 C 299.214661 116.113243 293.940308 121.387436 287.43454 121.387436 L 12.565445 121.387436 C 6.059482 121.387436 0.78534 116.113243 0.78534 109.60733 L 0.78534 13.795815 Z"
+      />
+      <path
+        id="path3"
+        fill="#d9d9d9"
+        stroke="none"
+        opacity="0.32"
+        d="M 51.832462 70.340317 C 51.832462 66.003036 55.348591 62.486912 59.685863 62.486912 L 273.298431 62.486912 C 277.635071 62.486912 281.151825 66.003036 281.151825 70.340317 C 281.151825 74.677589 277.635071 78.193718 273.298431 78.193718 L 59.685863 78.193718 C 55.348591 78.193718 51.832462 74.677589 51.832462 70.340317 Z"
+      />
+      <path
+        id="path4"
+        fill="#d9d9d9"
+        stroke="none"
+        opacity="0.32"
+        d="M 51.832462 29.502617 C 51.832462 25.165344 55.348591 21.649216 59.685863 21.649216 L 138.219894 21.649216 C 142.557175 21.649216 146.073303 25.165344 146.073303 29.502617 C 146.073303 33.83989 142.557175 37.356018 138.219894 37.356018 L 59.685863 37.356018 C 55.348591 37.356018 51.832462 33.83989 51.832462 29.502617 Z"
+      />
+      <path
+        id="path5"
+        fill="#d9d9d9"
+        stroke="none"
+        opacity="0.32"
+        d="M 14.136126 29.502617 C 14.136126 25.165344 17.65225 21.649216 21.989529 21.649216 L 36.125656 21.649216 C 40.462933 21.649216 43.979057 25.165344 43.979057 29.502617 C 43.979057 33.83989 40.462933 37.356018 36.125656 37.356018 L 21.989529 37.356018 C 17.65225 37.356018 14.136126 33.83989 14.136126 29.502617 Z"
+      />
+      <path
+        id="path6"
+        fill="#d9d9d9"
+        stroke="none"
+        opacity="0.32"
+        d="M 51.832462 93.900528 C 51.832462 89.563248 55.348591 86.047119 59.685863 86.047119 L 193.19371 86.047119 C 197.530365 86.047119 201.047119 89.563248 201.047119 93.900528 C 201.047119 98.237801 197.530365 101.753929 193.19371 101.753929 L 59.685863 101.753929 C 55.348591 101.753929 51.832462 98.237801 51.832462 93.900528 Z"
+      />
+    </g>
+  </svg>
+);

+ 5 - 1
apps/demo-free-layout/src/hooks/use-editor-props.tsx

@@ -3,6 +3,7 @@ import { useMemo } from 'react';
 
 import { createMinimapPlugin } from '@flowgram.ai/minimap-plugin';
 import { createFreeSnapPlugin } from '@flowgram.ai/free-snap-plugin';
+import { createFreeNodePanelPlugin } from '@flowgram.ai/free-node-panel-plugin';
 import { FreeLayoutProps } from '@flowgram.ai/free-layout-editor';
 
 import { FlowNodeRegistry, FlowDocumentJSON } from '../typings';
@@ -10,7 +11,7 @@ import { shortcuts } from '../shortcuts';
 import { createVariablePlugin } from '../plugins';
 import { defaultFormMeta } from '../nodes/default-form-meta';
 import { SelectorBoxPopover } from '../components/selector-box-popover';
-import { BaseNode } from '../components';
+import { BaseNode, NodePanel } from '../components';
 
 export function useEditorProps(
   initialData: FlowDocumentJSON,
@@ -164,6 +165,9 @@ export function useEditorProps(
           alignLineWidth: 1,
           alignCrossWidth: 8,
         }),
+        createFreeNodePanelPlugin({
+          renderer: NodePanel,
+        }),
       ],
     }),
     []