@bbki.ng/site 5.8.3 → 5.8.4
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/CHANGELOG.md +8 -0
- package/package.json +2 -2
- package/src/app/app.tsx +4 -8
- package/src/app/components/BaseLayout.tsx +2 -3
- package/src/app/components/cover/index.tsx +17 -15
- package/src/app/hooks/use_plugin_entries.ts +17 -27
- package/src/core/plugin-system/services/systemUIService.ts +28 -34
- package/src/core/shared-service/contract/IUIService.ts +2 -5
- package/src/plugins/store/components/storeIcon.tsx +16 -0
- package/src/plugins/store/index.ts +3 -1
- package/src/types/slots.ts +0 -17
- package/src/utils/index.tsx +0 -20
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bbki.ng/site",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.4",
|
|
4
4
|
"description": "code behind bbki.ng",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"react-router-dom": "6",
|
|
16
16
|
"sonner": "^2.0.7",
|
|
17
17
|
"swr": "^2.2.5",
|
|
18
|
-
"@bbki.ng/ui": "0.2.
|
|
18
|
+
"@bbki.ng/ui": "0.2.20"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@eslint/compat": "^1.0.0",
|
package/src/app/app.tsx
CHANGED
|
@@ -3,7 +3,6 @@ import { Route, Routes } from 'react-router-dom';
|
|
|
3
3
|
|
|
4
4
|
import { BotRedirect } from '#/app/components/bot';
|
|
5
5
|
import { BBContext } from '#/app/context/bbcontext';
|
|
6
|
-
import { Slot } from '#/core/components/SlotComp';
|
|
7
6
|
import { usePlugins } from '#/core/hooks/use_plugins';
|
|
8
7
|
import { Cover } from '#/app/components/cover';
|
|
9
8
|
import { SWR } from '#/app/swr';
|
|
@@ -20,13 +19,10 @@ const AppRoutes = () => {
|
|
|
20
19
|
<Route path="/" element={<BaseLayout />}>
|
|
21
20
|
<Route index element={<Cover />} />
|
|
22
21
|
<Route path="bot" element={<BotRedirect />} />
|
|
23
|
-
{pluginEntries?.map(route =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
element={<Slot name="route" data={route} />}
|
|
28
|
-
/>
|
|
29
|
-
))}
|
|
22
|
+
{pluginEntries?.map(route => {
|
|
23
|
+
const Com = route.pageComponent;
|
|
24
|
+
return <Route key={route.path} path={`${route.path}/*`} element={<Com data={route} />} />;
|
|
25
|
+
})}
|
|
30
26
|
<Route path="*" element={null} />
|
|
31
27
|
</Route>
|
|
32
28
|
</Routes>
|
|
@@ -14,9 +14,7 @@ export const BaseLayout = () => {
|
|
|
14
14
|
|
|
15
15
|
const nav = useNavigate();
|
|
16
16
|
|
|
17
|
-
const defaultLogo = (
|
|
18
|
-
<Logo className="mr-2 cursor-pointer hover:opacity-80" onClick={() => nav('/')} />
|
|
19
|
-
);
|
|
17
|
+
const defaultLogo = <Logo className="cursor-pointer hover:opacity-80" onClick={() => nav('/')} />;
|
|
20
18
|
|
|
21
19
|
return (
|
|
22
20
|
<>
|
|
@@ -31,6 +29,7 @@ export const BaseLayout = () => {
|
|
|
31
29
|
paddingTop: 'calc(var(--safe-top) + 4px)',
|
|
32
30
|
transition: 'all .2s ease-in-out',
|
|
33
31
|
}}
|
|
32
|
+
rightElement={<Slot name="nav-right" data={paths} />}
|
|
34
33
|
/>
|
|
35
34
|
}
|
|
36
35
|
main={
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import { LinkProps, LinkListProps, LinkList } from '@bbki.ng/ui';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { usePluginEntries } from '#/app/hooks/use_plugin_entries';
|
|
5
|
+
import { useMiddlewareTransformedData } from '#/core/hooks/useMiddlewareTransData';
|
|
5
6
|
|
|
6
7
|
const CenterLinkList = (props: LinkListProps) => {
|
|
7
8
|
return (
|
|
@@ -11,25 +12,26 @@ const CenterLinkList = (props: LinkListProps) => {
|
|
|
11
12
|
);
|
|
12
13
|
};
|
|
13
14
|
|
|
14
|
-
const baseEntries: Array<LinkProps> = [];
|
|
15
|
-
|
|
16
15
|
export const Cover = (_: { className?: string }) => {
|
|
17
|
-
const
|
|
16
|
+
const pluginEntries = usePluginEntries();
|
|
18
17
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
const entries: Array<LinkProps> = React.useMemo(() => {
|
|
19
|
+
return pluginEntries
|
|
20
|
+
.filter(entry => entry.label)
|
|
21
|
+
.map(entry => ({
|
|
22
|
+
to: entry.path,
|
|
23
|
+
children: entry.label,
|
|
24
|
+
})) as Array<LinkProps>;
|
|
25
|
+
}, [pluginEntries]);
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
const transformedEntries = useMiddlewareTransformedData(
|
|
28
|
+
'transformEntryLink',
|
|
29
|
+
entries
|
|
30
|
+
) as Array<LinkProps>;
|
|
29
31
|
|
|
30
32
|
return (
|
|
31
33
|
<>
|
|
32
|
-
<CenterLinkList className="select-none" links={
|
|
34
|
+
<CenterLinkList className="select-none" links={transformedEntries} />
|
|
33
35
|
</>
|
|
34
36
|
);
|
|
35
37
|
};
|
|
@@ -1,39 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type PathRouteProps } from 'react-router-dom';
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import { SystemUIService } from '#/core/plugin-system/services/systemUIService';
|
|
4
|
+
import { IPluginEntry } from '#/types/plugin';
|
|
5
5
|
|
|
6
6
|
const protectedRoutesSet = new Set<string>(['/bot', '/', 'default']);
|
|
7
7
|
|
|
8
|
-
type PluginRoute = Omit<PathRouteProps, 'element'>;
|
|
9
|
-
|
|
10
8
|
export const usePluginEntries = () => {
|
|
11
|
-
const [
|
|
12
|
-
|
|
13
|
-
Promise.resolve([])
|
|
9
|
+
const [entries, setEntries] = useState<Array<IPluginEntry>>(() =>
|
|
10
|
+
SystemUIService.getInstance().getPluginEntries()
|
|
14
11
|
);
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const { run } = useMiddlewareRunner<Array<PluginRoute>>({
|
|
21
|
-
hookPoint: 'extendedRoutes',
|
|
22
|
-
onMiddlewareChange,
|
|
23
|
-
});
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const service = SystemUIService.getInstance();
|
|
15
|
+
setEntries(service.getPluginEntries());
|
|
24
16
|
|
|
25
|
-
|
|
17
|
+
const unsubscribe = service.subscribePluginEntryChange(() => {
|
|
18
|
+
setEntries(service.getPluginEntries());
|
|
19
|
+
});
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
return () => {
|
|
22
|
+
unsubscribe();
|
|
23
|
+
};
|
|
24
|
+
}, []);
|
|
30
25
|
|
|
31
|
-
const
|
|
32
|
-
if ('path' in route) {
|
|
33
|
-
return !protectedRoutesSet.has(route.path);
|
|
34
|
-
}
|
|
35
|
-
return false;
|
|
36
|
-
}) as Array<PluginRoute>;
|
|
26
|
+
const safeEntries = entries?.filter(entry => !protectedRoutesSet.has(entry.path));
|
|
37
27
|
|
|
38
|
-
return
|
|
28
|
+
return safeEntries ?? [];
|
|
39
29
|
};
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import { PathRouteProps } from 'react-router-dom';
|
|
2
|
-
import { LinkProps } from '@bbki.ng/ui';
|
|
3
|
-
|
|
4
|
-
import { buildEntrySlotCom } from '#/utils';
|
|
5
1
|
import { createUIService } from '#/core/shared-service/factory/createUIService';
|
|
2
|
+
import { createEventBus } from '#/core/utils/eventBus';
|
|
6
3
|
import {
|
|
7
4
|
ISystemUIService,
|
|
8
5
|
SystemDataHookPoint,
|
|
@@ -15,6 +12,10 @@ const baseService = createUIService<SystemSlotName, SystemDataHookPoint>('system
|
|
|
15
12
|
export class SystemUIService implements ISystemUIService {
|
|
16
13
|
private static instance: SystemUIService;
|
|
17
14
|
|
|
15
|
+
private pluginEntries = new Map<string, IPluginEntry & { pluginId: PluginID }>();
|
|
16
|
+
|
|
17
|
+
private entryBus = createEventBus<{ pluginEntriesChanged: void }>();
|
|
18
|
+
|
|
18
19
|
private constructor() {}
|
|
19
20
|
|
|
20
21
|
static getInstance(): SystemUIService {
|
|
@@ -26,7 +27,6 @@ export class SystemUIService implements ISystemUIService {
|
|
|
26
27
|
|
|
27
28
|
registerSlot = baseService.registerSlot.bind(baseService);
|
|
28
29
|
registerMiddleware = baseService.registerMiddleware.bind(baseService);
|
|
29
|
-
unregisterAllByPluginId = baseService.unregisterAllByPluginId.bind(baseService);
|
|
30
30
|
getSlotEntries = baseService.getSlotEntries.bind(baseService);
|
|
31
31
|
getComponents = baseService.getComponents.bind(baseService);
|
|
32
32
|
subscribeSlotChange = baseService.subscribeSlotChange.bind(baseService);
|
|
@@ -34,37 +34,31 @@ export class SystemUIService implements ISystemUIService {
|
|
|
34
34
|
runMiddlewares = baseService.runMiddlewares.bind(baseService);
|
|
35
35
|
|
|
36
36
|
registerPluginEntry: (entry: IPluginEntry, id: PluginID) => void = (entry, id) => {
|
|
37
|
-
this.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return [
|
|
41
|
-
...routes,
|
|
42
|
-
{
|
|
43
|
-
path: entry.path,
|
|
44
|
-
},
|
|
45
|
-
];
|
|
46
|
-
},
|
|
47
|
-
id,
|
|
48
|
-
10
|
|
49
|
-
);
|
|
37
|
+
this.pluginEntries.set(entry.path, { ...entry, pluginId: id });
|
|
38
|
+
this.entryBus.emit('pluginEntriesChanged', undefined);
|
|
39
|
+
};
|
|
50
40
|
|
|
51
|
-
|
|
41
|
+
getPluginEntries = (): Array<IPluginEntry & { pluginId: PluginID }> => {
|
|
42
|
+
return Array.from(this.pluginEntries.values());
|
|
43
|
+
};
|
|
52
44
|
|
|
53
|
-
|
|
45
|
+
subscribePluginEntryChange = (listener: () => void): (() => void) => {
|
|
46
|
+
return this.entryBus.on('pluginEntriesChanged', listener);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
unregisterAllByPluginId: (pluginId: string) => void = pluginId => {
|
|
50
|
+
baseService.unregisterAllByPluginId(pluginId);
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
id,
|
|
67
|
-
10
|
|
68
|
-
);
|
|
52
|
+
let changed = false;
|
|
53
|
+
this.pluginEntries.forEach((entry, path) => {
|
|
54
|
+
if (entry.pluginId === pluginId) {
|
|
55
|
+
this.pluginEntries.delete(path);
|
|
56
|
+
changed = true;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (changed) {
|
|
61
|
+
this.entryBus.emit('pluginEntriesChanged', undefined);
|
|
62
|
+
}
|
|
69
63
|
};
|
|
70
64
|
}
|
|
@@ -38,12 +38,9 @@ export interface IBaseUIService<T extends string, K extends string> {
|
|
|
38
38
|
runMiddlewares: <S>(point: K, data: S) => Promise<S>;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export type SystemSlotName = 'leftCol' | 'rightCol' | 'logo' | '
|
|
41
|
+
export type SystemSlotName = 'leftCol' | 'rightCol' | 'logo' | 'pageFooter' | 'nav-right';
|
|
42
42
|
|
|
43
|
-
export type SystemDataHookPoint =
|
|
44
|
-
| 'extendedRoutes'
|
|
45
|
-
| 'transformBreadcrumbPath'
|
|
46
|
-
| 'transformCoverEntry';
|
|
43
|
+
export type SystemDataHookPoint = 'transformBreadcrumbPath' | 'transformEntryLink';
|
|
47
44
|
|
|
48
45
|
export interface ISystemUIService extends IBaseUIService<SystemSlotName, SystemDataHookPoint> {
|
|
49
46
|
registerPluginEntry: (entry: IPluginEntry, id: PluginID) => void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Link } from '@bbki.ng/ui';
|
|
2
|
+
|
|
3
|
+
const CupIcon = () => (
|
|
4
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
|
|
5
|
+
<path d="M8.75 8h6.5a.75.75 0 0 1 .75.75v6.5a.75.75 0 0 1-.75.75h-6.5a.75.75 0 0 1-.75-.75v-6.5A.75.75 0 0 1 8.75 8Zm.75 6.5h5v-5h-5Z"></path>
|
|
6
|
+
<path d="M15.25 1a.75.75 0 0 1 .75.75V4h2.25c.966 0 1.75.784 1.75 1.75V8h2.25a.75.75 0 0 1 0 1.5H20v5h2.25a.75.75 0 0 1 0 1.5H20v2.25A1.75 1.75 0 0 1 18.25 20H16v2.25a.75.75 0 0 1-1.5 0V20h-5v2.25a.75.75 0 0 1-1.5 0V20H5.75A1.75 1.75 0 0 1 4 18.25V16H1.75a.75.75 0 0 1 0-1.5H4v-5H1.75a.75.75 0 0 1 0-1.5H4V5.75C4 4.784 4.784 4 5.75 4H8V1.75a.75.75 0 0 1 1.5 0V4h5V1.75a.75.75 0 0 1 .75-.75Zm3 17.5a.25.25 0 0 0 .25-.25V5.75a.25.25 0 0 0-.25-.25H5.75a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25Z"></path>
|
|
7
|
+
</svg>
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
export const StoreIcon = () => {
|
|
11
|
+
return (
|
|
12
|
+
<Link to="/store" className="flex items-center">
|
|
13
|
+
<CupIcon />
|
|
14
|
+
</Link>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -2,6 +2,7 @@ import { BBPlugin } from '#/core/plugin-system/bbplugin';
|
|
|
2
2
|
import { IHostContext } from '#/types/hostApi';
|
|
3
3
|
import { PluginID } from '#/types/plugin';
|
|
4
4
|
|
|
5
|
+
import { StoreIcon } from './components/storeIcon';
|
|
5
6
|
import { StorePage } from './components/storePage';
|
|
6
7
|
import { StoreCtx } from './context';
|
|
7
8
|
|
|
@@ -20,10 +21,11 @@ export class StorePlugin extends BBPlugin {
|
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
systemUIService.registerSlot('nav-right', StoreIcon, this.id);
|
|
25
|
+
|
|
23
26
|
systemUIService.registerPluginEntry(
|
|
24
27
|
{
|
|
25
28
|
path: '/store',
|
|
26
|
-
label: 'cd ./store',
|
|
27
29
|
pageComponent: StoreCtx.withCtx(
|
|
28
30
|
{
|
|
29
31
|
install: ctx.store?.installPlugin || (async () => false),
|
package/src/types/slots.ts
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
export type SlotName =
|
|
2
|
-
| 'leftCol'
|
|
3
|
-
| 'rightCol'
|
|
4
|
-
| 'articleActionRow'
|
|
5
|
-
| 'logo'
|
|
6
|
-
| 'route'
|
|
7
|
-
| 'pageFooter'
|
|
8
|
-
| 'articleTitle';
|
|
9
|
-
|
|
10
|
-
export type HookPoint =
|
|
11
|
-
| 'filterPosts'
|
|
12
|
-
| 'transformPostContent'
|
|
13
|
-
| 'transformBreadcrumbPath'
|
|
14
|
-
| 'transformTitleList'
|
|
15
|
-
| 'extendedRoutes'
|
|
16
|
-
| 'transformCoverEntry';
|
|
17
|
-
|
|
18
1
|
export interface IComPropsRegisteredToSlot {
|
|
19
2
|
data: unknown;
|
|
20
3
|
}
|
package/src/utils/index.tsx
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { PathRouteProps } from 'react-router-dom';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
|
|
4
|
-
import { IPluginEntry } from '#/types/plugin';
|
|
5
|
-
import { IComPropsRegisteredToSlot } from '#/types/slots';
|
|
6
|
-
|
|
7
|
-
export const buildEntrySlotCom = (entry: IPluginEntry) => {
|
|
8
|
-
const Component = (props: IComPropsRegisteredToSlot) => {
|
|
9
|
-
const route = props.data as Omit<PathRouteProps, 'element'>;
|
|
10
|
-
if (route.path === entry.path) {
|
|
11
|
-
const Com = entry.pageComponent;
|
|
12
|
-
return <Com data={props.data} />;
|
|
13
|
-
}
|
|
14
|
-
return null;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
Component.displayName = `EntrySlotCom(${entry.path})`;
|
|
18
|
-
|
|
19
|
-
return Component;
|
|
20
|
-
};
|