mirror of https://gitee.com/answerdev/answer.git
feat(Plugin): Front-end plugins support `type`, and all plugins of the same type can be automatically rendered by `type`.
This commit is contained in:
parent
92cd3d7b40
commit
10637527f5
|
@ -1,35 +1,52 @@
|
|||
import { FC, ReactNode } from 'react';
|
||||
import { FC, ReactNode, memo } from 'react';
|
||||
|
||||
import builtin from '@/plugins/builtin';
|
||||
import * as plugins from '@/plugins';
|
||||
import { Plugin } from '@/utils/pluginKit';
|
||||
import { Plugin, PluginType } from '@/utils/pluginKit';
|
||||
|
||||
/**
|
||||
* Note:Please set at least either of the `slug_name` and `type` attributes, otherwise no plugins will be rendered.
|
||||
*
|
||||
* @field slug_name: The `slug_name` of the plugin needs to be rendered.
|
||||
* If this property is set, `PluginRender` will use it first (regardless of whether `type` is set)
|
||||
* to find the corresponding plugin and render it.
|
||||
* @field type: Used to formulate the rendering of all plugins of this type.
|
||||
* (if the `slug_name` attribute is set, it will be ignored)
|
||||
* @field prop: Any attribute you want to configure, e.g. `className`
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
slug_name: string;
|
||||
slug_name?: string;
|
||||
type?: PluginType;
|
||||
children?: ReactNode;
|
||||
[prop: string]: any;
|
||||
}
|
||||
|
||||
const findPluginBySlugName: (l, n) => Plugin | null = (source, slug_name) => {
|
||||
let ret: Plugin | null = null;
|
||||
const findPlugin: (s, k: 'slug_name' | 'type', v) => Plugin[] = (
|
||||
source,
|
||||
k,
|
||||
v,
|
||||
) => {
|
||||
const ret: Plugin[] = [];
|
||||
if (source) {
|
||||
Object.keys(source).forEach((k) => {
|
||||
const p = source[k];
|
||||
if (p && p.info && p.info.slug_name === slug_name && p.component) {
|
||||
ret = p;
|
||||
Object.keys(source).forEach((i) => {
|
||||
const p = source[i];
|
||||
if (p && p.component && p.info && p.info[k] === v) {
|
||||
ret.push(p);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
const Index: FC<Props> = ({ slug_name, children, ...props }) => {
|
||||
const bp = findPluginBySlugName(builtin, slug_name);
|
||||
const vp = findPluginBySlugName(plugins, slug_name);
|
||||
const plugin = bp || vp;
|
||||
const Index: FC<Props> = ({ slug_name, type, children, ...props }) => {
|
||||
const fk = slug_name ? 'slug_name' : 'type';
|
||||
const fv = fk === 'slug_name' ? slug_name : type;
|
||||
const bp = findPlugin(builtin, fk, fv);
|
||||
const vp = findPlugin(plugins, fk, fv);
|
||||
const pluginSlice = [...bp, ...vp];
|
||||
|
||||
if (!plugin) {
|
||||
if (!pluginSlice.length) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
|
@ -37,9 +54,19 @@ const Index: FC<Props> = ({ slug_name, children, ...props }) => {
|
|||
* ps: Logic such as version compatibility determination can be placed here
|
||||
*/
|
||||
|
||||
const PluginComponent = plugin.component;
|
||||
// @ts-ignore
|
||||
return <PluginComponent {...props}>{children}</PluginComponent>;
|
||||
return (
|
||||
<>
|
||||
{pluginSlice.map((ps) => {
|
||||
const PluginFC = ps.component;
|
||||
return (
|
||||
// @ts-ignore
|
||||
<PluginFC key={ps.info.slug_name} {...props}>
|
||||
{children}
|
||||
</PluginFC>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
||||
export default memo(Index);
|
||||
|
|
|
@ -175,15 +175,12 @@ const Index: React.FC = () => {
|
|||
return (
|
||||
<Container style={{ paddingTop: '4rem', paddingBottom: '5rem' }}>
|
||||
<WelcomeTitle />
|
||||
<Col className="mx-auto" md={6} lg={4} xl={3}>
|
||||
<PluginRender slug_name="ui_plugin_demo" className="mb-5" />
|
||||
</Col>
|
||||
{step === 1 ? (
|
||||
<Col className="mx-auto" md={6} lg={4} xl={3}>
|
||||
{ucAgentInfo ? (
|
||||
<PluginRender slug_name="uc_login" className="mb-5" />
|
||||
) : (
|
||||
<PluginRender slug_name="connector" className="mb-5" />
|
||||
<PluginRender type="Connector" className="mb-5" />
|
||||
)}
|
||||
{canOriginalLogin ? (
|
||||
<>
|
||||
|
|
|
@ -26,7 +26,7 @@ const Index: React.FC = () => {
|
|||
|
||||
{showForm ? (
|
||||
<Col className="mx-auto" md={6} lg={4} xl={3}>
|
||||
<PluginRender slug_name="connector" className="mb-5" />
|
||||
<PluginRender type="Connector" className="mb-5" />
|
||||
<SignUpForm callback={onStep} />
|
||||
</Col>
|
||||
) : (
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package demo
|
||||
|
||||
import "github.com/answerdev/answer/plugin"
|
||||
|
||||
type DemoPlugin struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
plugin.Register(&DemoPlugin{})
|
||||
}
|
||||
|
||||
func (d DemoPlugin) Info() plugin.Info {
|
||||
return plugin.Info{
|
||||
Name: plugin.MakeTranslator("i18n.demo.name"),
|
||||
SlugName: "demo_plugin",
|
||||
Description: plugin.MakeTranslator("i18n.demo.description"),
|
||||
Author: "answerdev",
|
||||
Version: "0.0.1",
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import './i18n';
|
|||
|
||||
const pluginInfo: PluginInfo = {
|
||||
slug_name: info.slug_name,
|
||||
type: info.type,
|
||||
};
|
||||
interface Props {
|
||||
className?: string;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
slug_name: connector
|
||||
type: Connector
|
||||
version: 0.0.1
|
||||
link: https://github.com/answerdev/plugins/tree/main/connector/
|
||||
author: Answer.dev
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export default null;
|
||||
|
||||
export { default as Demo } from './Demo';
|
||||
// export { default as Demo } from './Demo';
|
||||
|
|
|
@ -7,14 +7,19 @@ import i18next from 'i18next';
|
|||
* It may be used for feature upgrades or version compatibility processing.
|
||||
*
|
||||
* @field slug_name: Unique identity string for the plugin, usually configured in `info.yaml`
|
||||
* @field type: The type of plugin is defined and a single type of plugin can have multiple implementations.
|
||||
* For example, a plugin of type `Connector` can have a `google` implementation and a `github` implementation.
|
||||
* `PluginRender` automatically renders the plug-in types already included in `PluginType`.
|
||||
* @field name: Plugin name, optionally configurable. Usually read from the `i18n` file
|
||||
* @field description: Plugin description, optionally configurable. Usually read from the `i18n` file
|
||||
*/
|
||||
|
||||
const I18N_NS = 'plugin';
|
||||
|
||||
export type PluginType = 'Connector';
|
||||
export interface PluginInfo {
|
||||
slug_name: string;
|
||||
type?: PluginType;
|
||||
name?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue