mirror of https://gitee.com/answerdev/answer.git
refactor(plugin): Categorize plugins and rename some plugins
This commit is contained in:
parent
a8c37f4dab
commit
079de85f35
|
@ -1,6 +1,8 @@
|
|||
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
|
||||
|
||||
import './i18n/init';
|
||||
|
||||
import '@/utils/pluginKit';
|
||||
import routes from '@/router';
|
||||
|
||||
function App() {
|
||||
|
|
|
@ -9,12 +9,12 @@ import {
|
|||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import PluginRender from '../PluginRender';
|
||||
|
||||
import {
|
||||
BlockQuote,
|
||||
Bold,
|
||||
Chart,
|
||||
Code,
|
||||
Formula,
|
||||
Heading,
|
||||
Help,
|
||||
Hr,
|
||||
|
@ -127,7 +127,9 @@ const MDEditor: ForwardRefRenderFunction<EditorRef, Props> = (
|
|||
<div className={classNames('md-editor-wrap rounded', className)}>
|
||||
<EditorContext.Provider value={context}>
|
||||
{context && (
|
||||
<div className="toolbar-wrap px-3 d-flex align-items-center flex-wrap">
|
||||
<PluginRender
|
||||
type="editor"
|
||||
className="toolbar-wrap px-3 d-flex align-items-center flex-wrap">
|
||||
<Heading {...context} />
|
||||
<Bold {...context} />
|
||||
<Italice {...context} />
|
||||
|
@ -136,10 +138,7 @@ const MDEditor: ForwardRefRenderFunction<EditorRef, Props> = (
|
|||
<Link {...context} />
|
||||
<BlockQuote {...context} />
|
||||
<Image {...context} />
|
||||
<div className="toolbar-divider" />
|
||||
<Table {...context} />
|
||||
<Formula {...context} />
|
||||
<Chart {...context} />
|
||||
<div className="toolbar-divider" />
|
||||
<OL {...context} />
|
||||
<UL {...context} />
|
||||
|
@ -148,7 +147,7 @@ const MDEditor: ForwardRefRenderFunction<EditorRef, Props> = (
|
|||
<Hr {...context} />
|
||||
<div className="toolbar-divider" />
|
||||
<Help />
|
||||
</div>
|
||||
</PluginRender>
|
||||
)}
|
||||
</EditorContext.Provider>
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { FC, ReactNode, memo } from 'react';
|
||||
import React, { FC, ReactNode, memo } from 'react';
|
||||
|
||||
import builtin from '@/plugins/builtin';
|
||||
import * as plugins from '@/plugins';
|
||||
import { Plugin, PluginType } from '@/utils/pluginKit';
|
||||
import PluginKite, { Plugin, PluginType } from '@/utils/pluginKit';
|
||||
|
||||
/**
|
||||
* Note:Please set at least either of the `slug_name` and `type` attributes, otherwise no plugins will be rendered.
|
||||
|
@ -22,47 +20,60 @@ interface Props {
|
|||
[prop: string]: any;
|
||||
}
|
||||
|
||||
const findPlugin: (s, k: 'slug_name' | 'type', v) => Plugin[] = (
|
||||
source,
|
||||
k,
|
||||
v,
|
||||
) => {
|
||||
const ret: Plugin[] = [];
|
||||
if (source) {
|
||||
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, 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];
|
||||
const pluginSlice: Plugin[] = [];
|
||||
const plugins = PluginKite.getPlugins();
|
||||
|
||||
plugins.forEach((plugin) => {
|
||||
if (type && slug_name) {
|
||||
if (plugin.info.slug_name === slug_name && plugin.info.type === type) {
|
||||
pluginSlice.push(plugin);
|
||||
}
|
||||
} else if (type) {
|
||||
if (plugin.info.type === type) {
|
||||
pluginSlice.push(plugin);
|
||||
}
|
||||
} else if (slug_name) {
|
||||
if (plugin.info.slug_name === slug_name) {
|
||||
pluginSlice.push(plugin);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!pluginSlice.length) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* TODO: Rendering control for non-builtin plug-ins
|
||||
* ps: Logic such as version compatibility determination can be placed here
|
||||
*/
|
||||
|
||||
if (type === 'editor') {
|
||||
const nodes = React.Children.map(children, (child, index) => {
|
||||
if (index === 0) {
|
||||
return (
|
||||
<>
|
||||
{child}
|
||||
{pluginSlice.map((ps) => {
|
||||
const PluginFC = ps.component;
|
||||
return (
|
||||
// @ts-ignore
|
||||
<PluginFC key={ps.info.slug_name} />
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
return child;
|
||||
});
|
||||
|
||||
return <div {...props}>{nodes}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{pluginSlice.map((ps) => {
|
||||
const PluginFC = ps.component;
|
||||
return (
|
||||
// @ts-ignore
|
||||
<PluginFC key={ps.info.slug_name} {...props}>
|
||||
{children}
|
||||
</PluginFC>
|
||||
<PluginFC key={ps.info.slug_name} {...props} />
|
||||
);
|
||||
})}
|
||||
</>
|
||||
|
|
|
@ -38,7 +38,7 @@ const Index: FC<Props> = ({ data }) => {
|
|||
<div className="mb-5">
|
||||
<div className="mb-3 d-flex align-items-center justify-content-between">
|
||||
<h3 className="mb-0">{t('title')}</h3>
|
||||
<PluginRender slug_name="algolia" />
|
||||
<PluginRender type="search" slug_name="algolia" />
|
||||
</div>
|
||||
<p>
|
||||
<span className="text-secondary me-1">{t('keywords')}</span>
|
||||
|
|
|
@ -144,9 +144,17 @@ const Index: React.FC = () => {
|
|||
{step === 1 ? (
|
||||
<Col className="mx-auto" md={6} lg={4} xl={3}>
|
||||
{ucAgentInfo ? (
|
||||
<PluginRender slug_name="uc_login" className="mb-5" />
|
||||
<PluginRender
|
||||
type="connector"
|
||||
slug_name="hosting_connector"
|
||||
className="mb-5"
|
||||
/>
|
||||
) : (
|
||||
<PluginRender type="Connector" className="mb-5" />
|
||||
<PluginRender
|
||||
type="connector"
|
||||
slug_name="third_party_connector"
|
||||
className="mb-5"
|
||||
/>
|
||||
)}
|
||||
{canOriginalLogin ? (
|
||||
<>
|
||||
|
|
|
@ -35,7 +35,11 @@ const Index: React.FC = () => {
|
|||
|
||||
{showForm ? (
|
||||
<Col className="mx-auto" md={6} lg={4} xl={3}>
|
||||
<PluginRender type="Connector" className="mb-5" />
|
||||
<PluginRender
|
||||
type="connector"
|
||||
slug_name="third_party_connector"
|
||||
className="mb-5"
|
||||
/>
|
||||
{showSignupForm ? <SignUpForm callback={onStep} /> : null}
|
||||
<div className="text-center mt-5">
|
||||
<Trans i18nKey="login.info_login" ns="translation">
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import pluginKit from '@/utils/pluginKit';
|
||||
import { initI18nResource } from '@/utils/pluginKit/utils';
|
||||
|
||||
import en_US from './en_US.yaml';
|
||||
import zh_CN from './zh_CN.yaml';
|
||||
|
||||
pluginKit.initI18nResource({
|
||||
initI18nResource({
|
||||
en_US,
|
||||
zh_CN,
|
||||
});
|
||||
|
|
|
@ -2,18 +2,21 @@ import { memo, FC } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
|
||||
import pluginKit, { PluginInfo } from '@/utils/pluginKit';
|
||||
import { PluginInfo } from '@/utils/pluginKit';
|
||||
import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils';
|
||||
|
||||
import './i18n';
|
||||
|
||||
import info from './info.yaml';
|
||||
|
||||
const pluginInfo: PluginInfo = {
|
||||
slug_name: info.slug_name,
|
||||
type: info.type,
|
||||
};
|
||||
|
||||
const Index: FC = () => {
|
||||
const { t } = useTranslation(pluginKit.getTransNs(), {
|
||||
keyPrefix: pluginKit.getTransKeyPrefix(pluginInfo),
|
||||
const { t } = useTranslation(getTransNs(), {
|
||||
keyPrefix: getTransKeyPrefix(pluginInfo),
|
||||
});
|
||||
|
||||
return <Alert variant="info">{t('msg')}</Alert>;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import pluginKit from '@/utils/pluginKit';
|
||||
import { initI18nResource } from '@/utils/pluginKit/utils';
|
||||
|
||||
import en_US from './en_US.yaml';
|
||||
import zh_CN from './zh_CN.yaml';
|
||||
|
||||
pluginKit.initI18nResource({
|
||||
initI18nResource({
|
||||
en_US,
|
||||
zh_CN,
|
||||
});
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { memo, FC } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import pluginKit, { PluginInfo } from '@/utils/pluginKit';
|
||||
import { PluginInfo } from '@/utils/pluginKit';
|
||||
import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils';
|
||||
import { SvgIcon } from '@/components';
|
||||
|
||||
import info from './info.yaml';
|
||||
|
@ -10,11 +11,12 @@ import './i18n';
|
|||
|
||||
const pluginInfo: PluginInfo = {
|
||||
slug_name: info.slug_name,
|
||||
type: info.type,
|
||||
};
|
||||
|
||||
const Index: FC = () => {
|
||||
const { t } = useTranslation(pluginKit.getTransNs(), {
|
||||
keyPrefix: pluginKit.getTransKeyPrefix(pluginInfo),
|
||||
const { t } = useTranslation(getTransNs(), {
|
||||
keyPrefix: getTransKeyPrefix(pluginInfo),
|
||||
});
|
||||
|
||||
const { data } = useGetAlgoliaInfo();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
slug_name: algolia
|
||||
type: search
|
||||
version: 0.0.1
|
||||
author: Answer.dev
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
plugin:
|
||||
uc_login:
|
||||
hosting_connector:
|
||||
ui:
|
||||
connect: Connect with {{ auth_name }}
|
||||
login: Login
|
|
@ -1,9 +1,9 @@
|
|||
import pluginKit from '@/utils/pluginKit';
|
||||
import { initI18nResource } from '@/utils/pluginKit/utils';
|
||||
|
||||
import en_US from './en_US.yaml';
|
||||
import zh_CN from './zh_CN.yaml';
|
||||
|
||||
pluginKit.initI18nResource({
|
||||
initI18nResource({
|
||||
en_US,
|
||||
zh_CN,
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
plugin:
|
||||
uc_login:
|
||||
hosting_connector:
|
||||
ui:
|
||||
connect: 连接到 {{ auth_name }}
|
||||
login: 登录
|
|
@ -4,7 +4,8 @@ import { useTranslation } from 'react-i18next';
|
|||
|
||||
import classnames from 'classnames';
|
||||
|
||||
import pluginKit, { PluginInfo } from '@/utils/pluginKit';
|
||||
import { PluginInfo } from '@/utils/pluginKit';
|
||||
import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils';
|
||||
import { SvgIcon } from '@/components';
|
||||
import { userCenterStore } from '@/stores';
|
||||
import './i18n';
|
||||
|
@ -17,11 +18,12 @@ interface Props {
|
|||
|
||||
const pluginInfo: PluginInfo = {
|
||||
slug_name: info.slug_name,
|
||||
type: info.type,
|
||||
};
|
||||
|
||||
const Index: FC<Props> = ({ className }) => {
|
||||
const { t } = useTranslation(pluginKit.getTransNs(), {
|
||||
keyPrefix: pluginKit.getTransKeyPrefix(pluginInfo),
|
||||
const { t } = useTranslation(getTransNs(), {
|
||||
keyPrefix: getTransKeyPrefix(pluginInfo),
|
||||
});
|
||||
const ucAgent = userCenterStore().agent;
|
||||
const ucLoginRedirect =
|
|
@ -0,0 +1,5 @@
|
|||
slug_name: hosting_connector
|
||||
type: connector
|
||||
version: 0.0.1
|
||||
author: Answer.dev
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
plugin:
|
||||
connector:
|
||||
third_party_connector:
|
||||
ui:
|
||||
connect: Connect with {{ auth_name }}
|
||||
remove: Remove {{ auth_name }}
|
|
@ -1,9 +1,9 @@
|
|||
import pluginKit from '@/utils/pluginKit';
|
||||
import { initI18nResource } from '@/utils/pluginKit/utils';
|
||||
|
||||
import en_US from './en_US.yaml';
|
||||
import zh_CN from './zh_CN.yaml';
|
||||
|
||||
pluginKit.initI18nResource({
|
||||
initI18nResource({
|
||||
en_US,
|
||||
zh_CN,
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
plugin:
|
||||
connector:
|
||||
third_party_connector:
|
||||
ui:
|
||||
connect: 连接到 {{ auth_name }}
|
||||
remove: 解绑 {{ auth_name }}
|
|
@ -4,7 +4,8 @@ import { useTranslation } from 'react-i18next';
|
|||
|
||||
import classnames from 'classnames';
|
||||
|
||||
import pluginKit, { PluginInfo } from '@/utils/pluginKit';
|
||||
import { PluginInfo } from '@/utils/pluginKit';
|
||||
import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils';
|
||||
import { SvgIcon } from '@/components';
|
||||
|
||||
import info from './info.yaml';
|
||||
|
@ -19,8 +20,8 @@ interface Props {
|
|||
className?: string;
|
||||
}
|
||||
const Index: FC<Props> = ({ className }) => {
|
||||
const { t } = useTranslation(pluginKit.getTransNs(), {
|
||||
keyPrefix: pluginKit.getTransKeyPrefix(pluginInfo),
|
||||
const { t } = useTranslation(getTransNs(), {
|
||||
keyPrefix: getTransKeyPrefix(pluginInfo),
|
||||
});
|
||||
|
||||
const { data } = useGetStartUseOauthConnector();
|
|
@ -1,5 +1,5 @@
|
|||
slug_name: connector
|
||||
type: Connector
|
||||
slug_name: third_party_connector
|
||||
type: connector
|
||||
version: 0.0.1
|
||||
link: https://github.com/answerdev/plugins/tree/main/connector/
|
||||
author: Answer.dev
|
|
@ -1,4 +0,0 @@
|
|||
slug_name: uc_login
|
||||
version: 0.0.1
|
||||
author: Answer.dev
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import Connector from './Connector';
|
||||
import UcLogin from './UcLogin';
|
||||
import ThirdPartyConnector from './ThirdPartyConnector';
|
||||
import HostingConnector from './HostingConnector';
|
||||
import Algolia from './Algolia';
|
||||
|
||||
export default {
|
||||
Connector,
|
||||
UcLogin,
|
||||
ThirdPartyConnector,
|
||||
HostingConnector,
|
||||
Algolia,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
import { NamedExoticComponent, FC } from 'react';
|
||||
|
||||
import builtin from '@/plugins/builtin';
|
||||
import * as allPlugins from '@/plugins';
|
||||
|
||||
/**
|
||||
* This information is to be defined for all components.
|
||||
* 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
|
||||
*/
|
||||
|
||||
export type PluginType = 'connector' | 'search' | 'editor';
|
||||
export interface PluginInfo {
|
||||
slug_name: string;
|
||||
type: PluginType;
|
||||
name?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface Plugin {
|
||||
info: PluginInfo;
|
||||
component: NamedExoticComponent | FC;
|
||||
}
|
||||
|
||||
class Plugins {
|
||||
plugins: Plugin[] = [];
|
||||
|
||||
constructor() {
|
||||
this.registerBuiltin();
|
||||
this.registerPlugins();
|
||||
}
|
||||
|
||||
validate(plugin: Plugin) {
|
||||
if (!plugin) {
|
||||
return false;
|
||||
}
|
||||
const { info } = plugin;
|
||||
const { slug_name, type } = info;
|
||||
|
||||
if (!slug_name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
registerBuiltin() {
|
||||
Object.keys(builtin).forEach((key) => {
|
||||
const plugin = builtin[key];
|
||||
this.register(plugin);
|
||||
});
|
||||
}
|
||||
|
||||
registerPlugins() {
|
||||
Object.keys(allPlugins).forEach((key) => {
|
||||
const plugin = allPlugins[key];
|
||||
this.register(plugin);
|
||||
});
|
||||
}
|
||||
|
||||
register(plugin: Plugin) {
|
||||
const bool = this.validate(plugin);
|
||||
if (!bool) {
|
||||
return;
|
||||
}
|
||||
this.plugins.push(plugin);
|
||||
}
|
||||
|
||||
getPlugin(slug_name: string) {
|
||||
return this.plugins.find((p) => p.info.slug_name === slug_name);
|
||||
}
|
||||
|
||||
getPluginsByType(type: PluginType) {
|
||||
return this.plugins.filter((p) => p.info.type === type);
|
||||
}
|
||||
|
||||
getPlugins() {
|
||||
return this.plugins;
|
||||
}
|
||||
}
|
||||
|
||||
const plugins = new Plugins();
|
||||
|
||||
export default plugins;
|
|
@ -8,7 +8,7 @@ import i18next from 'i18next';
|
|||
*
|
||||
* @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.
|
||||
* 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
|
||||
|
@ -16,10 +16,10 @@ import i18next from 'i18next';
|
|||
|
||||
const I18N_NS = 'plugin';
|
||||
|
||||
export type PluginType = 'Connector';
|
||||
export type PluginType = 'connector' | 'search' | 'editor';
|
||||
export interface PluginInfo {
|
||||
slug_name: string;
|
||||
type?: PluginType;
|
||||
type: PluginType;
|
||||
name?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
@ -71,8 +71,4 @@ const getTransKeyPrefix = (info: PluginInfo) => {
|
|||
return kp;
|
||||
};
|
||||
|
||||
export default {
|
||||
initI18nResource,
|
||||
getTransNs,
|
||||
getTransKeyPrefix,
|
||||
};
|
||||
export { initI18nResource, getTransNs, getTransKeyPrefix };
|
Loading…
Reference in New Issue