From 09ad0c28527739231da8e9ca284681ead162358e Mon Sep 17 00:00:00 2001 From: juzhiyuan Date: Sat, 28 Nov 2020 09:04:30 +0800 Subject: [PATCH 1/8] feat(plugin): added code mirror --- web/package.json | 1 + .../components/Plugin/CodeMirrorDrawer.tsx | 83 ++++++++ web/src/components/Plugin/IconFont.tsx | 24 +++ web/src/components/Plugin/PluginPage.tsx | 189 ++++++++++++++++++ web/src/components/Plugin/data.tsx | 169 ++++++++++++++++ web/src/components/Plugin/index.ts | 17 ++ web/src/components/Plugin/service.ts | 98 +++++++++ web/src/components/Plugin/typing.d.ts | 39 ++++ web/src/pages/Route/Create.tsx | 7 +- .../pages/Route/components/Step3/index.tsx | 69 ++++--- 10 files changed, 658 insertions(+), 38 deletions(-) create mode 100644 web/src/components/Plugin/CodeMirrorDrawer.tsx create mode 100644 web/src/components/Plugin/IconFont.tsx create mode 100644 web/src/components/Plugin/PluginPage.tsx create mode 100644 web/src/components/Plugin/data.tsx create mode 100644 web/src/components/Plugin/index.ts create mode 100644 web/src/components/Plugin/service.ts create mode 100644 web/src/components/Plugin/typing.d.ts diff --git a/web/package.json b/web/package.json index da5799d63c..05ce18f23e 100644 --- a/web/package.json +++ b/web/package.json @@ -60,6 +60,7 @@ "@api7-dashboard/ui": "^1.0.3", "@rjsf/antd": "2.2.0", "@rjsf/core": "2.2.0", + "@uiw/react-codemirror": "^3.0.1", "antd": "^4.4.0", "classnames": "^2.2.6", "dayjs": "1.8.28", diff --git a/web/src/components/Plugin/CodeMirrorDrawer.tsx b/web/src/components/Plugin/CodeMirrorDrawer.tsx new file mode 100644 index 0000000000..b382d85d66 --- /dev/null +++ b/web/src/components/Plugin/CodeMirrorDrawer.tsx @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useRef } from 'react'; +import { Drawer, Button, notification } from 'antd'; +import CodeMirror from '@uiw/react-codemirror'; + +type Props = { + visible?: boolean; + data?: object; + readonly?: boolean; + onClose?: () => void; + onSubmit?: (data: object) => void; +}; + +const CodeMirrorDrawer: React.FC = ({ + visible = false, + readonly = false, + data = {}, + onClose, + onSubmit, +}) => { + const ref = useRef(null); + return ( + + + + + ) + } + > + + + ); +}; + +export default CodeMirrorDrawer; diff --git a/web/src/components/Plugin/IconFont.tsx b/web/src/components/Plugin/IconFont.tsx new file mode 100644 index 0000000000..488972064c --- /dev/null +++ b/web/src/components/Plugin/IconFont.tsx @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createFromIconfontCN } from '@ant-design/icons'; + +// NOTE: Icons from AliCDN https://www.iconfont.cn/manage/index +const IconFont = createFromIconfontCN({ + scriptUrl: '//at.alicdn.com/t/font_2088089_a3klmsocd15.js', +}); + +export default IconFont; diff --git a/web/src/components/Plugin/PluginPage.tsx b/web/src/components/Plugin/PluginPage.tsx new file mode 100644 index 0000000000..d5bfabd377 --- /dev/null +++ b/web/src/components/Plugin/PluginPage.tsx @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useEffect, useState } from 'react'; +import { Anchor, Layout, Switch, Card, Tooltip, Button, notification, Avatar } from 'antd'; +import { SettingFilled } from '@ant-design/icons'; +import { PanelSection } from '@api7-dashboard/ui'; +import { validate } from 'json-schema'; + +import { fetchSchema, getList } from './service'; +import { PLUGIN_MAPPER_SOURCE } from './data'; +import CodeMirrorDrawer from './CodeMirrorDrawer'; + +type Props = { + readonly?: boolean; + initialData?: PluginComponent.Data; + schemaType?: PluginComponent.Schema; + onChange?: (data: PluginComponent.Data) => void; +}; + +const PanelSectionStyle = { + display: 'grid', + gridTemplateColumns: 'repeat(3, 33.333333%)', + gridRowGap: 15, + gridColumnGap: 10, + width: 'calc(100% - 20px)', +}; + +const { Sider, Content } = Layout; + +// NOTE: use this flag as plugin's name to hide drawer +const NEVER_EXIST_PLUGIN_FLAG = 'NEVER_EXIST_PLUGIN_FLAG'; + +const PluginPage: React.FC = ({ + readonly = false, + initialData = {}, + schemaType = '', + onChange = () => {}, +}) => { + const [pluginList, setPlugin] = useState([]); + const [name, setName] = useState(NEVER_EXIST_PLUGIN_FLAG); + + useEffect(() => { + getList().then(setPlugin); + }, []); + + return ( + <> + + + + + {pluginList.map((plugins) => { + const { category } = plugins[0]; + return ( + + ); + })} + + + + {pluginList.map((plugins) => { + const { category } = plugins[0]; + return ( + + {plugins.map((item) => ( + + ), + + {item.name} + , + ]} + style={{ height: 66 }} + extra={[ + + + + + ) + } + > + } onClick={() => { - try { - if (onSubmit) { - onSubmit(JSON.parse(ref.current?.editor.getValue())); - } - } catch (error) { - notification.error({ - message: 'Invalid JSON data', - }); - } + window.open(`https://github.com/apache/apisix/blob/master/doc/plugins/${name}.md`); }} > - Submit - - - ) - } - > - - + Document + , + , + ]} + > + + + + ); }; diff --git a/web/src/components/Plugin/PluginPage.tsx b/web/src/components/Plugin/PluginPage.tsx index cbb071d2f5..8c5ab79122 100644 --- a/web/src/components/Plugin/PluginPage.tsx +++ b/web/src/components/Plugin/PluginPage.tsx @@ -125,14 +125,7 @@ const PluginPage: React.FC = ({ }} /> ), - - {item.name} - , + {item.name}, ]} style={{ height: 66 }} extra={[ @@ -171,6 +164,7 @@ const PluginPage: React.FC = ({ Date: Thu, 10 Dec 2020 10:21:44 +0800 Subject: [PATCH 6/8] feat: use local icon files --- web/src/app.tsx | 2 + .../IconFont/IconFont.tsx} | 19 +++-- .../IconFont.tsx => IconFont/index.ts} | 9 +- web/src/components/Plugin/data.tsx | 14 +-- web/src/global.less | 12 +++ web/src/helpers.tsx | 14 +-- web/src/libs/iconfont.js | 85 +++++++++++++++++++ 7 files changed, 128 insertions(+), 27 deletions(-) rename web/src/{iconfont.ts => components/IconFont/IconFont.tsx} (74%) rename web/src/components/{Plugin/IconFont.tsx => IconFont/index.ts} (75%) create mode 100644 web/src/libs/iconfont.js diff --git a/web/src/app.tsx b/web/src/app.tsx index 41921f1790..8e67c873f4 100644 --- a/web/src/app.tsx +++ b/web/src/app.tsx @@ -26,6 +26,8 @@ import RightContent from '@/components/RightContent'; import Footer from '@/components/Footer'; import { queryCurrent } from '@/services/user'; import { getMenuData, errorHandler } from '@/helpers'; + +import './libs/iconfont'; import defaultSettings from '../config/defaultSettings'; export async function getInitialState(): Promise<{ diff --git a/web/src/iconfont.ts b/web/src/components/IconFont/IconFont.tsx similarity index 74% rename from web/src/iconfont.ts rename to web/src/components/IconFont/IconFont.tsx index df1b5077d3..540ddfc38a 100644 --- a/web/src/iconfont.ts +++ b/web/src/components/IconFont/IconFont.tsx @@ -14,11 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createFromIconfontCN } from '@ant-design/icons'; +import React from 'react'; -// NOTE: 增加新图标时,请访问 https://www.iconfont.cn/manage/index 进行图标管理 -const IconFont = createFromIconfontCN({ - scriptUrl: '//at.alicdn.com/t/font_1918158_alfpv3n06l6.js', -}); +type Props = { + name: string; +}; + +/** + * Icon Font + * https://www.iconfont.cn/help/detail?helptype=code + */ +const IconFont: React.FC = ({ name }) => ( + +); export default IconFont; diff --git a/web/src/components/Plugin/IconFont.tsx b/web/src/components/IconFont/index.ts similarity index 75% rename from web/src/components/Plugin/IconFont.tsx rename to web/src/components/IconFont/index.ts index 488972064c..a0209291eb 100644 --- a/web/src/components/Plugin/IconFont.tsx +++ b/web/src/components/IconFont/index.ts @@ -14,11 +14,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createFromIconfontCN } from '@ant-design/icons'; - -// NOTE: Icons from AliCDN https://www.iconfont.cn/manage/index -const IconFont = createFromIconfontCN({ - scriptUrl: '//at.alicdn.com/t/font_2088089_a3klmsocd15.js', -}); - -export default IconFont; +export { default } from './IconFont'; diff --git a/web/src/components/Plugin/data.tsx b/web/src/components/Plugin/data.tsx index df66c3b224..40b1d608d7 100644 --- a/web/src/components/Plugin/data.tsx +++ b/web/src/components/Plugin/data.tsx @@ -16,7 +16,7 @@ */ import React from 'react'; -import IconFont from './IconFont'; +import IconFont from '../IconFont'; export const PLUGIN_MAPPER_SOURCE: Record> = { 'limit-req': { @@ -34,12 +34,12 @@ export const PLUGIN_MAPPER_SOURCE: Record, + avatar: , }, skywalking: { category: 'Observability', priority: 2, - avatar: , + avatar: , }, zipkin: { category: 'Observability', @@ -63,12 +63,12 @@ export const PLUGIN_MAPPER_SOURCE: Record, + avatar: , }, 'authz-keycloak': { category: 'Authentication', priority: 5, - avatar: , + avatar: , }, 'ip-restriction': { category: 'Security', @@ -86,7 +86,7 @@ export const PLUGIN_MAPPER_SOURCE: Record, + avatar: , }, 'proxy-rewrite': { category: 'Other', @@ -124,7 +124,7 @@ export const PLUGIN_MAPPER_SOURCE: Record, + avatar: , }, cors: { category: 'Security', diff --git a/web/src/global.less b/web/src/global.less index b86beab960..aa176711c1 100644 --- a/web/src/global.less +++ b/web/src/global.less @@ -49,11 +49,13 @@ ol { .ant-table { width: 100%; overflow-x: auto; + &-thead > tr, &-tbody > tr { > th, > td { white-space: pre; + > span { display: block; } @@ -88,3 +90,13 @@ ol { .ant-layout.ant-layout-has-sider > .ant-layout-content { overflow-x: unset; } + +// NOTE: compatible with IconFont +.icon { + width: 1.2em; + height: 1.2em; + margin-right: 1em; + overflow: hidden; + vertical-align: -0.15em; + fill: currentColor; +} diff --git a/web/src/helpers.tsx b/web/src/helpers.tsx index 34860ab328..a0ffe74f51 100644 --- a/web/src/helpers.tsx +++ b/web/src/helpers.tsx @@ -21,39 +21,39 @@ import { history } from 'umi'; import moment from 'moment'; import { codeMessage } from './constants'; -import IconFont from './iconfont'; +import IconFont from './components/IconFont'; export const getMenuData = (): MenuDataItem[] => { return [ { name: 'metrics', path: '/metrics', - icon: , + icon: , }, { name: 'routes', path: '/routes/list', - icon: , + icon: , }, { name: 'ssl', path: '/ssl/list', - icon: , + icon: , }, { name: 'upstream', path: '/upstream/list', - icon: , + icon: , }, { name: 'consumer', path: '/consumer/list', - icon: , + icon: , }, { name: 'setting', path: '/settings', - icon: , + icon: , }, ]; }; diff --git a/web/src/libs/iconfont.js b/web/src/libs/iconfont.js new file mode 100644 index 0000000000..0c8d3d6a6f --- /dev/null +++ b/web/src/libs/iconfont.js @@ -0,0 +1,85 @@ +/* +* MIT License + +* Copyright (c) 2019 Alipay.inc + +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: + +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +/* eslint-disable */ +!(function (l) { + var c, + h, + t, + a, + i, + e, + z = + '', + p = (p = document.getElementsByTagName('script'))[p.length - 1].getAttribute('data-injectcss'); + if (p && !l.__iconfont__svg__cssinject__) { + l.__iconfont__svg__cssinject__ = !0; + try { + document.write( + '', + ); + } catch (l) { + console && console.log(l); + } + } + function o() { + i || ((i = !0), t()); + } + (c = function () { + var l, c, h, t; + ((t = document.createElement('div')).innerHTML = z), + (z = null), + (h = t.getElementsByTagName('svg')[0]) && + (h.setAttribute('aria-hidden', 'true'), + (h.style.position = 'absolute'), + (h.style.width = 0), + (h.style.height = 0), + (h.style.overflow = 'hidden'), + (l = h), + (c = document.body).firstChild + ? ((t = l), (h = c.firstChild).parentNode.insertBefore(t, h)) + : c.appendChild(l)); + }), + document.addEventListener + ? ~['complete', 'loaded', 'interactive'].indexOf(document.readyState) + ? setTimeout(c, 0) + : ((h = function () { + document.removeEventListener('DOMContentLoaded', h, !1), c(); + }), + document.addEventListener('DOMContentLoaded', h, !1)) + : document.attachEvent && + ((t = c), + (a = l.document), + (i = !1), + (e = function () { + try { + a.documentElement.doScroll('left'); + } catch (l) { + return void setTimeout(e, 50); + } + o(); + })(), + (a.onreadystatechange = function () { + 'complete' == a.readyState && ((a.onreadystatechange = null), o()); + })); +})(window); From f207da9272ca7703e32caab057faf8da067255f5 Mon Sep 17 00:00:00 2001 From: juzhiyuan Date: Thu, 10 Dec 2020 10:23:48 +0800 Subject: [PATCH 7/8] feat: update ASF Release cfg --- .actions/ASF-Release.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/.actions/ASF-Release.cfg b/.actions/ASF-Release.cfg index bed65bf18b..603f24546c 100644 --- a/.actions/ASF-Release.cfg +++ b/.actions/ASF-Release.cfg @@ -78,6 +78,7 @@ web/src/e2e/__mocks__/antd-pro-merge-less.js web/src/e2e/baseLayout.e2e.js web/src/pages/404.tsx web/src/service-worker.js +web/src/libs/iconinfo.js api/build-tools/json.lua # Skip files containing Apache 2.0 License From f602efc0e0dea76eea66bbe8b061c8f6a5872a8f Mon Sep 17 00:00:00 2001 From: juzhiyuan Date: Thu, 10 Dec 2020 10:26:18 +0800 Subject: [PATCH 8/8] feat: update ASF Release cfg --- .actions/ASF-Release.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.actions/ASF-Release.cfg b/.actions/ASF-Release.cfg index 603f24546c..e2828f79fa 100644 --- a/.actions/ASF-Release.cfg +++ b/.actions/ASF-Release.cfg @@ -78,7 +78,7 @@ web/src/e2e/__mocks__/antd-pro-merge-less.js web/src/e2e/baseLayout.e2e.js web/src/pages/404.tsx web/src/service-worker.js -web/src/libs/iconinfo.js +web/src/libs/iconfont.js api/build-tools/json.lua # Skip files containing Apache 2.0 License