Browse Source

feat(material): inject materials like VariableSelector, TypeSelector (#665)

* feat(material): inject material

* feat: update docs

* fix: file path

* fix: i18n text

* fix: remove useless code
Yiwei Mao 5 months ago
parent
commit
8a373f77bb

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

@@ -192,6 +192,7 @@ export function useEditorProps(
         enableScrollLimit: false,
       },
       materials: {
+        components: {},
         /**
          * Render Node
          */

+ 4 - 4
packages/materials/form-materials/src/components/assign-row/index.tsx

@@ -9,8 +9,8 @@ import { IconButton } from '@douyinfe/semi-ui';
 import { IconMinus } from '@douyinfe/semi-icons';
 
 import { IFlowConstantRefValue } from '@/typings';
-import { VariableSelector } from '@/components/variable-selector';
-import { DynamicValueInput } from '@/components/dynamic-value-input';
+import { InjectVariableSelector } from '@/components/variable-selector';
+import { InjectDynamicValueInput } from '@/components/dynamic-value-input';
 
 import { AssignRowProps } from './types';
 import { BlurInput } from './components/blur-input';
@@ -29,7 +29,7 @@ export function AssignRow(props: AssignRowProps) {
     <div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
       <div style={{ width: 150, minWidth: 150, maxWidth: 150 }}>
         {value?.operator === 'assign' ? (
-          <VariableSelector
+          <InjectVariableSelector
             style={{ width: '100%', height: 26 }}
             value={value?.left?.content}
             config={{ placeholder: 'Select Left' }}
@@ -56,7 +56,7 @@ export function AssignRow(props: AssignRowProps) {
         )}
       </div>
       <div style={{ flexGrow: 1 }}>
-        <DynamicValueInput
+        <InjectDynamicValueInput
           readonly={readonly}
           value={value?.right as IFlowConstantRefValue | undefined}
           onChange={(v) =>

+ 2 - 2
packages/materials/form-materials/src/components/batch-outputs/index.tsx

@@ -9,7 +9,7 @@ import { Button, Input } from '@douyinfe/semi-ui';
 import { IconDelete, IconPlus } from '@douyinfe/semi-icons';
 
 import { useObjectList } from '@/hooks';
-import { VariableSelector } from '@/components/variable-selector';
+import { InjectVariableSelector } from '@/components/variable-selector';
 
 import { PropsType } from './types';
 import { UIRow, UIRows } from './styles';
@@ -31,7 +31,7 @@ export function BatchOutputs(props: PropsType) {
               value={item.key}
               onChange={(v) => updateKey(item.id, v)}
             />
-            <VariableSelector
+            <InjectVariableSelector
               style={{ flexGrow: 1 }}
               readonly={readonly}
               value={item.value?.content}

+ 4 - 4
packages/materials/form-materials/src/components/condition-row/index.tsx

@@ -8,8 +8,8 @@ import React, { useMemo } from 'react';
 import { I18n } from '@flowgram.ai/editor';
 import { Input } from '@douyinfe/semi-ui';
 
-import { VariableSelector } from '@/components/variable-selector';
-import { DynamicValueInput } from '@/components/dynamic-value-input';
+import { InjectVariableSelector } from '@/components/variable-selector';
+import { InjectDynamicValueInput } from '@/components/dynamic-value-input';
 
 import { ConditionRowValueType, IRules, OpConfigs } from './types';
 import { UIContainer, UILeft, UIOperator, UIRight, UIValues } from './styles';
@@ -59,7 +59,7 @@ export function ConditionRow({
       <UIOperator>{renderOpSelect()}</UIOperator>
       <UIValues>
         <UILeft>
-          <VariableSelector
+          <InjectVariableSelector
             readonly={readonly}
             style={{ width: '100%' }}
             value={left?.content}
@@ -76,7 +76,7 @@ export function ConditionRow({
         </UILeft>
         <UIRight>
           {targetSchema ? (
-            <DynamicValueInput
+            <InjectDynamicValueInput
               readonly={readonly || !rule}
               value={right}
               schema={targetSchema}

+ 8 - 4
packages/materials/form-materials/src/components/dynamic-value-input/index.tsx

@@ -10,7 +10,8 @@ import { IconButton } from '@douyinfe/semi-ui';
 import { IconSetting } from '@douyinfe/semi-icons';
 
 import { IFlowConstantRefValue } from '@/typings/flow-value';
-import { VariableSelector } from '@/components/variable-selector';
+import { createInjectMaterial } from '@/shared';
+import { InjectVariableSelector } from '@/components/variable-selector';
 import { TypeSelector } from '@/components/type-selector';
 import { ConstantInput, ConstantInputStrategy } from '@/components/constant-input';
 
@@ -88,7 +89,7 @@ export function DynamicValueInput({
     if (value?.type === 'ref') {
       // Display Variable Or Delete
       return (
-        <VariableSelector
+        <InjectVariableSelector
           style={{ width: '100%' }}
           value={value?.content}
           onChange={(_v) => onChange(_v ? { type: 'ref', content: _v } : undefined)}
@@ -108,7 +109,7 @@ export function DynamicValueInput({
         readonly={readonly}
         strategies={[...(constantProps?.strategies || [])]}
         fallbackRenderer={() => (
-          <VariableSelector
+          <InjectVariableSelector
             style={{ width: '100%' }}
             onChange={(_v) => onChange(_v ? { type: 'ref', content: _v } : undefined)}
             includeSchema={includeSchema}
@@ -121,7 +122,7 @@ export function DynamicValueInput({
   };
 
   const renderTrigger = () => (
-    <VariableSelector
+    <InjectVariableSelector
       style={{ width: '100%' }}
       value={value?.type === 'ref' ? value?.content : undefined}
       onChange={(_v) => onChange({ type: 'ref', content: _v })}
@@ -141,3 +142,6 @@ export function DynamicValueInput({
     </UIContainer>
   );
 }
+
+DynamicValueInput.renderKey = 'dynamic-value-input-render-key';
+export const InjectDynamicValueInput = createInjectMaterial(DynamicValueInput);

+ 4 - 3
packages/materials/form-materials/src/components/inputs-values/index.tsx

@@ -5,12 +5,13 @@
 
 import React from 'react';
 
+import { I18n } from '@flowgram.ai/editor';
 import { Button, IconButton } from '@douyinfe/semi-ui';
 import { IconDelete, IconPlus } from '@douyinfe/semi-icons';
 
 import { IFlowConstantRefValue, IFlowValue } from '@/typings';
 import { useObjectList } from '@/hooks';
-import { DynamicValueInput } from '@/components/dynamic-value-input';
+import { InjectDynamicValueInput } from '@/components/dynamic-value-input';
 
 import { PropsType } from './types';
 import { UIRow, UIRows } from './styles';
@@ -42,9 +43,9 @@ export function InputsValues({
               size="small"
               value={item.key}
               onChange={(v) => updateKey(item.id, v)}
-              placeholder="Input Key"
+              placeholder={I18n.t('Input Key')}
             />
-            <DynamicValueInput
+            <InjectDynamicValueInput
               style={{ flexGrow: 1 }}
               readonly={readonly}
               value={item.value as IFlowConstantRefValue}

+ 5 - 3
packages/materials/form-materials/src/components/json-schema-editor/index.tsx

@@ -17,7 +17,7 @@ import {
   IconMinus,
 } from '@douyinfe/semi-icons';
 
-import { TypeSelector } from '@/components/type-selector';
+import { InjectTypeSelector } from '@/components/type-selector';
 
 import { ConfigType, PropertyValueType } from './types';
 import {
@@ -41,6 +41,8 @@ import { usePropertiesEdit } from './hooks';
 import { DefaultValue } from './default-value';
 import { BlurInput } from './components/blur-input';
 
+const DEFAULT = { type: 'object' };
+
 export function JsonSchemaEditor(props: {
   value?: IJsonSchema;
   onChange?: (value: IJsonSchema) => void;
@@ -48,7 +50,7 @@ export function JsonSchemaEditor(props: {
   className?: string;
   readonly?: boolean;
 }) {
-  const { value = { type: 'object' }, config = {}, onChange: onChangeProps, readonly } = props;
+  const { value = DEFAULT, config = {}, onChange: onChangeProps, readonly } = props;
   const { propertyList, onAddProperty, onRemoveProperty, onEditProperty } = usePropertiesEdit(
     value,
     onChangeProps
@@ -170,7 +172,7 @@ function PropertyEdit(props: {
               />
             </UIName>
             <UIType>
-              <TypeSelector
+              <InjectTypeSelector
                 value={typeSelectorValue}
                 readonly={readonly}
                 onChange={(_value) => {

+ 6 - 2
packages/materials/form-materials/src/components/type-selector/index.tsx

@@ -8,9 +8,10 @@ import React, { useMemo } from 'react';
 import { IJsonSchema } from '@flowgram.ai/json-schema';
 import { Cascader, Icon, IconButton } from '@douyinfe/semi-ui';
 
+import { createInjectMaterial } from '@/shared/inject-material';
 import { useTypeManager } from '@/plugins';
 
-interface PropTypes {
+export interface TypeSelectorProps {
   value?: Partial<IJsonSchema>;
   onChange?: (value?: Partial<IJsonSchema>) => void;
   readonly?: boolean;
@@ -41,7 +42,7 @@ export const parseTypeSelectValue = (value?: string[]): Partial<IJsonSchema> | u
   return { type };
 };
 
-export function TypeSelector(props: PropTypes) {
+export function TypeSelector(props: TypeSelectorProps) {
   const { value, onChange, readonly, disabled, style } = props;
 
   const selectValue = useMemo(() => getTypeSelectValue(value), [value]);
@@ -101,3 +102,6 @@ export function TypeSelector(props: PropTypes) {
     />
   );
 }
+
+TypeSelector.renderKey = 'type-selector-render-key';
+export const InjectTypeSelector = createInjectMaterial(TypeSelector);

+ 7 - 4
packages/materials/form-materials/src/components/variable-selector/index.tsx

@@ -12,10 +12,12 @@ import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
 import { Popover } from '@douyinfe/semi-ui';
 import { IconChevronDownStroked, IconIssueStroked } from '@douyinfe/semi-icons';
 
+import { createInjectMaterial } from '@/shared';
+
 import { useVariableTree } from './use-variable-tree';
 import { UIPopoverContent, UIRootTitle, UITag, UITreeSelect, UIVarName } from './styles';
 
-interface PropTypes {
+export interface VariableSelectorProps {
   value?: string[];
   config?: {
     placeholder?: string;
@@ -30,8 +32,6 @@ interface PropTypes {
   triggerRender?: (props: TriggerRenderProps) => React.ReactNode;
 }
 
-export type VariableSelectorProps = PropTypes;
-
 export { useVariableTree };
 
 export const VariableSelector = ({
@@ -44,7 +44,7 @@ export const VariableSelector = ({
   excludeSchema,
   hasError,
   triggerRender,
-}: PropTypes) => {
+}: VariableSelectorProps) => {
   const treeData = useVariableTree({ includeSchema, excludeSchema });
 
   const treeValue = useMemo(() => {
@@ -136,3 +136,6 @@ export const VariableSelector = ({
     </>
   );
 };
+
+VariableSelector.renderKey = 'variable-selector-render-key';
+export const InjectVariableSelector = createInjectMaterial(VariableSelector);

+ 1 - 0
packages/materials/form-materials/src/shared/index.ts

@@ -4,3 +4,4 @@
  */
 
 export * from './format-legacy-refs';
+export * from './inject-material';

+ 170 - 0
packages/materials/form-materials/src/shared/inject-material/README.md

@@ -0,0 +1,170 @@
+# InjectMaterial Component
+
+A material component wrapper with dependency injection support for implementing dynamic component replacement mechanisms.
+
+## Why Dependency Injection Matters
+
+### ❌ Tight Coupling: Traditional Dependency Issues
+
+```mermaid
+graph TD
+    A[Material A] --> B[Material B]
+    B --> D[Material D]
+    C[Material C] --> D
+
+    style D fill:#ff4757
+    style A fill:#ffa502
+    style B fill:#ffa502
+    style C fill:#ffa502
+
+    note["💥 Problem: D changes require modifications to A, B, C"]
+```
+
+**Issues:** Chain reactions, high maintenance costs
+
+### ✅ Decoupling: Dependency Injection Solution
+
+```mermaid
+graph TD
+    A[Material A] --> RenderKey[Material D RenderKey]
+    B[Material B] --> RenderKey
+    C[Material C] --> RenderKey
+
+    RenderKey -.-> BaseD[Origin D]
+    CustomD[Custom D] -.-> RenderKey
+
+    style RenderKey fill:#5f27cd
+    style BaseD fill:#2ed573
+    style CustomD fill:#26d0ce
+    style A fill:#a55eea
+    style B fill:#a55eea
+    style C fill:#a55eea
+
+    note2["✅ A, B, C depend on abstract interface, decoupled from D"]
+```
+
+**Benefits:** Hot-swapping, parallel development, version compatibility
+
+## Features
+
+- 🔧 **Dependency Injection**: Support dynamic component replacement via FlowRendererRegistry
+- 🔄 **Smart Fallback**: Automatically use default component when no custom component is registered
+- 🎯 **Type Safety**: Full TypeScript type inference support
+- 📦 **Zero Configuration**: Works out of the box without additional setup
+
+## Usage
+
+### 1. Create Injectable Material Component
+
+```tsx
+import { createInjectMaterial } from '@flowgram.ai/form-materials';
+import { VariableSelector } from './VariableSelector';
+
+// Create injectable material wrapper component
+const InjectVariableSelector = createInjectMaterial(VariableSelector);
+
+// Now you can use it like a regular component
+function MyComponent() {
+  return <InjectVariableSelector value={value} onChange={handleChange} />;
+}
+```
+
+### 2. Register Custom Components
+
+Configure custom renderer in `use-editor-props.tsx`:
+
+```tsx
+import { useEditorProps } from '@flowgram.ai/editor';
+import { YourCustomVariableSelector } from './YourCustomVariableSelector';
+import { VariableSelector } from '@flowgram.ai/form-materials';
+
+function useCustomEditorProps() {
+  const editorProps = useEditorProps({
+    materials: {
+      components: {
+        // Use component's renderKey or component name as key
+        [VariableSelector.renderKey]: YourCustomVariableSelector,
+        [TypeSelector.renderKey]: YourCustomTypeSelector,
+      }
+    }
+  });
+
+  return editorProps;
+}
+```
+
+### 3. Use Custom renderKey
+
+If your component requires a specific renderKey:
+
+```tsx
+const InjectCustomComponent = createInjectMaterial(MyComponent, {
+  renderKey: 'my-custom-key'
+});
+
+// When registering
+{
+  materials: {
+    components: {
+      'my-custom-key': MyCustomRenderer
+    }
+  }
+}
+```
+
+## Sequence Diagram
+
+Complete component registration and rendering sequence diagram:
+
+```mermaid
+sequenceDiagram
+    participant App as Application
+    participant Editor as use-editor-props
+    participant Registry as FlowRendererRegistry
+    participant Inject as InjectMaterial
+    participant Default as Default Component
+    participant Custom as Custom Component
+
+    Note over App,Custom: Component Registration Phase
+    App->>Editor: Call use-editor-props()
+    Editor->>Editor: Configure materials.components
+    Editor->>Registry: Register component to FlowRendererRegistry
+    Registry->>Registry: Store mapping relationship
+    Registry-->>App: Registration complete
+
+    Note over App,Custom: Component Rendering Phase
+    App->>Inject: Render InjectMaterial component
+    Inject->>Registry: Query renderer (getRendererComponent)
+
+    alt Custom renderer exists
+        Registry-->>Inject: Return custom React component
+        Inject->>Custom: Render with custom component
+        Custom-->>App: Render custom UI
+    else No custom renderer
+        Registry-->>Inject: Return null or type mismatch
+        Inject->>Default: Render with default component
+        Default-->>App: Render default UI
+    end
+```
+
+## Render Key Priority
+
+Component render key determination follows this priority order:
+
+1. `params.renderKey` (second parameter of createInjectMaterial)
+2. `Component.renderKey` (component's own renderKey property)
+3. `Component.name` (component's display name)
+4. Empty string (final fallback)
+
+## Type Definition
+
+```typescript
+interface CreateInjectMaterialOptions {
+  renderKey?: string;
+}
+
+function createInjectMaterial<Props>(
+  Component: React.FC<Props> & { renderKey?: string },
+  params?: CreateInjectMaterialOptions
+): React.FC<Props>
+```

+ 174 - 0
packages/materials/form-materials/src/shared/inject-material/README.zh.md

@@ -0,0 +1,174 @@
+# InjectMaterial 组件
+
+一个支持依赖注入的 Material 组件包装器,用于实现动态组件替换机制。
+
+## 为什么需要依赖注入
+
+### ❌ 紧耦合:传统依赖问题
+
+```mermaid
+graph TD
+    A[Material A] --> B[Material B]
+    B --> D[Material D]
+    C[Material C] --> D
+
+    style D fill:#ff4757
+    style A fill:#ffa502
+    style B fill:#ffa502
+    style C fill:#ffa502
+
+    note["💥 问题:D变更导致A、B、C全部需要修改"]
+```
+
+**问题:** 连锁反应、高维护成本
+
+### ✅ 解耦:依赖注入方案
+
+```mermaid
+graph TD
+    A[Material A] --> RenderKey[Material D RenderKey]
+    B[Material B] --> RenderKey
+    C[Material C] --> RenderKey
+
+    RenderKey -.-> BaseD[Origin D]
+    CustomD[Custom D] -.-> RenderKey
+
+    style RenderKey fill:#5f27cd
+    style BaseD fill:#2ed573
+    style CustomD fill:#26d0ce
+    style A fill:#a55eea
+    style B fill:#a55eea
+    style C fill:#a55eea
+
+    note2["✅ A、B、C依赖抽象接口,与D实现解耦"]
+```
+
+**优势:** 热插拔、并行开发、版本兼容
+
+## 特性
+
+- 🔧 **依赖注入**:通过 FlowRendererRegistry 支持动态组件替换
+- 🔄 **智能回退**:当没有注册自定义组件时自动使用默认组件
+- 🎯 **类型安全**:完整的 TypeScript 类型推断支持
+- 📦 **零配置**:开箱即用,无需额外设置
+
+## 安装
+
+该组件是 `@flowgram.ai/form-materials` 包的一部分,无需单独安装。
+
+## 使用
+
+### 1. 创建可注入的 Material 组件件
+
+```tsx
+import { createInjectMaterial } from '@flowgram.ai/form-materials';
+import { VariableSelector } from './VariableSelector';
+
+// 创建可注入的Material包装组件
+const InjectVariableSelector = createInjectMaterial(VariableSelector);
+
+// 现在你可以像使用普通组件一样使用它
+function MyComponent() {
+  return <InjectVariableSelector value={value} onChange={handleChange} />;
+}
+```
+
+### 2. 注册自定义组件
+
+在 `use-editor-props.tsx` 中配置自定义渲染器:
+
+```tsx
+import { useEditorProps } from '@flowgram.ai/editor';
+import { YourCustomVariableSelector } from './YourCustomVariableSelector';
+import { VariableSelector } from '@flowgram.ai/form-materials';
+
+function useCustomEditorProps() {
+  const editorProps = useEditorProps({
+    materials: {
+      components: {
+        // 使用组件的 renderKey 或组件名称作为键
+        [VariableSelector.renderKey]: YourCustomVariableSelector,
+        [TypeSelector.renderKey]: YourCustomTypeSelector,
+      }
+    }
+  });
+
+  return editorProps;
+}
+```
+
+### 3. 使用自定义 renderKey
+
+如果你的组件需要特定的 renderKey:
+
+```tsx
+const InjectCustomComponent = createInjectMaterial(MyComponent, {
+  renderKey: 'my-custom-key'
+});
+
+// 注册时
+{
+  materials: {
+    components: {
+      'my-custom-key': MyCustomRenderer
+    }
+  }
+}
+```
+
+## 时序图
+
+完整的组件注册和渲染时序图:
+
+```mermaid
+sequenceDiagram
+    participant App as 应用程序
+    participant Editor as use-editor-props
+    participant Registry as FlowRendererRegistry
+    participant Inject as InjectMaterial
+    participant Default as 默认组件
+    participant Custom as 自定义组件
+
+    Note over App,Custom: 组件注册阶段
+    App->>Editor: 调用 use-editor-props()
+    Editor->>Editor: 配置 materials.components
+    Editor->>Registry: 向 FlowRendererRegistry 注册组件
+    Registry->>Registry: 存储映射关系
+    Registry-->>App: 注册完成
+
+    Note over App,Custom: 组件渲染阶段
+    App->>Inject: 渲染 InjectMaterial 组件
+    Inject->>Registry: 查询渲染器 (getRendererComponent)
+
+    alt 存在自定义渲染器
+        Registry-->>Inject: 返回自定义 React 组件
+        Inject->>Custom: 使用自定义组件渲染
+        Custom-->>App: 渲染自定义 UI
+    else 无自定义渲染器
+        Registry-->>Inject: 返回 null 或类型不匹配
+        Inject->>Default: 使用默认组件渲染
+        Default-->>App: 渲染默认 UI
+    end
+```
+
+## 渲染键优先级
+
+组件渲染键的确定遵循以下优先级顺序:
+
+1. `params.renderKey` (createInjectMaterial 的第二个参数)
+2. `Component.renderKey` (组件自身的 renderKey 属性)
+3. `Component.name` (组件的显示名称)
+4. 空字符串 (最终回退)
+
+## 类型定义
+
+```typescript
+interface CreateInjectMaterialOptions {
+  renderKey?: string;
+}
+
+function createInjectMaterial<Props>(
+  Component: React.FC<Props> & { renderKey?: string },
+  params?: CreateInjectMaterialOptions
+): React.FC<Props>
+```

+ 87 - 0
packages/materials/form-materials/src/shared/inject-material/index.tsx

@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import React from 'react';
+
+import {
+  FlowRendererComponentType,
+  FlowRendererRegistry,
+  usePlaygroundContainer,
+} from '@flowgram.ai/editor';
+
+/**
+ * Creates a material component wrapper with dependency injection support
+ *
+ * This Higher-Order Component (HOC) implements a dynamic component replacement mechanism
+ * for material components. It automatically checks if a custom renderer is registered
+ * in the editor context, using the injected component if available, otherwise
+ * falling back to the default component.
+ *
+ * @example
+ * ```tsx
+ * // 1.Create an injectable material component
+ * const InjectVariableSelector = createInjectMaterial(VariableSelector)
+ *
+ * // 2. Register custom components in editor
+ * // Configure in use-editor-props.tsx:
+ * const editorProps = {
+ *   materials: {
+ *     components: {
+ *       [VariableSelector.renderKey]: YourCustomVariableSelector
+ *     }
+ *   }
+ * }
+ * ```
+ *
+ * @description
+ * Data flow explanation:
+ * - Register components to FlowRendererRegistry in use-editor-props
+ * - InjectMaterial reads renderers from FlowRendererRegistry
+ * - If registered renderer exists and type is REACT, use injected component
+ * - If not exists or type mismatch, fallback to default component
+ *
+ * @param Component - Default React component
+ * @param params - Optional parameters
+ * @param params.renderKey - Custom render key name, highest priority
+ * @returns Wrapper component with dependency injection support
+ */
+export function createInjectMaterial<Props>(
+  Component: React.FC<Props> & { renderKey?: string },
+  params?: {
+    renderKey?: string;
+  }
+): React.FC<Props> {
+  const InjectComponent: React.FC<Props> = (props) => {
+    const container = usePlaygroundContainer();
+
+    // Check if renderer registry is bound in container
+    if (!container?.isBound(FlowRendererRegistry)) {
+      // If no registry, use default component directly
+      return React.createElement(Component as (props?: any) => any, { ...props });
+    }
+
+    // Get renderer registry instance
+    const rendererRegistry = container.get(FlowRendererRegistry);
+
+    // Determine render key: prioritize param specified, then component renderKey, finally component name
+    const renderKey = params?.renderKey || Component.renderKey || Component.name || '';
+
+    // Get corresponding renderer from registry
+    const renderer = rendererRegistry.tryToGetRendererComponent(renderKey);
+
+    // Check if renderer exists and type is React component
+    if (renderer?.type !== FlowRendererComponentType.REACT) {
+      // If no suitable renderer found, fallback to default component
+      return React.createElement(Component as (props?: any) => any, { ...props });
+    }
+
+    // Render using injected React component
+    return React.createElement(renderer.renderer, {
+      ...props,
+    });
+  };
+
+  return InjectComponent;
+}