@akinon/ui-shell-dev 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index-cI-pE1VB.js +387 -0
- package/dist/assets/index-tRJwBFdH.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +4 -8
- package/src/components/AppLayout.tsx +58 -44
- package/src/components/AppRouter.tsx +41 -0
- package/src/components/Header.tsx +28 -52
- package/src/components/HeaderSearch.tsx +12 -0
- package/src/components/HeaderUser.tsx +23 -0
- package/src/components/Navigation.tsx +82 -0
- package/src/components/PluginTestPage.tsx +223 -0
- package/src/components/WelcomePage.tsx +114 -0
- package/src/components/layout.scss +46 -0
- package/src/shell-app.tsx +1 -1
- package/src/types.ts +4 -0
- package/dist/assets/index-CuIRnKVE.css +0 -1
- package/dist/assets/index-DUUd718F.js +0 -193
- package/src/components/Sidebar.tsx +0 -74
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.akinon-dropdown-menu-item-selected{font-weight:700!important}.akinon-method-modal{font-family:var(--token-fontFamily)}.akinon-method-modal-content{background-color:var(--token-colorBgElevated)!important}.akinon-method-modal-confirm-title{color:var(--color-neutral-50)!important}.akinon-method-modal-confirm-content{color:var(--color-neutral-100)!important;font-weight:300!important}.ant-notification-notice{padding:21px 70px 21px 24px!important;border-radius:5px;background-color:var(--color-ebonyClay-500)}.ant-notification-notice .ant-notification-notice-icon{right:24.5px!important}.ant-notification-notice .ant-notification-notice-message{color:var(--color-neutral-50)!important;font-size:var(--token-fontSize)!important;line-height:1.4285714285714286!important;margin:0!important;font-family:var(--token-fontFamily)}.ant-notification-notice-success{border:1px solid var(--color-green-954)}.ant-notification-notice-success .ant-notification-notice-icon svg{color:var(--color-green-954)}.ant-notification-notice-info{border:1px solid var(--color-orange-954)}.ant-notification-notice-info .ant-notification-notice-icon svg{color:var(--color-orange-954)}.ant-notification-notice-error{border:1px solid var(--color-red-425)}.ant-notification-notice-error .ant-notification-notice-icon svg{color:var(--color-red-425)}.akinon-table-wrapper{font-weight:500;line-height:normal}.akinon-table-wrapper .akinon-table-tbody tr:last-child td{border-bottom:none}.akinon-table-wrapper .akinon-table-thead>tr>th{border-bottom:none}.akinon-table-wrapper .akinon-table-thead .akinon-table-selection{display:none}.akinon-table-wrapper .akinon-checkbox-wrapper{margin-right:20px}.akinon-upload.akinon-upload-select{cursor:pointer;flex:1;width:100%;padding:10px 20px;font-weight:600;background-color:var(--color-ebonyClay-475);border-radius:5px;background-image:url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='black' stroke-width='4' stroke-dasharray='6%2c 12' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e")}.akinon-upload.akinon-upload-select:not(:has(.akinon-upload-list-item)){max-width:300px}.akinon-upload-wrapper .akinon-upload-list:not(.akinon-upload-list-picture-card){line-height:0;max-width:300px}.akinon-upload-wrapper .akinon-upload-list:not(.akinon-upload-list-picture-card) .akinon-upload-list-item{border-radius:5px;height:unset;padding:10px 20px;background-color:var(--color-ebonyClay-550);font-size:14px;font-weight:600;gap:10px;align-items:center}.akinon-upload-wrapper .akinon-upload-list:not(.akinon-upload-list-picture-card) .akinon-upload-list-item .akinon-upload-list-item-name{white-space:normal;word-break:break-all}.akinon-upload-wrapper.akinon-upload-picture-card-wrapper .akinon-upload-list.akinon-upload-list-picture-card .akinon-upload-list-item{border-radius:0;border:2px solid var(--color-neutral-350)}.akinon-upload-wrapper.akinon-upload-picture-card-wrapper .akinon-upload-list.akinon-upload-list-picture-card .akinon-upload-list-item-error{border-color:var(--color-red-500)}.akinon-upload-wrapper.akinon-upload-picture-card-wrapper .akinon-upload-list.akinon-upload-list-picture-card .akinon-upload-list-item:before{display:none}.akinon-upload-wrapper.akinon-upload-picture-card-wrapper .akinon-upload-list.akinon-upload-list-picture-card .akinon-upload-list-item-actions{cursor:pointer;width:fit-content;inset:auto 6px 6px auto;background-color:#ffffff80;display:flex;align-items:center;justify-content:center;border-radius:100%;height:18px;width:18px;font-size:12px}.akinon-upload-wrapper .akinon-upload-list .akinon-upload-list-item .akinon-upload-list-item-actions .akinon-upload-list-item-action{opacity:1}.akinon-layout-footer{text-align:center}.akinon-layout-header{padding-inline:1rem;height:3.5rem;border-bottom:1px solid var(--color-gray-900);display:flex;align-items:center;justify-content:space-between;line-height:unset;position:sticky;top:0;z-index:999}.akinon-layout-sider{position:sticky;top:0;height:100svh;overflow-y:auto}.akinon-layout-sider.left{inset-inline-start:0}.akinon-layout-sider.right{inset-inline-end:0}.akinon-layout-sider .logo-container{display:flex;flex-direction:row;align-items:center;height:3.5rem;gap:.8rem;background-color:var(--color-ebonyClay-600);padding-inline:1.5rem}.akinon-layout-sider .logo-container.collapsed{justify-content:center;padding-inline:1rem}.akinon-layout-sider .logo-container.collapsed .brand-image{display:none}.log-messages{display:flex;flex-direction:column;padding:.75rem}.log-messages .akinon-space-item{width:100%}.log-messages-item{border-radius:10px;border:1px solid #fff;padding:.75rem;display:flex;flex-direction:column;margin-bottom:.25rem}.log-messages-item>div{width:100%}.flex{display:flex}.items-center{align-items:center}.mr-2{margin-right:.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-8{padding-top:2rem;padding-bottom:2rem}
|
package/dist/index.html
CHANGED
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
box-sizing: border-box;
|
|
25
25
|
}
|
|
26
26
|
</style>
|
|
27
|
-
<script type="module" crossorigin src="/assets/index-
|
|
28
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
27
|
+
<script type="module" crossorigin src="/assets/index-cI-pE1VB.js"></script>
|
|
28
|
+
<link rel="stylesheet" crossorigin href="/assets/index-tRJwBFdH.css">
|
|
29
29
|
</head>
|
|
30
30
|
<body>
|
|
31
31
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/ui-shell-dev",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Development shell application for Akinon UI Protocol plugins",
|
|
6
6
|
"type": "module",
|
|
@@ -14,11 +14,7 @@
|
|
|
14
14
|
"tsconfig.json"
|
|
15
15
|
],
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@akinon/ui-
|
|
18
|
-
"@akinon/ui-layout": "^1.0.0",
|
|
19
|
-
"@akinon/ui-menu": "^1.0.0",
|
|
20
|
-
"@akinon/ui-breadcrumb": "^1.0.0",
|
|
21
|
-
"@akinon/ui-button": "^1.0.0",
|
|
17
|
+
"@akinon/ui-react": "^1.1.0",
|
|
22
18
|
"@akinon/icons": "^1.0.0",
|
|
23
19
|
"@akinon/fonts-jost-variable": "^1.0.0",
|
|
24
20
|
"react": "^18.2.0",
|
|
@@ -28,13 +24,13 @@
|
|
|
28
24
|
"vite": "^5.2.0"
|
|
29
25
|
},
|
|
30
26
|
"devDependencies": {
|
|
31
|
-
"@akinon/typescript-config": "^1.0.0",
|
|
32
27
|
"@types/react": "^18.2.66",
|
|
33
28
|
"@types/react-dom": "^18.2.22",
|
|
34
29
|
"@types/express": "^4.17.21",
|
|
35
30
|
"@types/cors": "^2.8.17",
|
|
36
31
|
"@vitejs/plugin-react-swc": "^3.7.2",
|
|
37
|
-
"typescript": "^5.2.2"
|
|
32
|
+
"typescript": "^5.2.2",
|
|
33
|
+
"@akinon/typescript-config": "1.0.1"
|
|
38
34
|
},
|
|
39
35
|
"engines": {
|
|
40
36
|
"node": ">=18"
|
|
@@ -1,64 +1,78 @@
|
|
|
1
|
-
import
|
|
1
|
+
import './layout.scss';
|
|
2
|
+
|
|
3
|
+
import { Layout, Space, Text } from '@akinon/ui-react';
|
|
2
4
|
import React, { useState } from 'react';
|
|
3
5
|
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
+
import type { ShellConfig, ViewType } from '../types';
|
|
7
|
+
import { AppRouter } from './AppRouter';
|
|
6
8
|
import { Header } from './Header';
|
|
7
|
-
import {
|
|
8
|
-
import { Sidebar } from './Sidebar';
|
|
9
|
+
import { Navigation } from './Navigation';
|
|
9
10
|
|
|
10
11
|
interface AppLayoutProps {
|
|
11
12
|
config: ShellConfig;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export const AppLayout: React.FC<AppLayoutProps> = ({ config }) => {
|
|
15
|
-
const [
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
config.sidebar?.items || getSidebarItems(config.shell.theme);
|
|
16
|
+
const [leftSiderCollapsed, setLeftSiderCollapsed] = useState(false);
|
|
17
|
+
const [rightSiderCollapsed, setRightSiderCollapsed] = useState(true);
|
|
18
|
+
const [currentView, setCurrentView] = useState<ViewType>('welcome');
|
|
19
19
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
: theme.sidebar.width;
|
|
20
|
+
const toggleLeftSider = () => setLeftSiderCollapsed(!leftSiderCollapsed);
|
|
21
|
+
const toggleRightSider = () => setRightSiderCollapsed(!rightSiderCollapsed);
|
|
23
22
|
|
|
24
23
|
return (
|
|
25
|
-
<Layout
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<Layout
|
|
31
|
-
style={{ marginLeft: sidebarWidth, transition: 'margin-left 0.2s' }}
|
|
24
|
+
<Layout>
|
|
25
|
+
<Layout.Sider
|
|
26
|
+
collapsed={leftSiderCollapsed}
|
|
27
|
+
brandImageUrl="/akinon-logo.svg"
|
|
28
|
+
iconUrl="/akinon-favicon.svg"
|
|
32
29
|
>
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
onToggleCollapse={() => setCollapsed(!collapsed)}
|
|
38
|
-
sidebarWidth={sidebarWidth}
|
|
30
|
+
<Navigation
|
|
31
|
+
config={config}
|
|
32
|
+
currentView={currentView}
|
|
33
|
+
onNavigate={setCurrentView}
|
|
39
34
|
/>
|
|
35
|
+
</Layout.Sider>
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.06)'
|
|
56
|
-
}}
|
|
57
|
-
>
|
|
58
|
-
<PluginLoader config={config} />
|
|
59
|
-
</div>
|
|
37
|
+
<Layout>
|
|
38
|
+
<Layout.Header>
|
|
39
|
+
<Header
|
|
40
|
+
onToggleLeftSider={toggleLeftSider}
|
|
41
|
+
onToggleRightSider={toggleRightSider}
|
|
42
|
+
/>
|
|
43
|
+
</Layout.Header>
|
|
44
|
+
|
|
45
|
+
<Layout.Content>
|
|
46
|
+
<AppRouter
|
|
47
|
+
config={config}
|
|
48
|
+
currentView={currentView}
|
|
49
|
+
onNavigate={setCurrentView}
|
|
50
|
+
/>
|
|
60
51
|
</Layout.Content>
|
|
52
|
+
|
|
53
|
+
<Layout.Footer>
|
|
54
|
+
{config.shell.title} © {new Date().getFullYear()} Created by
|
|
55
|
+
Akinon
|
|
56
|
+
</Layout.Footer>
|
|
61
57
|
</Layout>
|
|
58
|
+
|
|
59
|
+
{/* Development logs/notifications area */}
|
|
60
|
+
<Layout.Sider
|
|
61
|
+
collapsed={rightSiderCollapsed}
|
|
62
|
+
collapsedWidth={0}
|
|
63
|
+
position="right"
|
|
64
|
+
>
|
|
65
|
+
<Space className="log-messages">
|
|
66
|
+
<div className="log-messages-item">
|
|
67
|
+
<Text type="success">Development server running</Text>
|
|
68
|
+
<Text>{new Date().toLocaleTimeString()}</Text>
|
|
69
|
+
</div>
|
|
70
|
+
<div className="log-messages-item">
|
|
71
|
+
<Text>Plugin URL: {config.plugin.url}</Text>
|
|
72
|
+
<Text>Type: {config.plugin.type}</Text>
|
|
73
|
+
</div>
|
|
74
|
+
</Space>
|
|
75
|
+
</Layout.Sider>
|
|
62
76
|
</Layout>
|
|
63
77
|
);
|
|
64
78
|
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { ShellConfig, ViewType } from '../types';
|
|
4
|
+
import { PluginLoader } from './PluginLoader';
|
|
5
|
+
import { WelcomePage } from './WelcomePage';
|
|
6
|
+
import { PluginTestPage } from './PluginTestPage';
|
|
7
|
+
|
|
8
|
+
interface AppRouterProps {
|
|
9
|
+
config: ShellConfig;
|
|
10
|
+
currentView: ViewType;
|
|
11
|
+
onNavigate: (view: ViewType) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const AppRouter: React.FC<AppRouterProps> = ({ config, currentView, onNavigate }) => {
|
|
15
|
+
const renderView = () => {
|
|
16
|
+
switch (currentView) {
|
|
17
|
+
case 'welcome':
|
|
18
|
+
return (
|
|
19
|
+
<div className="px-8 py-8">
|
|
20
|
+
<WelcomePage config={config} onNavigate={onNavigate} />
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
case 'app':
|
|
24
|
+
return <PluginLoader config={config} />;
|
|
25
|
+
case 'plugin-test':
|
|
26
|
+
return (
|
|
27
|
+
<div className="px-8 py-8">
|
|
28
|
+
<PluginTestPage config={config} onNavigate={onNavigate} />
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
default:
|
|
32
|
+
return (
|
|
33
|
+
<div className="px-8 py-8">
|
|
34
|
+
<WelcomePage config={config} onNavigate={onNavigate} />
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return renderView();
|
|
41
|
+
};
|
|
@@ -1,66 +1,42 @@
|
|
|
1
|
-
import { Button } from '@akinon/ui-
|
|
2
|
-
import { Layout } from '@akinon/ui-layout';
|
|
1
|
+
import { Button } from '@akinon/ui-react';
|
|
3
2
|
import React from 'react';
|
|
4
3
|
|
|
5
|
-
import
|
|
4
|
+
import { HeaderSearch } from './HeaderSearch';
|
|
5
|
+
import { HeaderUser } from './HeaderUser';
|
|
6
6
|
|
|
7
7
|
interface HeaderProps {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
collapsed: boolean;
|
|
11
|
-
onToggleCollapse: () => void;
|
|
12
|
-
sidebarWidth: number;
|
|
8
|
+
onToggleLeftSider: () => void;
|
|
9
|
+
onToggleRightSider: () => void;
|
|
13
10
|
}
|
|
14
11
|
|
|
15
|
-
export const Header: React.FC<HeaderProps> = ({
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
collapsed,
|
|
19
|
-
onToggleCollapse,
|
|
20
|
-
sidebarWidth
|
|
12
|
+
export const Header: React.FC<HeaderProps> = ({
|
|
13
|
+
onToggleLeftSider,
|
|
14
|
+
onToggleRightSider
|
|
21
15
|
}) => {
|
|
22
16
|
return (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
backgroundColor: theme.colors.header,
|
|
26
|
-
padding: '0 24px',
|
|
27
|
-
height: theme.header.height,
|
|
28
|
-
lineHeight: `${theme.header.height}px`,
|
|
29
|
-
position: 'fixed',
|
|
30
|
-
top: 0,
|
|
31
|
-
right: 0,
|
|
32
|
-
left: sidebarWidth,
|
|
33
|
-
zIndex: 100,
|
|
34
|
-
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.06)',
|
|
35
|
-
display: 'flex',
|
|
36
|
-
alignItems: 'center',
|
|
37
|
-
justifyContent: 'space-between',
|
|
38
|
-
transition: 'left 0.2s'
|
|
39
|
-
}}
|
|
40
|
-
>
|
|
41
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
|
|
17
|
+
<>
|
|
18
|
+
<div className="flex items-center">
|
|
42
19
|
<Button
|
|
43
20
|
type="text"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
21
|
+
icon="outdent"
|
|
22
|
+
onClick={onToggleLeftSider}
|
|
23
|
+
className="mr-2"
|
|
24
|
+
/>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div>
|
|
28
|
+
<HeaderSearch />
|
|
52
29
|
</div>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}}
|
|
30
|
+
|
|
31
|
+
<div className="flex items-center">
|
|
32
|
+
<HeaderUser />
|
|
33
|
+
<Button
|
|
34
|
+
type="text"
|
|
35
|
+
icon="bell"
|
|
36
|
+
onClick={onToggleRightSider}
|
|
37
|
+
className="mr-2"
|
|
62
38
|
/>
|
|
63
39
|
</div>
|
|
64
|
-
|
|
40
|
+
</>
|
|
65
41
|
);
|
|
66
|
-
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Avatar, Dropdown } from '@akinon/ui-react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
export const HeaderUser: React.FC = () => {
|
|
5
|
+
return (
|
|
6
|
+
<Dropdown
|
|
7
|
+
menu={{
|
|
8
|
+
items: [
|
|
9
|
+
{ key: 'profile', label: 'Profile' },
|
|
10
|
+
{ key: 'settings', label: 'Settings' },
|
|
11
|
+
{ key: 'divider', type: 'divider' },
|
|
12
|
+
{ key: 'logout', label: 'Logout' }
|
|
13
|
+
]
|
|
14
|
+
}}
|
|
15
|
+
placement="bottomRight"
|
|
16
|
+
>
|
|
17
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
|
18
|
+
<Avatar size="small">Dev</Avatar>
|
|
19
|
+
<span style={{ fontSize: '14px' }}>Developer</span>
|
|
20
|
+
</div>
|
|
21
|
+
</Dropdown>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Menu, IMenuProps } from '@akinon/ui-react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { ShellConfig, ViewType } from '../types';
|
|
5
|
+
|
|
6
|
+
interface NavigationProps {
|
|
7
|
+
config: ShellConfig;
|
|
8
|
+
currentView: ViewType;
|
|
9
|
+
onNavigate: (view: ViewType) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Navigation: React.FC<NavigationProps> = ({
|
|
13
|
+
config,
|
|
14
|
+
currentView,
|
|
15
|
+
onNavigate
|
|
16
|
+
}) => {
|
|
17
|
+
const getMenuItems = () => {
|
|
18
|
+
const items = [
|
|
19
|
+
{
|
|
20
|
+
key: 'welcome',
|
|
21
|
+
label: 'Welcome',
|
|
22
|
+
icon: 'home'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
key: 'development',
|
|
26
|
+
label: 'Development',
|
|
27
|
+
icon: 'build',
|
|
28
|
+
children: [
|
|
29
|
+
{
|
|
30
|
+
key: 'app',
|
|
31
|
+
label: config.plugin.name,
|
|
32
|
+
icon: config.plugin.type === 'fullpage' ? 'desktop' : 'appstore'
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
// Add plugin test page for plugin apps
|
|
39
|
+
if (config.plugin.type === 'plugin') {
|
|
40
|
+
items[1].children.push({
|
|
41
|
+
key: 'plugin-test',
|
|
42
|
+
label: 'Plugin Test',
|
|
43
|
+
icon: 'experiment'
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Add static menu items to match playground
|
|
48
|
+
items.push(
|
|
49
|
+
{
|
|
50
|
+
key: 'settings',
|
|
51
|
+
label: 'Settings',
|
|
52
|
+
icon: 'setting'
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
key: 'help',
|
|
56
|
+
label: 'Help & Support',
|
|
57
|
+
icon: 'question-circle'
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return items;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const onClick: IMenuProps['onClick'] = (e: any) => {
|
|
65
|
+
if (e.key === 'welcome' || e.key === 'app' || e.key === 'plugin-test') {
|
|
66
|
+
onNavigate(e.key as ViewType);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div>
|
|
72
|
+
<Menu
|
|
73
|
+
mode="inline"
|
|
74
|
+
selectedKeys={[currentView]}
|
|
75
|
+
defaultOpenKeys={['development']}
|
|
76
|
+
items={getMenuItems()}
|
|
77
|
+
onClick={onClick}
|
|
78
|
+
theme="dark"
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { Button, Card } from '@akinon/ui-react';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import type { ShellConfig } from '../types';
|
|
5
|
+
import { PluginLoader } from './PluginLoader';
|
|
6
|
+
|
|
7
|
+
interface PluginTestPageProps {
|
|
8
|
+
config: ShellConfig;
|
|
9
|
+
onNavigate: (view: 'welcome' | 'app' | 'plugin-test') => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type PlaceholderType = 'sidebar' | 'header' | 'content' | 'modal' | 'drawer';
|
|
13
|
+
|
|
14
|
+
const placeholderConfigs = {
|
|
15
|
+
sidebar: {
|
|
16
|
+
name: 'Sidebar Plugin',
|
|
17
|
+
description: 'Plugin rendered in sidebar area',
|
|
18
|
+
dimensions: '300px × 100%',
|
|
19
|
+
containerStyle: {
|
|
20
|
+
width: '300px',
|
|
21
|
+
height: '400px',
|
|
22
|
+
border: '2px dashed #1890ff',
|
|
23
|
+
borderRadius: '4px',
|
|
24
|
+
backgroundColor: '#f0f8ff'
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
header: {
|
|
28
|
+
name: 'Header Plugin',
|
|
29
|
+
description: 'Plugin rendered in header area',
|
|
30
|
+
dimensions: '100% × 60px',
|
|
31
|
+
containerStyle: {
|
|
32
|
+
width: '100%',
|
|
33
|
+
height: '60px',
|
|
34
|
+
border: '2px dashed #52c41a',
|
|
35
|
+
borderRadius: '4px',
|
|
36
|
+
backgroundColor: '#f6ffed'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
content: {
|
|
40
|
+
name: 'Content Plugin',
|
|
41
|
+
description: 'Plugin rendered in main content area',
|
|
42
|
+
dimensions: '100% × 400px',
|
|
43
|
+
containerStyle: {
|
|
44
|
+
width: '100%',
|
|
45
|
+
height: '400px',
|
|
46
|
+
border: '2px dashed #722ed1',
|
|
47
|
+
borderRadius: '4px',
|
|
48
|
+
backgroundColor: '#f9f0ff'
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
modal: {
|
|
52
|
+
name: 'Modal Plugin',
|
|
53
|
+
description: 'Plugin rendered in modal overlay',
|
|
54
|
+
dimensions: '600px × 400px',
|
|
55
|
+
containerStyle: {
|
|
56
|
+
width: '600px',
|
|
57
|
+
height: '400px',
|
|
58
|
+
border: '2px dashed #fa541c',
|
|
59
|
+
borderRadius: '4px',
|
|
60
|
+
backgroundColor: '#fff7e6'
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
drawer: {
|
|
64
|
+
name: 'Drawer Plugin',
|
|
65
|
+
description: 'Plugin rendered in drawer panel',
|
|
66
|
+
dimensions: '400px × 100%',
|
|
67
|
+
containerStyle: {
|
|
68
|
+
width: '400px',
|
|
69
|
+
height: '300px',
|
|
70
|
+
border: '2px dashed #eb2f96',
|
|
71
|
+
borderRadius: '4px',
|
|
72
|
+
backgroundColor: '#fff0f6'
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const PluginTestPage: React.FC<PluginTestPageProps> = ({ config, onNavigate }) => {
|
|
78
|
+
const [selectedPlaceholder, setSelectedPlaceholder] = useState<PlaceholderType>('content');
|
|
79
|
+
const [isPluginVisible, setIsPluginVisible] = useState(false);
|
|
80
|
+
|
|
81
|
+
const currentConfig = placeholderConfigs[selectedPlaceholder];
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div style={{ padding: '24px' }}>
|
|
85
|
+
<div style={{ marginBottom: '24px', display: 'flex', alignItems: 'center', gap: '16px' }}>
|
|
86
|
+
<Button onClick={() => onNavigate('welcome')}>← Back to Welcome</Button>
|
|
87
|
+
<h2 style={{ margin: 0 }}>Plugin Integration Test</h2>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div style={{ display: 'grid', gridTemplateColumns: '300px 1fr', gap: '24px' }}>
|
|
91
|
+
<Card title="Test Configuration" size="small">
|
|
92
|
+
<div style={{ marginBottom: '16px' }}>
|
|
93
|
+
<label style={{ display: 'block', marginBottom: '8px', fontWeight: '500' }}>
|
|
94
|
+
Placeholder Type:
|
|
95
|
+
</label>
|
|
96
|
+
<select
|
|
97
|
+
value={selectedPlaceholder}
|
|
98
|
+
onChange={(e) => setSelectedPlaceholder(e.target.value as PlaceholderType)}
|
|
99
|
+
style={{
|
|
100
|
+
width: '100%',
|
|
101
|
+
padding: '8px 12px',
|
|
102
|
+
border: '1px solid #d9d9d9',
|
|
103
|
+
borderRadius: '6px',
|
|
104
|
+
fontSize: '14px',
|
|
105
|
+
backgroundColor: 'white'
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
{Object.entries(placeholderConfigs).map(([key, config]) => (
|
|
109
|
+
<option key={key} value={key}>
|
|
110
|
+
{config.name}
|
|
111
|
+
</option>
|
|
112
|
+
))}
|
|
113
|
+
</select>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div style={{ marginBottom: '16px' }}>
|
|
117
|
+
<label style={{ display: 'block', marginBottom: '8px', fontWeight: '500' }}>
|
|
118
|
+
Plugin Status:
|
|
119
|
+
</label>
|
|
120
|
+
<span style={{
|
|
121
|
+
padding: '4px 8px',
|
|
122
|
+
borderRadius: '4px',
|
|
123
|
+
fontSize: '12px',
|
|
124
|
+
backgroundColor: isPluginVisible ? '#f6ffed' : '#f5f5f5',
|
|
125
|
+
color: isPluginVisible ? '#52c41a' : '#666',
|
|
126
|
+
border: `1px solid ${isPluginVisible ? '#b7eb8f' : '#d9d9d9'}`
|
|
127
|
+
}}>
|
|
128
|
+
{isPluginVisible ? 'Visible' : 'Hidden'}
|
|
129
|
+
</span>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div style={{ marginBottom: '16px' }}>
|
|
133
|
+
<Button
|
|
134
|
+
type="primary"
|
|
135
|
+
block
|
|
136
|
+
onClick={() => setIsPluginVisible(!isPluginVisible)}
|
|
137
|
+
>
|
|
138
|
+
{isPluginVisible ? 'Hide Plugin' : 'Show Plugin'}
|
|
139
|
+
</Button>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div style={{ fontSize: '12px', color: '#666' }}>
|
|
143
|
+
<p><strong>Dimensions:</strong> {currentConfig.dimensions}</p>
|
|
144
|
+
<p><strong>Description:</strong> {currentConfig.description}</p>
|
|
145
|
+
</div>
|
|
146
|
+
</Card>
|
|
147
|
+
|
|
148
|
+
<Card title={`${currentConfig.name} Preview`} size="small">
|
|
149
|
+
<div style={{
|
|
150
|
+
padding: '24px',
|
|
151
|
+
backgroundColor: '#fafafa',
|
|
152
|
+
borderRadius: '4px',
|
|
153
|
+
minHeight: '500px',
|
|
154
|
+
display: 'flex',
|
|
155
|
+
alignItems: 'center',
|
|
156
|
+
justifyContent: 'center'
|
|
157
|
+
}}>
|
|
158
|
+
<div style={currentConfig.containerStyle}>
|
|
159
|
+
{isPluginVisible ? (
|
|
160
|
+
<div style={{ width: '100%', height: '100%', position: 'relative' }}>
|
|
161
|
+
<div style={{
|
|
162
|
+
position: 'absolute',
|
|
163
|
+
top: '8px',
|
|
164
|
+
left: '8px',
|
|
165
|
+
right: '8px',
|
|
166
|
+
backgroundColor: 'rgba(0,0,0,0.7)',
|
|
167
|
+
color: 'white',
|
|
168
|
+
padding: '4px 8px',
|
|
169
|
+
borderRadius: '2px',
|
|
170
|
+
fontSize: '10px',
|
|
171
|
+
zIndex: 10
|
|
172
|
+
}}>
|
|
173
|
+
Plugin: {config.plugin.name} ({selectedPlaceholder})
|
|
174
|
+
</div>
|
|
175
|
+
<PluginLoader config={config} />
|
|
176
|
+
</div>
|
|
177
|
+
) : (
|
|
178
|
+
<div style={{
|
|
179
|
+
display: 'flex',
|
|
180
|
+
flexDirection: 'column',
|
|
181
|
+
alignItems: 'center',
|
|
182
|
+
justifyContent: 'center',
|
|
183
|
+
height: '100%',
|
|
184
|
+
color: '#999',
|
|
185
|
+
textAlign: 'center'
|
|
186
|
+
}}>
|
|
187
|
+
<div style={{ fontSize: '48px', marginBottom: '8px' }}>📦</div>
|
|
188
|
+
<p style={{ margin: 0, fontSize: '14px' }}>
|
|
189
|
+
{currentConfig.name} Placeholder
|
|
190
|
+
</p>
|
|
191
|
+
<p style={{ margin: '4px 0 0 0', fontSize: '12px' }}>
|
|
192
|
+
Click "Show Plugin" to load
|
|
193
|
+
</p>
|
|
194
|
+
</div>
|
|
195
|
+
)}
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</Card>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<Card
|
|
202
|
+
title="Integration Notes"
|
|
203
|
+
style={{ marginTop: '24px' }}
|
|
204
|
+
size="small"
|
|
205
|
+
>
|
|
206
|
+
<div style={{ fontSize: '14px', lineHeight: '1.5' }}>
|
|
207
|
+
<p>
|
|
208
|
+
This test environment simulates how your plugin will appear in different
|
|
209
|
+
placeholder configurations within host applications. Each placeholder type
|
|
210
|
+
represents a different integration scenario:
|
|
211
|
+
</p>
|
|
212
|
+
<ul>
|
|
213
|
+
<li><strong>Sidebar:</strong> Narrow vertical space, typically for navigation or quick actions</li>
|
|
214
|
+
<li><strong>Header:</strong> Horizontal space at top, usually for search or user controls</li>
|
|
215
|
+
<li><strong>Content:</strong> Main content area with full width and flexible height</li>
|
|
216
|
+
<li><strong>Modal:</strong> Overlay dialog for focused interactions</li>
|
|
217
|
+
<li><strong>Drawer:</strong> Slide-out panel for secondary content or forms</li>
|
|
218
|
+
</ul>
|
|
219
|
+
</div>
|
|
220
|
+
</Card>
|
|
221
|
+
</div>
|
|
222
|
+
);
|
|
223
|
+
};
|