@teambit/scope 0.0.567 → 0.0.571
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/exceptions/component-not-found.ts +13 -0
- package/exceptions/index.ts +1 -0
- package/package-tar/teambit-scope-0.0.571.tgz +0 -0
- package/package.json +45 -60
- package/routes/action.route.ts +22 -0
- package/routes/delete.route.ts +26 -0
- package/routes/fetch.route.ts +45 -0
- package/routes/index.ts +4 -0
- package/routes/put.route.ts +37 -0
- package/scope.ui.runtime.tsx +327 -0
- package/types/asset.d.ts +29 -0
- package/types/style.d.ts +42 -0
- package/ui/components-drawer/components.drawer.tsx +56 -0
- package/ui/components-drawer/index.ts +1 -0
- package/ui/menu/index.ts +1 -0
- package/ui/menu/menu.tsx +25 -0
- package/ui/scope-overview/index.ts +1 -0
- package/ui/scope-overview/scope-overview.tsx +61 -0
- package/ui/scope.tsx +92 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BitError } from '@teambit/bit-error';
|
|
2
|
+
import { ComponentID } from '@teambit/component';
|
|
3
|
+
|
|
4
|
+
export class ComponentNotFound extends BitError {
|
|
5
|
+
constructor(
|
|
6
|
+
/**
|
|
7
|
+
* id of the missing component.
|
|
8
|
+
*/
|
|
9
|
+
id: ComponentID
|
|
10
|
+
) {
|
|
11
|
+
super(`component with id: ${id} was not found`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ComponentNotFound } from './component-not-found';
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teambit/scope",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.571",
|
|
4
4
|
"homepage": "https://bit.dev/teambit/scope/scope",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"componentId": {
|
|
7
7
|
"scope": "teambit.scope",
|
|
8
8
|
"name": "scope",
|
|
9
|
-
"version": "0.0.
|
|
9
|
+
"version": "0.0.571"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"semver": "7.3.4",
|
|
@@ -23,50 +23,50 @@
|
|
|
23
23
|
"@teambit/base-ui.surfaces.split-pane.hover-splitter": "1.0.0",
|
|
24
24
|
"@teambit/base-ui.surfaces.split-pane.split-pane": "1.0.0",
|
|
25
25
|
"@teambit/base-ui.graph.tree.recursive-tree": "1.0.0",
|
|
26
|
-
"@teambit/scope.models.scope-model": "0.0.
|
|
27
|
-
"@teambit/scope.ui.hooks.scope-context": "0.0.
|
|
28
|
-
"@teambit/component": "0.0.
|
|
29
|
-
"@teambit/logger": "0.0.
|
|
30
|
-
"@teambit/aspect-loader": "0.0.
|
|
31
|
-
"@teambit/builder": "0.0.
|
|
32
|
-
"@teambit/cli": "0.0.
|
|
33
|
-
"@teambit/compiler": "0.0.
|
|
34
|
-
"@teambit/config": "0.0.
|
|
35
|
-
"@teambit/envs": "0.0.
|
|
36
|
-
"@teambit/express": "0.0.
|
|
37
|
-
"@teambit/graphql": "0.0.
|
|
38
|
-
"@teambit/harmony.modules.requireable-component": "0.0.
|
|
39
|
-
"@teambit/isolator": "0.0.
|
|
40
|
-
"@teambit/legacy-bit-id": "0.0.
|
|
41
|
-
"@teambit/ui": "0.0.
|
|
42
|
-
"@teambit/workspace.modules.match-pattern": "0.0.
|
|
43
|
-
"@teambit/command-bar": "0.0.
|
|
44
|
-
"@teambit/component-tree": "0.0.
|
|
26
|
+
"@teambit/scope.models.scope-model": "0.0.82",
|
|
27
|
+
"@teambit/scope.ui.hooks.scope-context": "0.0.82",
|
|
28
|
+
"@teambit/component": "0.0.571",
|
|
29
|
+
"@teambit/logger": "0.0.483",
|
|
30
|
+
"@teambit/aspect-loader": "0.0.571",
|
|
31
|
+
"@teambit/builder": "0.0.571",
|
|
32
|
+
"@teambit/cli": "0.0.395",
|
|
33
|
+
"@teambit/compiler": "0.0.571",
|
|
34
|
+
"@teambit/config": "0.0.407",
|
|
35
|
+
"@teambit/envs": "0.0.571",
|
|
36
|
+
"@teambit/express": "0.0.487",
|
|
37
|
+
"@teambit/graphql": "0.0.571",
|
|
38
|
+
"@teambit/harmony.modules.requireable-component": "0.0.470",
|
|
39
|
+
"@teambit/isolator": "0.0.571",
|
|
40
|
+
"@teambit/legacy-bit-id": "0.0.384",
|
|
41
|
+
"@teambit/ui": "0.0.571",
|
|
42
|
+
"@teambit/workspace.modules.match-pattern": "0.0.474",
|
|
43
|
+
"@teambit/command-bar": "0.0.571",
|
|
44
|
+
"@teambit/component-tree": "0.0.381",
|
|
45
45
|
"@teambit/design.ui.surfaces.menu.link-item": "0.0.374",
|
|
46
|
-
"@teambit/react-router": "0.0.
|
|
47
|
-
"@teambit/sidebar": "0.0.
|
|
48
|
-
"@teambit/ui-foundation.ui.main-dropdown": "0.0.
|
|
49
|
-
"@teambit/ui-foundation.ui.menu": "0.0.
|
|
50
|
-
"@teambit/ui-foundation.ui.react-router.slot-router": "0.0.
|
|
51
|
-
"@teambit/bit-error": "0.0.
|
|
52
|
-
"@teambit/ui-foundation.ui.constants.z-indexes": "0.0.
|
|
53
|
-
"@teambit/scope.ui.hooks.use-scope": "0.0.
|
|
54
|
-
"@teambit/ui-foundation.ui.buttons.collapser": "0.0.
|
|
55
|
-
"@teambit/ui-foundation.ui.corner": "0.0.
|
|
56
|
-
"@teambit/ui-foundation.ui.hooks.use-is-mobile": "0.0.
|
|
57
|
-
"@teambit/ui-foundation.ui.top-bar": "0.0.
|
|
46
|
+
"@teambit/react-router": "0.0.571",
|
|
47
|
+
"@teambit/sidebar": "0.0.571",
|
|
48
|
+
"@teambit/ui-foundation.ui.main-dropdown": "0.0.472",
|
|
49
|
+
"@teambit/ui-foundation.ui.menu": "0.0.472",
|
|
50
|
+
"@teambit/ui-foundation.ui.react-router.slot-router": "0.0.474",
|
|
51
|
+
"@teambit/bit-error": "0.0.381",
|
|
52
|
+
"@teambit/ui-foundation.ui.constants.z-indexes": "0.0.473",
|
|
53
|
+
"@teambit/scope.ui.hooks.use-scope": "0.0.83",
|
|
54
|
+
"@teambit/ui-foundation.ui.buttons.collapser": "0.0.178",
|
|
55
|
+
"@teambit/ui-foundation.ui.corner": "0.0.479",
|
|
56
|
+
"@teambit/ui-foundation.ui.hooks.use-is-mobile": "0.0.165",
|
|
57
|
+
"@teambit/ui-foundation.ui.top-bar": "0.0.475",
|
|
58
58
|
"@teambit/design.ui.styles.ellipsis": "0.0.346",
|
|
59
59
|
"@teambit/design.ui.styles.muted-italic": "0.0.35",
|
|
60
|
-
"@teambit/ui-foundation.ui.full-loader": "0.0.
|
|
61
|
-
"@teambit/ui-foundation.ui.side-bar": "0.0.
|
|
62
|
-
"@teambit/ui-foundation.ui.tree.drawer": "0.0.
|
|
63
|
-
"@teambit/ui-foundation.ui.use-box.dropdown": "0.0.
|
|
64
|
-
"@teambit/ui-foundation.ui.use-box.scope-menu": "0.0.
|
|
65
|
-
"@teambit/explorer.ui.gallery.component-card": "0.0.
|
|
66
|
-
"@teambit/explorer.ui.gallery.component-grid": "0.0.
|
|
67
|
-
"@teambit/preview.ui.preview-placeholder": "0.0.
|
|
68
|
-
"@teambit/scope.ui.empty-scope": "0.0.
|
|
69
|
-
"@teambit/scope.ui.scope-details": "0.0.
|
|
60
|
+
"@teambit/ui-foundation.ui.full-loader": "0.0.472",
|
|
61
|
+
"@teambit/ui-foundation.ui.side-bar": "0.0.478",
|
|
62
|
+
"@teambit/ui-foundation.ui.tree.drawer": "0.0.472",
|
|
63
|
+
"@teambit/ui-foundation.ui.use-box.dropdown": "0.0.93",
|
|
64
|
+
"@teambit/ui-foundation.ui.use-box.scope-menu": "0.0.93",
|
|
65
|
+
"@teambit/explorer.ui.gallery.component-card": "0.0.476",
|
|
66
|
+
"@teambit/explorer.ui.gallery.component-grid": "0.0.472",
|
|
67
|
+
"@teambit/preview.ui.preview-placeholder": "0.0.472",
|
|
68
|
+
"@teambit/scope.ui.empty-scope": "0.0.475",
|
|
69
|
+
"@teambit/scope.ui.scope-details": "0.0.481"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/semver": "7.3.4",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"@types/node": "12.20.4"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
|
-
"@teambit/legacy": "1.0.
|
|
85
|
+
"@teambit/legacy": "1.0.181",
|
|
86
86
|
"react-dom": "^16.8.0 || ^17.0.0",
|
|
87
87
|
"react": "^16.8.0 || ^17.0.0"
|
|
88
88
|
},
|
|
@@ -110,27 +110,12 @@
|
|
|
110
110
|
"react": "-"
|
|
111
111
|
},
|
|
112
112
|
"peerDependencies": {
|
|
113
|
-
"@teambit/legacy": "1.0.
|
|
113
|
+
"@teambit/legacy": "1.0.181",
|
|
114
114
|
"react-dom": "^16.8.0 || ^17.0.0",
|
|
115
115
|
"react": "^16.8.0 || ^17.0.0"
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
},
|
|
119
|
-
"files": [
|
|
120
|
-
"dist",
|
|
121
|
-
"!dist/tsconfig.tsbuildinfo",
|
|
122
|
-
"**/*.md",
|
|
123
|
-
"**/*.mdx",
|
|
124
|
-
"**/*.js",
|
|
125
|
-
"**/*.json",
|
|
126
|
-
"**/*.sass",
|
|
127
|
-
"**/*.scss",
|
|
128
|
-
"**/*.less",
|
|
129
|
-
"**/*.css",
|
|
130
|
-
"**/*.css",
|
|
131
|
-
"**/*.jpeg",
|
|
132
|
-
"**/*.gif"
|
|
133
|
-
],
|
|
134
119
|
"private": false,
|
|
135
120
|
"engines": {
|
|
136
121
|
"node": ">=12.22.0"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Route, Verb, Request, Response } from '@teambit/express';
|
|
2
|
+
import { action } from '@teambit/legacy/dist/api/scope/lib/action';
|
|
3
|
+
import { getAuthDataFromHeader } from '@teambit/legacy/dist/scope/network/http/http';
|
|
4
|
+
import { ScopeMain } from '../scope.main.runtime';
|
|
5
|
+
|
|
6
|
+
export class ActionRoute implements Route {
|
|
7
|
+
constructor(private scope: ScopeMain) {}
|
|
8
|
+
|
|
9
|
+
method = 'post';
|
|
10
|
+
route = '/scope/action';
|
|
11
|
+
verb = Verb.WRITE;
|
|
12
|
+
|
|
13
|
+
middlewares = [
|
|
14
|
+
async (req: Request, res: Response) => {
|
|
15
|
+
req.setTimeout(this.scope.config.httpTimeOut);
|
|
16
|
+
const authData = getAuthDataFromHeader(req.headers.authorization);
|
|
17
|
+
const result = await action(this.scope.path, req.body.name, req.body.options, authData);
|
|
18
|
+
// in case the result is empty, send `{}` to make it a valid json.
|
|
19
|
+
res.json(result || {});
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Route, Verb, Request, Response } from '@teambit/express';
|
|
2
|
+
import { ScopeMain } from '../scope.main.runtime';
|
|
3
|
+
|
|
4
|
+
export class DeleteRoute implements Route {
|
|
5
|
+
constructor(private scope: ScopeMain) {}
|
|
6
|
+
|
|
7
|
+
method = 'post';
|
|
8
|
+
route = '/scope/delete';
|
|
9
|
+
verb = Verb.WRITE;
|
|
10
|
+
|
|
11
|
+
middlewares = [
|
|
12
|
+
async (req: Request, res: Response) => {
|
|
13
|
+
const { headers } = req;
|
|
14
|
+
req.setTimeout(this.scope.config.httpTimeOut);
|
|
15
|
+
const result = await this.scope.delete(
|
|
16
|
+
{
|
|
17
|
+
ids: req.body.ids,
|
|
18
|
+
force: req.body.force,
|
|
19
|
+
lanes: req.body.lanes,
|
|
20
|
+
},
|
|
21
|
+
headers
|
|
22
|
+
);
|
|
23
|
+
res.json(result);
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Route, Verb, Request, Response } from '@teambit/express';
|
|
2
|
+
import { fetch } from '@teambit/legacy/dist/api/scope';
|
|
3
|
+
import { ObjectList } from '@teambit/legacy/dist/scope/objects/object-list';
|
|
4
|
+
import { Logger } from '@teambit/logger';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import { pipeline } from 'stream';
|
|
7
|
+
import { ScopeMain } from '../scope.main.runtime';
|
|
8
|
+
|
|
9
|
+
export class FetchRoute implements Route {
|
|
10
|
+
constructor(private scope: ScopeMain, private logger: Logger) {}
|
|
11
|
+
|
|
12
|
+
route = '/scope/fetch';
|
|
13
|
+
method = 'post';
|
|
14
|
+
verb = Verb.READ;
|
|
15
|
+
middlewares = [
|
|
16
|
+
async (req: Request, res: Response) => {
|
|
17
|
+
req.setTimeout(this.scope.config.httpTimeOut);
|
|
18
|
+
const preFetchHookP = this.scope.preFetchObjects
|
|
19
|
+
.values()
|
|
20
|
+
.map((fn) => fn({ ids: req.body.ids, fetchOptions: req.body.fetchOptions }, { headers: req.headers }));
|
|
21
|
+
|
|
22
|
+
Promise.all(preFetchHookP).catch((err) => {
|
|
23
|
+
this.logger.error('fatal: onPreFetchObjects encountered an error (this error does not stop the process)', err);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const readable = await fetch(this.scope.path, req.body.ids, req.body.fetchOptions);
|
|
27
|
+
const pack = ObjectList.fromObjectStreamToTar(readable, this.scope.name);
|
|
28
|
+
const pipelinePromise = promisify(pipeline);
|
|
29
|
+
try {
|
|
30
|
+
await pipelinePromise(pack, res);
|
|
31
|
+
this.logger.info('fetch.router, the response has been sent successfully to the client', req.headers);
|
|
32
|
+
} catch (err: any) {
|
|
33
|
+
if (req.aborted) {
|
|
34
|
+
this.logger.warn('FetchRoute, the client aborted the request', err);
|
|
35
|
+
} else {
|
|
36
|
+
this.logger.error(
|
|
37
|
+
`FetchRoute encountered an error during the pipeline streaming, this should never happen.
|
|
38
|
+
make sure the error is caught in fromObjectStreamToTar and it streamed using the name "ERROR"`,
|
|
39
|
+
err
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
}
|
package/routes/index.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Route, Verb, Request, Response } from '@teambit/express';
|
|
2
|
+
import { ObjectList } from '@teambit/legacy/dist/scope/objects/object-list';
|
|
3
|
+
import { put } from '@teambit/legacy/dist/api/scope';
|
|
4
|
+
import { OnPostPutSlot, ScopeMain } from '../scope.main.runtime';
|
|
5
|
+
|
|
6
|
+
export class PutRoute implements Route {
|
|
7
|
+
constructor(private scope: ScopeMain, private postPutSlot: OnPostPutSlot) {}
|
|
8
|
+
|
|
9
|
+
method = 'post';
|
|
10
|
+
route = '/scope/put';
|
|
11
|
+
verb = Verb.WRITE;
|
|
12
|
+
|
|
13
|
+
middlewares = [
|
|
14
|
+
async (req: Request, res: Response) => {
|
|
15
|
+
req.setTimeout(this.scope.config.httpTimeOut);
|
|
16
|
+
const pushOptionsStr = req.headers['push-options'];
|
|
17
|
+
if (!pushOptionsStr) throw new Error('http is missing the push-options header');
|
|
18
|
+
const pushOptions = JSON.parse(pushOptionsStr as string);
|
|
19
|
+
const objectList = await ObjectList.fromTar(req);
|
|
20
|
+
const ids = await put(
|
|
21
|
+
{
|
|
22
|
+
path: this.scope.path,
|
|
23
|
+
objectList,
|
|
24
|
+
},
|
|
25
|
+
pushOptions
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
await Promise.all(
|
|
29
|
+
ids.map((id) => {
|
|
30
|
+
return this.scope.resolveComponentId(id);
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
res.json(ids);
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import type { ComponentUI, ComponentModel } from '@teambit/component';
|
|
2
|
+
import { ComponentAspect } from '@teambit/component';
|
|
3
|
+
import { Slot, SlotRegistry } from '@teambit/harmony';
|
|
4
|
+
import ReactRouterAspect, { ReactRouterUI } from '@teambit/react-router';
|
|
5
|
+
import { RouteSlot } from '@teambit/ui-foundation.ui.react-router.slot-router';
|
|
6
|
+
import { SidebarAspect, SidebarUI, SidebarItem, SidebarItemSlot } from '@teambit/sidebar';
|
|
7
|
+
import { ComponentTreeNode } from '@teambit/component-tree';
|
|
8
|
+
import { UIAspect, UIRootUI as UIRoot, UIRuntime, UiUI } from '@teambit/ui';
|
|
9
|
+
import React, { ComponentType, ReactNode } from 'react';
|
|
10
|
+
import { MenuItemSlot, MenuItem } from '@teambit/ui-foundation.ui.main-dropdown';
|
|
11
|
+
import { RouteProps } from 'react-router-dom';
|
|
12
|
+
import { MenuWidget, MenuWidgetSlot } from '@teambit/ui-foundation.ui.menu';
|
|
13
|
+
import { MenuLinkItem } from '@teambit/design.ui.surfaces.menu.link-item';
|
|
14
|
+
import CommandBarAspect, { CommandBarUI, ComponentSearcher, CommandHandler } from '@teambit/command-bar';
|
|
15
|
+
import { ScopeModel } from '@teambit/scope.models.scope-model';
|
|
16
|
+
import { ScopeMenu, ScopeUseBox } from './ui/menu';
|
|
17
|
+
import { ScopeAspect } from './scope.aspect';
|
|
18
|
+
import { Scope } from './ui/scope';
|
|
19
|
+
import { ComponentsDrawer } from './ui/components-drawer';
|
|
20
|
+
|
|
21
|
+
export type ScopeBadge = ComponentType;
|
|
22
|
+
|
|
23
|
+
export type ScopeBadgeSlot = SlotRegistry<ScopeBadge[]>;
|
|
24
|
+
|
|
25
|
+
export type ScopeContextType = ComponentType<{ scope: ScopeModel; children: ReactNode }>;
|
|
26
|
+
|
|
27
|
+
export type SidebarSlot = SlotRegistry<ComponentTreeNode>;
|
|
28
|
+
|
|
29
|
+
export type ScopeOverview = ComponentType;
|
|
30
|
+
|
|
31
|
+
export type ScopeOverviewSlot = SlotRegistry<ScopeOverview>;
|
|
32
|
+
|
|
33
|
+
export type Corner = ComponentType;
|
|
34
|
+
|
|
35
|
+
export type CornerSlot = SlotRegistry<Corner>;
|
|
36
|
+
|
|
37
|
+
export type OverviewLine = ComponentType;
|
|
38
|
+
|
|
39
|
+
export type OverviewLineSlot = SlotRegistry<OverviewLine[]>;
|
|
40
|
+
|
|
41
|
+
export class ScopeUI {
|
|
42
|
+
constructor(
|
|
43
|
+
/**
|
|
44
|
+
* route slot.
|
|
45
|
+
*/
|
|
46
|
+
private routeSlot: RouteSlot,
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* component ui extension.
|
|
50
|
+
*/
|
|
51
|
+
private componentUi: ComponentUI,
|
|
52
|
+
/**
|
|
53
|
+
* menu slot
|
|
54
|
+
*/
|
|
55
|
+
private menuSlot: RouteSlot,
|
|
56
|
+
|
|
57
|
+
private sidebar: SidebarUI,
|
|
58
|
+
|
|
59
|
+
private sidebarSlot: SidebarSlot,
|
|
60
|
+
|
|
61
|
+
private commandBarUI: CommandBarUI,
|
|
62
|
+
|
|
63
|
+
private componentSearcher: ComponentSearcher,
|
|
64
|
+
|
|
65
|
+
private scopeBadgeSlot: ScopeBadgeSlot,
|
|
66
|
+
|
|
67
|
+
private menuWidgetSlot: MenuWidgetSlot,
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* sidebar link slot
|
|
71
|
+
*/
|
|
72
|
+
private sidebarItemSlot: SidebarItemSlot,
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* main dropdown item slot
|
|
76
|
+
*/
|
|
77
|
+
private menuItemSlot: MenuItemSlot,
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* corner slot
|
|
81
|
+
*/
|
|
82
|
+
private cornerSlot: CornerSlot,
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* overview line slot to add new lines beneath the overview section
|
|
86
|
+
*/
|
|
87
|
+
private overviewSlot: OverviewLineSlot
|
|
88
|
+
) {}
|
|
89
|
+
|
|
90
|
+
private setSidebarToggle: (updated: CommandHandler) => void = () => {};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* register a new badge into the scope overview.
|
|
94
|
+
*/
|
|
95
|
+
registerBadge(...badges: ScopeBadge[]) {
|
|
96
|
+
this.scopeBadgeSlot.register(badges);
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* register a new line beneath the scope overview section.
|
|
102
|
+
*/
|
|
103
|
+
registerOverviewLine(...lines: OverviewLine[]) {
|
|
104
|
+
this.overviewSlot.register(lines);
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* register a route to the scope.
|
|
110
|
+
*/
|
|
111
|
+
registerRoute(route: RouteProps) {
|
|
112
|
+
this.routeSlot.register(route);
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private registerExplicitRoutes() {
|
|
117
|
+
this.routeSlot.register({
|
|
118
|
+
path: this.componentUi.routePath,
|
|
119
|
+
children: this.componentUi.getComponentUI(ScopeAspect.id),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
this.menuSlot.register([
|
|
123
|
+
{
|
|
124
|
+
path: this.componentUi.routePath,
|
|
125
|
+
children: this.componentUi.getMenu(ScopeAspect.id),
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
exact: true,
|
|
129
|
+
path: '/',
|
|
130
|
+
children: <ScopeMenu widgetSlot={this.menuWidgetSlot} menuItemSlot={this.menuItemSlot} />,
|
|
131
|
+
},
|
|
132
|
+
]);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
registerMenuWidget(...menuItems: MenuWidget[]) {
|
|
136
|
+
this.menuWidgetSlot.register(menuItems);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
registerCorner(corner: Corner) {
|
|
140
|
+
this.cornerSlot.register(corner);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* register a scope overview.
|
|
145
|
+
*/
|
|
146
|
+
replaceOverview() {}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* register description.
|
|
150
|
+
*/
|
|
151
|
+
replaceDescription() {}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* register metadata section.
|
|
155
|
+
*/
|
|
156
|
+
replaceMetadataSection() {}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* register a metadata item.
|
|
160
|
+
*/
|
|
161
|
+
registerMetadataItem() {}
|
|
162
|
+
|
|
163
|
+
replaceComponentGrid() {}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* register metadata.
|
|
167
|
+
*/
|
|
168
|
+
registerMetadata() {}
|
|
169
|
+
|
|
170
|
+
private _context: () => ScopeContextType;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* add a new context to the scope.
|
|
174
|
+
*/
|
|
175
|
+
addContext(context: ScopeContextType) {
|
|
176
|
+
this._context = () => context;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
getContext() {
|
|
180
|
+
if (!this._context) return undefined;
|
|
181
|
+
return this._context();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
registerMenuItem = (menuItems: MenuItem[]) => {
|
|
185
|
+
this.menuItemSlot.register(menuItems);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* register a sidebar link to the section above the drawers
|
|
190
|
+
*/
|
|
191
|
+
registerSidebarLink = (...links: SidebarItem[]) => {
|
|
192
|
+
this.sidebarItemSlot.register(links);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
uiRoot(): UIRoot {
|
|
196
|
+
this.sidebar.registerDrawer(new ComponentsDrawer(this.sidebarSlot));
|
|
197
|
+
this.commandBarUI.addSearcher(this.componentSearcher);
|
|
198
|
+
|
|
199
|
+
const [setKeyBindHandler] = this.commandBarUI.addCommand({
|
|
200
|
+
id: 'sidebar.toggle', // TODO - extract to a component!
|
|
201
|
+
handler: () => {},
|
|
202
|
+
displayName: 'Toggle component list',
|
|
203
|
+
keybinding: 'alt+s',
|
|
204
|
+
});
|
|
205
|
+
this.setSidebarToggle = setKeyBindHandler;
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
routes: [
|
|
209
|
+
{
|
|
210
|
+
path: '/',
|
|
211
|
+
children: (
|
|
212
|
+
<Scope
|
|
213
|
+
routeSlot={this.routeSlot}
|
|
214
|
+
menuSlot={this.menuSlot}
|
|
215
|
+
sidebar={<this.sidebar.render itemSlot={this.sidebarItemSlot} />}
|
|
216
|
+
scopeUi={this}
|
|
217
|
+
badgeSlot={this.scopeBadgeSlot}
|
|
218
|
+
overviewLineSlot={this.overviewSlot}
|
|
219
|
+
context={this.getContext()}
|
|
220
|
+
onSidebarTogglerChange={this.setSidebarToggle}
|
|
221
|
+
cornerSlot={this.cornerSlot}
|
|
222
|
+
/>
|
|
223
|
+
),
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** registers available components */
|
|
230
|
+
setComponents = (components: ComponentModel[]) => {
|
|
231
|
+
this.componentSearcher.update(components);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
private menuItems: MenuItem[] = [
|
|
235
|
+
{
|
|
236
|
+
category: 'general',
|
|
237
|
+
title: 'Open command bar',
|
|
238
|
+
keyChar: 'mod+k',
|
|
239
|
+
handler: () => this.commandBarUI?.run('command-bar.open'),
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
category: 'general',
|
|
243
|
+
title: 'Toggle component list',
|
|
244
|
+
keyChar: 'alt+s',
|
|
245
|
+
handler: () => this.commandBarUI?.run('sidebar.toggle'),
|
|
246
|
+
},
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
static dependencies = [UIAspect, ComponentAspect, SidebarAspect, CommandBarAspect, ReactRouterAspect];
|
|
250
|
+
static runtime = UIRuntime;
|
|
251
|
+
static slots = [
|
|
252
|
+
Slot.withType<RouteProps>(),
|
|
253
|
+
Slot.withType<RouteProps>(),
|
|
254
|
+
Slot.withType<ComponentTreeNode>(),
|
|
255
|
+
Slot.withType<ScopeBadge>(),
|
|
256
|
+
Slot.withType<ScopeOverview>(),
|
|
257
|
+
Slot.withType<MenuWidget[]>(),
|
|
258
|
+
Slot.withType<MenuItemSlot>(),
|
|
259
|
+
Slot.withType<CornerSlot>(),
|
|
260
|
+
Slot.withType<OverviewLineSlot>(),
|
|
261
|
+
Slot.withType<SidebarItemSlot>(),
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
static async provider(
|
|
265
|
+
[ui, componentUi, sidebar, commandBarUI, reactRouterUI]: [
|
|
266
|
+
UiUI,
|
|
267
|
+
ComponentUI,
|
|
268
|
+
SidebarUI,
|
|
269
|
+
CommandBarUI,
|
|
270
|
+
ReactRouterUI
|
|
271
|
+
],
|
|
272
|
+
config,
|
|
273
|
+
[
|
|
274
|
+
routeSlot,
|
|
275
|
+
menuSlot,
|
|
276
|
+
sidebarSlot,
|
|
277
|
+
scopeBadgeSlot,
|
|
278
|
+
menuWidgetSlot,
|
|
279
|
+
menuItemSlot,
|
|
280
|
+
sidebarItemSlot,
|
|
281
|
+
cornerSlot,
|
|
282
|
+
overviewSlot,
|
|
283
|
+
]: [
|
|
284
|
+
RouteSlot,
|
|
285
|
+
RouteSlot,
|
|
286
|
+
SidebarSlot,
|
|
287
|
+
ScopeBadgeSlot,
|
|
288
|
+
MenuWidgetSlot,
|
|
289
|
+
MenuItemSlot,
|
|
290
|
+
SidebarItemSlot,
|
|
291
|
+
CornerSlot,
|
|
292
|
+
OverviewLineSlot
|
|
293
|
+
]
|
|
294
|
+
) {
|
|
295
|
+
const componentSearcher = new ComponentSearcher(reactRouterUI.navigateTo);
|
|
296
|
+
const scopeUi = new ScopeUI(
|
|
297
|
+
routeSlot,
|
|
298
|
+
componentUi,
|
|
299
|
+
menuSlot,
|
|
300
|
+
sidebar,
|
|
301
|
+
sidebarSlot,
|
|
302
|
+
commandBarUI,
|
|
303
|
+
componentSearcher,
|
|
304
|
+
scopeBadgeSlot,
|
|
305
|
+
menuWidgetSlot,
|
|
306
|
+
sidebarItemSlot,
|
|
307
|
+
menuItemSlot,
|
|
308
|
+
cornerSlot,
|
|
309
|
+
overviewSlot
|
|
310
|
+
);
|
|
311
|
+
scopeUi.registerExplicitRoutes();
|
|
312
|
+
scopeUi.registerMenuItem(scopeUi.menuItems);
|
|
313
|
+
scopeUi.registerMenuWidget(() => <ScopeUseBox />);
|
|
314
|
+
ui.registerRoot(scopeUi.uiRoot.bind(scopeUi));
|
|
315
|
+
scopeUi.registerSidebarLink(() => (
|
|
316
|
+
<MenuLinkItem exact href="/" icon="comps">
|
|
317
|
+
Gallery
|
|
318
|
+
</MenuLinkItem>
|
|
319
|
+
));
|
|
320
|
+
|
|
321
|
+
return scopeUi;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export default ScopeUI;
|
|
326
|
+
|
|
327
|
+
ScopeAspect.addRuntime(ScopeUI);
|
package/types/asset.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
declare module '*.png' {
|
|
2
|
+
const value: any;
|
|
3
|
+
export = value;
|
|
4
|
+
}
|
|
5
|
+
declare module '*.svg' {
|
|
6
|
+
import type { FunctionComponent, SVGProps } from 'react';
|
|
7
|
+
|
|
8
|
+
export const ReactComponent: FunctionComponent<SVGProps<SVGSVGElement> & { title?: string }>;
|
|
9
|
+
const src: string;
|
|
10
|
+
export default src;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// @TODO Gilad
|
|
14
|
+
declare module '*.jpg' {
|
|
15
|
+
const value: any;
|
|
16
|
+
export = value;
|
|
17
|
+
}
|
|
18
|
+
declare module '*.jpeg' {
|
|
19
|
+
const value: any;
|
|
20
|
+
export = value;
|
|
21
|
+
}
|
|
22
|
+
declare module '*.gif' {
|
|
23
|
+
const value: any;
|
|
24
|
+
export = value;
|
|
25
|
+
}
|
|
26
|
+
declare module '*.bmp' {
|
|
27
|
+
const value: any;
|
|
28
|
+
export = value;
|
|
29
|
+
}
|
package/types/style.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
declare module '*.module.css' {
|
|
2
|
+
const classes: { readonly [key: string]: string };
|
|
3
|
+
export default classes;
|
|
4
|
+
}
|
|
5
|
+
declare module '*.module.scss' {
|
|
6
|
+
const classes: { readonly [key: string]: string };
|
|
7
|
+
export default classes;
|
|
8
|
+
}
|
|
9
|
+
declare module '*.module.sass' {
|
|
10
|
+
const classes: { readonly [key: string]: string };
|
|
11
|
+
export default classes;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare module '*.module.less' {
|
|
15
|
+
const classes: { readonly [key: string]: string };
|
|
16
|
+
export default classes;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare module '*.less' {
|
|
20
|
+
const classes: { readonly [key: string]: string };
|
|
21
|
+
export default classes;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare module '*.css' {
|
|
25
|
+
const classes: { readonly [key: string]: string };
|
|
26
|
+
export default classes;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
declare module '*.sass' {
|
|
30
|
+
const classes: { readonly [key: string]: string };
|
|
31
|
+
export default classes;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
declare module '*.scss' {
|
|
35
|
+
const classes: { readonly [key: string]: string };
|
|
36
|
+
export default classes;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
declare module '*.mdx' {
|
|
40
|
+
const component: any;
|
|
41
|
+
export default component;
|
|
42
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import {
|
|
4
|
+
ComponentTree,
|
|
5
|
+
ComponentView,
|
|
6
|
+
NamespaceTreeNode,
|
|
7
|
+
PayloadType,
|
|
8
|
+
ScopePayload,
|
|
9
|
+
} from '@teambit/ui-foundation.ui.side-bar';
|
|
10
|
+
import { TreeNodeProps } from '@teambit/base-ui.graph.tree.recursive-tree';
|
|
11
|
+
|
|
12
|
+
import { FullLoader } from '@teambit/ui-foundation.ui.full-loader';
|
|
13
|
+
import { ComponentTreeSlot } from '@teambit/component-tree';
|
|
14
|
+
import type { DrawerType } from '@teambit/ui-foundation.ui.tree.drawer';
|
|
15
|
+
import { mutedItalic } from '@teambit/design.ui.styles.muted-italic';
|
|
16
|
+
import { ellipsis } from '@teambit/design.ui.styles.ellipsis';
|
|
17
|
+
import { useScopeQuery } from '@teambit/scope.ui.hooks.use-scope';
|
|
18
|
+
import styles from './components-drawer.module.scss';
|
|
19
|
+
|
|
20
|
+
export class ComponentsDrawer implements DrawerType {
|
|
21
|
+
constructor(private treeNodeSlot: ComponentTreeSlot) {}
|
|
22
|
+
|
|
23
|
+
name = 'COMPONENTS';
|
|
24
|
+
|
|
25
|
+
render = () => {
|
|
26
|
+
const { scope } = useScopeQuery();
|
|
27
|
+
const { treeNodeSlot } = this;
|
|
28
|
+
|
|
29
|
+
const TreeNodeRenderer = useCallback(
|
|
30
|
+
function TreeNode(props: TreeNodeProps<PayloadType>) {
|
|
31
|
+
const children = props.node.children;
|
|
32
|
+
|
|
33
|
+
if (!children) return <ComponentView {...props} treeNodeSlot={treeNodeSlot} />;
|
|
34
|
+
|
|
35
|
+
// skip over scope node and render only children
|
|
36
|
+
if (props.node.payload instanceof ScopePayload) {
|
|
37
|
+
return (
|
|
38
|
+
<>
|
|
39
|
+
{children.map((childNode) => (
|
|
40
|
+
<TreeNodeRenderer key={childNode.id} {...props} node={childNode}></TreeNodeRenderer>
|
|
41
|
+
))}
|
|
42
|
+
</>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return <NamespaceTreeNode {...props} />;
|
|
47
|
+
},
|
|
48
|
+
[treeNodeSlot]
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (!scope) return <FullLoader />;
|
|
52
|
+
if (scope.components.length === 0)
|
|
53
|
+
return <span className={classNames(mutedItalic, ellipsis, styles.emptyScope)}>Scope is empty</span>;
|
|
54
|
+
return <ComponentTree components={scope.components} TreeNode={TreeNodeRenderer} />;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ComponentsDrawer } from './components.drawer';
|
package/ui/menu/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ScopeMenu, ScopeUseBox } from './menu';
|
package/ui/menu/menu.tsx
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Menu, MenuProps } from '@teambit/ui-foundation.ui.menu';
|
|
2
|
+
import { ScopeContext } from '@teambit/scope.ui.hooks.scope-context';
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
import React, { useContext } from 'react';
|
|
5
|
+
import { UseBoxDropdown } from '@teambit/ui-foundation.ui.use-box.dropdown';
|
|
6
|
+
import { Menu as ScopeUseBoxMenu } from '@teambit/ui-foundation.ui.use-box.scope-menu';
|
|
7
|
+
import styles from './menu.module.scss';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* scope menu.
|
|
11
|
+
*/
|
|
12
|
+
export function ScopeMenu({ className, ...rest }: MenuProps) {
|
|
13
|
+
return <Menu {...rest} className={classNames(styles.scopMenu, className)} />;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function ScopeUseBox() {
|
|
17
|
+
const scope = useContext(ScopeContext);
|
|
18
|
+
return (
|
|
19
|
+
<UseBoxDropdown
|
|
20
|
+
position="bottom-end"
|
|
21
|
+
className={styles.useBox}
|
|
22
|
+
Menu={() => <ScopeUseBoxMenu scopeName={scope.name} />}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './scope-overview';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { ComponentCard } from '@teambit/explorer.ui.gallery.component-card';
|
|
3
|
+
import { ComponentGrid } from '@teambit/explorer.ui.gallery.component-grid';
|
|
4
|
+
import { ScopeDetails } from '@teambit/scope.ui.scope-details';
|
|
5
|
+
import { PreviewPlaceholder } from '@teambit/preview.ui.preview-placeholder';
|
|
6
|
+
import { EmptyScope } from '@teambit/scope.ui.empty-scope';
|
|
7
|
+
import { ComponentModel } from '@teambit/component';
|
|
8
|
+
import { ScopeContext } from '@teambit/scope.ui.hooks.scope-context';
|
|
9
|
+
import styles from './scope-overview.module.scss';
|
|
10
|
+
import type { ScopeBadgeSlot, OverviewLineSlot } from '../../scope.ui.runtime';
|
|
11
|
+
|
|
12
|
+
export type ScopeOverviewProps = {
|
|
13
|
+
badgeSlot: ScopeBadgeSlot;
|
|
14
|
+
overviewSlot: OverviewLineSlot;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function ScopeOverview({ badgeSlot, overviewSlot }: ScopeOverviewProps) {
|
|
18
|
+
const scope = useContext(ScopeContext);
|
|
19
|
+
const { components } = scope;
|
|
20
|
+
if (!components || components.length === 0) return <EmptyScope name={scope.name} />;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className={styles.container}>
|
|
24
|
+
<ScopeDetails
|
|
25
|
+
scopeName={scope.name}
|
|
26
|
+
icon={scope.icon}
|
|
27
|
+
backgroundIconColor={scope.backgroundIconColor}
|
|
28
|
+
badgeSlot={badgeSlot}
|
|
29
|
+
overviewSlot={overviewSlot}
|
|
30
|
+
description={scope.description}
|
|
31
|
+
componentCount={scope.components.length}
|
|
32
|
+
/>
|
|
33
|
+
<ComponentGrid>
|
|
34
|
+
{components.map((component, index) => {
|
|
35
|
+
return (
|
|
36
|
+
<div key={index}>
|
|
37
|
+
<ScopeComponentCard component={component} />
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
})}
|
|
41
|
+
</ComponentGrid>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type ScopeComponentCardProps = {
|
|
47
|
+
component: ComponentModel;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export function ScopeComponentCard({ component }: ScopeComponentCardProps) {
|
|
51
|
+
const shouldShowPreview = component.compositions.length > 0;
|
|
52
|
+
return (
|
|
53
|
+
<ComponentCard
|
|
54
|
+
id={component.id.fullName}
|
|
55
|
+
envIcon={component.environment?.icon}
|
|
56
|
+
description={component.description}
|
|
57
|
+
version={component.version}
|
|
58
|
+
preview={<PreviewPlaceholder component={component} shouldShowPreview={shouldShowPreview} />}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
package/ui/scope.tsx
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import 'reset-css';
|
|
2
|
+
import { SplitPane, Pane, Layout } from '@teambit/base-ui.surfaces.split-pane.split-pane';
|
|
3
|
+
import { RouteSlot, SlotRouter } from '@teambit/ui-foundation.ui.react-router.slot-router';
|
|
4
|
+
import { Corner } from '@teambit/ui-foundation.ui.corner';
|
|
5
|
+
import { Collapser } from '@teambit/ui-foundation.ui.buttons.collapser';
|
|
6
|
+
import { HoverSplitter } from '@teambit/base-ui.surfaces.split-pane.hover-splitter';
|
|
7
|
+
import { TopBar } from '@teambit/ui-foundation.ui.top-bar';
|
|
8
|
+
import { FullLoader } from '@teambit/legacy/dist/to-eject/full-loader';
|
|
9
|
+
import React, { useReducer } from 'react';
|
|
10
|
+
import { Route } from 'react-router-dom';
|
|
11
|
+
import { useIsMobile } from '@teambit/ui-foundation.ui.hooks.use-is-mobile';
|
|
12
|
+
import { ScopeProvider } from '@teambit/scope.ui.hooks.scope-context';
|
|
13
|
+
import { useScopeQuery } from '@teambit/scope.ui.hooks.use-scope';
|
|
14
|
+
import { ScopeOverview } from './scope-overview';
|
|
15
|
+
import styles from './scope.module.scss';
|
|
16
|
+
import ScopeUI, { ScopeBadgeSlot, ScopeContextType, CornerSlot, OverviewLineSlot } from '../scope.ui.runtime';
|
|
17
|
+
|
|
18
|
+
export type ScopeProps = {
|
|
19
|
+
routeSlot: RouteSlot;
|
|
20
|
+
menuSlot: RouteSlot;
|
|
21
|
+
sidebar: JSX.Element;
|
|
22
|
+
scopeUi: ScopeUI;
|
|
23
|
+
badgeSlot: ScopeBadgeSlot;
|
|
24
|
+
overviewLineSlot: OverviewLineSlot;
|
|
25
|
+
cornerSlot: CornerSlot;
|
|
26
|
+
context?: ScopeContextType;
|
|
27
|
+
onSidebarTogglerChange: (callback: () => void) => void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* root component of the scope
|
|
32
|
+
*/
|
|
33
|
+
export function Scope({
|
|
34
|
+
routeSlot,
|
|
35
|
+
menuSlot,
|
|
36
|
+
sidebar,
|
|
37
|
+
scopeUi,
|
|
38
|
+
badgeSlot,
|
|
39
|
+
overviewLineSlot,
|
|
40
|
+
cornerSlot,
|
|
41
|
+
context,
|
|
42
|
+
onSidebarTogglerChange,
|
|
43
|
+
}: ScopeProps) {
|
|
44
|
+
const { scope } = useScopeQuery();
|
|
45
|
+
const isMobile = useIsMobile();
|
|
46
|
+
const [isSidebarOpen, handleSidebarToggle] = useReducer((x) => !x, !isMobile);
|
|
47
|
+
const sidebarOpenness = isSidebarOpen ? Layout.row : Layout.right;
|
|
48
|
+
if (!scope) {
|
|
49
|
+
return <FullLoader />;
|
|
50
|
+
}
|
|
51
|
+
const CornerOverride = cornerSlot?.values()[0];
|
|
52
|
+
scopeUi.setComponents(scope.components);
|
|
53
|
+
const defaultContext = ({ children }) => <div>{children}</div>;
|
|
54
|
+
const Context = context || defaultContext;
|
|
55
|
+
|
|
56
|
+
onSidebarTogglerChange(handleSidebarToggle);
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<ScopeProvider scope={scope}>
|
|
60
|
+
<Context scope={scope}>
|
|
61
|
+
<div className={styles.scope}>
|
|
62
|
+
<TopBar
|
|
63
|
+
className={styles.topbar}
|
|
64
|
+
Corner={() => {
|
|
65
|
+
if (CornerOverride) return <CornerOverride />;
|
|
66
|
+
return <Corner name={scope.name} className={styles.whiteCorner} />;
|
|
67
|
+
}}
|
|
68
|
+
menu={menuSlot}
|
|
69
|
+
/>
|
|
70
|
+
|
|
71
|
+
<SplitPane className={styles.main} size={264} layout={sidebarOpenness}>
|
|
72
|
+
<Pane className={styles.sidebar}>{sidebar}</Pane>
|
|
73
|
+
<HoverSplitter className={styles.splitter}>
|
|
74
|
+
<Collapser
|
|
75
|
+
isOpen={isSidebarOpen}
|
|
76
|
+
onMouseDown={(e) => e.stopPropagation()} // avoid split-pane drag
|
|
77
|
+
onClick={handleSidebarToggle}
|
|
78
|
+
tooltipContent={`${isSidebarOpen ? 'Hide' : 'Show'} side panel`}
|
|
79
|
+
/>
|
|
80
|
+
</HoverSplitter>
|
|
81
|
+
<Pane>
|
|
82
|
+
<SlotRouter slot={routeSlot} />
|
|
83
|
+
<Route exact path="/">
|
|
84
|
+
<ScopeOverview badgeSlot={badgeSlot} overviewSlot={overviewLineSlot} />
|
|
85
|
+
</Route>
|
|
86
|
+
</Pane>
|
|
87
|
+
</SplitPane>
|
|
88
|
+
</div>
|
|
89
|
+
</Context>
|
|
90
|
+
</ScopeProvider>
|
|
91
|
+
);
|
|
92
|
+
}
|