@teambit/ui 0.0.568 → 0.0.569

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/compose.tsx ADDED
@@ -0,0 +1,27 @@
1
+ import React, { ReactNode, ComponentType } from 'react';
2
+
3
+ type ComponentTuple<T = any> = [Component: ComponentType<T>, props?: T];
4
+ export type Wrapper<T = any> = ComponentType<T> | ComponentTuple<T>;
5
+ interface Props<T = any> {
6
+ /** Compose these components. Can be a ReactComponent, or a [ReactComponent, Props] tuple */
7
+ components: Wrapper<T>[];
8
+ children?: ReactNode;
9
+ }
10
+
11
+ /**
12
+ * A react Component composer. equivalent to `(n+1) => <a[n+1]> <Compose(a[n]) /> </a[n+1]>`.
13
+ * Component can be a React Component, or a `[Component, { ...props }]` tuple.
14
+ */
15
+ export function Compose(props: Props) {
16
+ const { components = [], children } = props;
17
+
18
+ const arrayified: ComponentTuple[] = components.map((tuple) => (Array.isArray(tuple) ? tuple : [tuple, undefined]));
19
+
20
+ return (
21
+ <>
22
+ {arrayified.reduceRight((acc, [Comp, forwardProps]) => {
23
+ return <Comp {...forwardProps}>{acc}</Comp>;
24
+ }, children)}
25
+ </>
26
+ );
27
+ }
@@ -0,0 +1 @@
1
+ export * from './ui-server-started-event';
@@ -0,0 +1,14 @@
1
+ /* eslint-disable max-classes-per-file */
2
+ import { BitBaseEvent } from '@teambit/pubsub';
3
+
4
+ class UiServerStartedEventData {
5
+ constructor(readonly targetHost, readonly targetPort, readonly uiRoot) {}
6
+ }
7
+
8
+ export class UiServerStartedEvent extends BitBaseEvent<UiServerStartedEventData> {
9
+ static readonly TYPE = 'ui-server-started';
10
+
11
+ constructor(readonly timestamp, readonly targetHost, readonly targetPort, readonly uiRoot) {
12
+ super(UiServerStartedEvent.TYPE, '0.0.1', timestamp, new UiServerStartedEventData(targetHost, targetPort, uiRoot));
13
+ }
14
+ }
@@ -0,0 +1,2 @@
1
+ export { UnknownUI } from './unknown-ui';
2
+ export { UnknownBuildError } from './unknown-build-error';
@@ -0,0 +1,5 @@
1
+ export class UnknownBuildError extends Error {
2
+ constructor() {
3
+ super('unknown UI build error');
4
+ }
5
+ }
@@ -0,0 +1,9 @@
1
+ export class UnknownUI extends Error {
2
+ constructor(readonly uiRoot: string = '', readonly available?: string[]) {
3
+ super();
4
+ }
5
+
6
+ toString() {
7
+ return `Unknown UI root: "${this.uiRoot}". Available ui roots are: [${this.available?.join(', ')}]`;
8
+ }
9
+ }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@teambit/ui",
3
- "version": "0.0.568",
3
+ "version": "0.0.569",
4
4
  "homepage": "https://bit.dev/teambit/ui-foundation/ui",
5
5
  "main": "dist/index.js",
6
6
  "componentId": {
7
7
  "scope": "teambit.ui-foundation",
8
8
  "name": "ui",
9
- "version": "0.0.568"
9
+ "version": "0.0.569"
10
10
  },
11
11
  "dependencies": {
12
12
  "lodash": "4.17.21",
@@ -53,28 +53,28 @@
53
53
  "@teambit/base-ui.loaders.loader-ribbon": "1.0.0",
54
54
  "@teambit/base-ui.theme.fonts.roboto": "1.0.0",
55
55
  "@teambit/base-ui.theme.theme-provider": "1.0.1",
56
- "@teambit/aspect-loader": "0.0.568",
57
- "@teambit/toolbox.path.to-windows-compatible-path": "0.0.468",
58
- "@teambit/ui-foundation.ui.hooks.use-data-query": "0.0.472",
59
- "@teambit/logger": "0.0.480",
60
- "@teambit/cli": "0.0.394",
61
- "@teambit/ui-foundation.cli.ui-server-console": "0.0.473",
62
- "@teambit/bundler": "0.0.568",
63
- "@teambit/component": "0.0.568",
64
- "@teambit/express": "0.0.484",
65
- "@teambit/graphql": "0.0.568",
66
- "@teambit/toolbox.network.get-port": "0.0.98",
67
- "@teambit/aspect": "0.0.568",
68
- "@teambit/cache": "0.0.480",
69
- "@teambit/pubsub": "0.0.568",
70
- "@teambit/react-router": "0.0.568",
71
- "@teambit/ui-foundation.ui.rendering.html": "0.0.54",
56
+ "@teambit/aspect-loader": "0.0.569",
57
+ "@teambit/toolbox.path.to-windows-compatible-path": "0.0.469",
58
+ "@teambit/ui-foundation.ui.hooks.use-data-query": "0.0.473",
59
+ "@teambit/logger": "0.0.481",
60
+ "@teambit/cli": "0.0.395",
61
+ "@teambit/ui-foundation.cli.ui-server-console": "0.0.474",
62
+ "@teambit/bundler": "0.0.569",
63
+ "@teambit/component": "0.0.569",
64
+ "@teambit/express": "0.0.485",
65
+ "@teambit/graphql": "0.0.569",
66
+ "@teambit/toolbox.network.get-port": "0.0.99",
67
+ "@teambit/aspect": "0.0.569",
68
+ "@teambit/cache": "0.0.481",
69
+ "@teambit/pubsub": "0.0.569",
70
+ "@teambit/react-router": "0.0.569",
71
+ "@teambit/ui-foundation.ui.rendering.html": "0.0.55",
72
72
  "@teambit/design.theme.icons-font": "1.0.2",
73
73
  "@teambit/design.ui.tooltip": "0.0.352",
74
- "@teambit/ui-foundation.ui.global-loader": "0.0.471",
75
- "@teambit/webpack.modules.generate-style-loaders": "0.0.88",
76
- "@teambit/webpack.modules.style-regexps": "0.0.117",
77
- "@teambit/webpack": "0.0.568"
74
+ "@teambit/ui-foundation.ui.global-loader": "0.0.472",
75
+ "@teambit/webpack.modules.generate-style-loaders": "0.0.89",
76
+ "@teambit/webpack.modules.style-regexps": "0.0.118",
77
+ "@teambit/webpack": "0.0.569"
78
78
  },
79
79
  "devDependencies": {
80
80
  "@types/react": "^17.0.8",
@@ -96,7 +96,7 @@
96
96
  },
97
97
  "peerDependencies": {
98
98
  "@apollo/client": "^3.0.0",
99
- "@teambit/legacy": "1.0.180",
99
+ "@teambit/legacy": "1.0.181",
100
100
  "react-dom": "^16.8.0 || ^17.0.0",
101
101
  "react": "^16.8.0 || ^17.0.0"
102
102
  },
@@ -141,27 +141,12 @@
141
141
  "react": "-"
142
142
  },
143
143
  "peerDependencies": {
144
- "@teambit/legacy": "1.0.180",
144
+ "@teambit/legacy": "1.0.181",
145
145
  "react-dom": "^16.8.0 || ^17.0.0",
146
146
  "react": "^16.8.0 || ^17.0.0"
147
147
  }
148
148
  }
149
149
  },
150
- "files": [
151
- "dist",
152
- "!dist/tsconfig.tsbuildinfo",
153
- "**/*.md",
154
- "**/*.mdx",
155
- "**/*.js",
156
- "**/*.json",
157
- "**/*.sass",
158
- "**/*.scss",
159
- "**/*.less",
160
- "**/*.css",
161
- "**/*.css",
162
- "**/*.jpeg",
163
- "**/*.gif"
164
- ],
165
150
  "private": false,
166
151
  "engines": {
167
152
  "node": ">=12.22.0"
@@ -0,0 +1,56 @@
1
+ import { ReactNode, ComponentType } from 'react';
2
+ import { BrowserData } from './ssr/request-browser';
3
+ import { RequestServer } from './ssr/request-server';
4
+ import { ContextProps } from './ui.ui.runtime';
5
+
6
+ export type RenderLifecycle<RenderCtx = any, Serialized = any> = {
7
+ /**
8
+ * Initialize a context state for this specific rendering.
9
+ * Context state will only be available to the current Aspect, in the other hooks, as well as a prop to the react context component.
10
+ */
11
+ serverInit?: (state: {
12
+ browser?: BrowserData;
13
+ server?: RequestServer;
14
+ }) => RenderCtx | void | undefined | Promise<RenderCtx | void | undefined>;
15
+ /**
16
+ * Executes before running ReactDOM.renderToString(). Return value will replace the existing context state.
17
+ */
18
+ onBeforeRender?: (
19
+ ctx: RenderCtx,
20
+ app: ReactNode
21
+ ) => RenderCtx | void | undefined | Promise<RenderCtx | void | undefined>;
22
+ /**
23
+ * Produce html assets. Runs after the body is rendered, and before rendering the final html.
24
+ * @returns
25
+ * json: will be rendered to the dom as a `<script type="json"/>`.
26
+ * More assets will be available in the future.
27
+ */
28
+ serialize?: (ctx: RenderCtx, app: ReactNode) => { json: string } | Promise<{ json: string }> | undefined;
29
+ /**
30
+ * Converts serialized data from raw string back to structured data.
31
+ * @example deserialize: (data) => { const parsed = JSON.parse(data); return { analytics: new AnalyticsService(parsed); } }
32
+ */
33
+ deserialize?: (data?: string) => Serialized;
34
+ /**
35
+ * Initialize the context state for client side rendering.
36
+ * Context state will only be available to the current Aspect, in the other hooks, as well as a prop to the react context component.
37
+ */
38
+ browserInit?: (deserializedData: Serialized) => RenderCtx | void | undefined | Promise<RenderCtx | void | undefined>;
39
+ /**
40
+ * Executes before running ReactDOM.hydrate() (or .render() in case server side rendering is skipped). Receives the context produced by `deserialize()`
41
+ */
42
+ onBeforeHydrate?: (
43
+ context: RenderCtx,
44
+ app: ReactNode
45
+ ) => RenderCtx | void | undefined | Promise<RenderCtx | void | undefined>;
46
+ /**
47
+ * Executes after browser rendering is complete. Receives context from the previous steps.
48
+ * @example onHydrate: (ref, { analytics }) => { analytics.reportPageView() }
49
+ */
50
+ onHydrate?: (context: RenderCtx, ref: HTMLElement | null) => void;
51
+
52
+ /**
53
+ * Wraps dom with a context. Will receive render context, produced by `onBeforeRender()` (at server-side) or `deserialize()` (at the browser)
54
+ */
55
+ reactContext?: ComponentType<ContextProps<RenderCtx>>;
56
+ };
@@ -0,0 +1,129 @@
1
+ import type { Assets } from '@teambit/ui-foundation.ui.rendering.html';
2
+ import { Request, Response, NextFunction } from 'express';
3
+ import path from 'path';
4
+ import * as fs from 'fs-extra';
5
+ import type { Logger } from '@teambit/logger';
6
+ import { requestToObj } from './request-browser';
7
+ import { SsrContent } from './ssr-content';
8
+
9
+ const denyList = /^\/favicon.ico$/;
10
+
11
+ type ssrRenderProps = {
12
+ root: string;
13
+ port: number;
14
+ title: string;
15
+ logger: Logger;
16
+ };
17
+
18
+ type ManifestFile = {
19
+ files?: Record<string, string>;
20
+ entrypoints?: string[];
21
+ };
22
+
23
+ export async function createSsrMiddleware({ root, port, title, logger }: ssrRenderProps) {
24
+ const runtime = await loadRuntime(root, { logger });
25
+ if (!runtime) return undefined;
26
+
27
+ const { render } = runtime;
28
+ const assets = { ...runtime.assets, title };
29
+
30
+ return async function serverRenderMiddleware(req: Request, res: Response, next: NextFunction) {
31
+ const { query, url } = req;
32
+
33
+ const browser = requestToObj(req, port);
34
+
35
+ if (denyList.test(url)) {
36
+ logger.debug(`[ssr] skipping static denyList file ${url}`);
37
+ next();
38
+ return;
39
+ }
40
+
41
+ if (query.rendering === 'client') {
42
+ logger.debug(`[ssr] skipping ${url}`);
43
+ next();
44
+ return;
45
+ }
46
+
47
+ logger.debug(`[ssr] ${req.method} ${url}`);
48
+ const server = { port, request: req, response: res };
49
+ const props: SsrContent = { assets, browser, server };
50
+
51
+ try {
52
+ const rendered = await render(props);
53
+ res.set('Cache-Control', 'no-cache');
54
+ res.send(rendered);
55
+ logger.debug(`[ssr] success '${url}'`);
56
+ } catch (e: any) {
57
+ logger.error(`[ssr] failed at '${url}'`, e);
58
+ next();
59
+ }
60
+ };
61
+ }
62
+
63
+ async function loadRuntime(root: string, { logger }: { logger: Logger }) {
64
+ let render: (...arg: any[]) => any;
65
+ let assets: Assets | undefined;
66
+
67
+ try {
68
+ const entryFilepath = path.join(root, 'ssr', 'index.js');
69
+ if (!fs.existsSync(entryFilepath)) {
70
+ logger.warn(`[ssr] - Skipping setup - failed finding ssr bundle at "${entryFilepath}"`);
71
+ return undefined;
72
+ }
73
+
74
+ const manifestFilepath = path.join(root, 'asset-manifest.json');
75
+ if (!fs.existsSync(manifestFilepath)) {
76
+ logger.warn('[ssr] - Skipping setup (cannot find asset manifest file)');
77
+ return undefined;
78
+ }
79
+
80
+ assets = await parseManifest(manifestFilepath);
81
+ if (!assets) {
82
+ logger.warn('[ssr] - Skipping setup (failed parsing assets manifest)');
83
+ return undefined;
84
+ }
85
+
86
+ const imported = await import(entryFilepath);
87
+ render = imported?.render;
88
+
89
+ if (!render || typeof render !== 'function') {
90
+ logger.warn('[ssr] - index file does not export a render() function. Skipping setup.');
91
+ return undefined;
92
+ }
93
+ } catch (e: any) {
94
+ logger.error(e);
95
+ return undefined;
96
+ }
97
+
98
+ return {
99
+ render,
100
+ assets,
101
+ };
102
+ }
103
+
104
+ async function parseManifest(filepath: string, logger?: Logger) {
105
+ try {
106
+ const file = await fs.readFile(filepath);
107
+ logger?.debug('[ssr] - ✓ aread manifest file');
108
+ const contents = file.toString();
109
+ const parsed: ManifestFile = JSON.parse(contents);
110
+ logger?.debug('[ssr] - ✓ prased manifest file', parsed);
111
+ const assets = getAssets(parsed);
112
+ logger?.debug('[ssr] - ✓ extracted data from manifest file', assets);
113
+
114
+ return assets;
115
+ } catch (e: any) {
116
+ logger?.error('[ssr] - error parsing asset manifest', e);
117
+ process.exit();
118
+ return undefined;
119
+ }
120
+ }
121
+
122
+ function getAssets(manifest: ManifestFile) {
123
+ const assets: Assets = { css: [], js: [] };
124
+
125
+ assets.css = manifest.entrypoints?.filter((x) => x.endsWith('css')).map((x) => path.join('/', x));
126
+ assets.js = manifest.entrypoints?.filter((x) => x.endsWith('js')).map((x) => path.join('/', x));
127
+
128
+ return assets;
129
+ }
@@ -0,0 +1,86 @@
1
+ import type { IncomingHttpHeaders } from 'http';
2
+ import type { Request } from 'express';
3
+
4
+ export type ParsedQuery = { [key: string]: undefined | string | string[] | ParsedQuery | ParsedQuery[] };
5
+
6
+ export type BrowserData = {
7
+ connection: {
8
+ secure: boolean;
9
+ headers: IncomingHttpHeaders;
10
+ body: any;
11
+ };
12
+ /**
13
+ * isomorphic location object, resembling the browser's window.location
14
+ */
15
+ location: {
16
+ /** hostname + port
17
+ * @example localhost:3000
18
+ */
19
+ host: string;
20
+ /**
21
+ * @example localhost
22
+ */
23
+ hostname: string;
24
+ /** full url
25
+ * @example http://localhost:3000/components?q=button
26
+ */
27
+ href: string;
28
+ /** full url without query
29
+ * @example http://localhost:3000/components
30
+ */
31
+ origin: string;
32
+ /**
33
+ * @example /component
34
+ */
35
+ pathname: string;
36
+ /**
37
+ * @example 3000
38
+ */
39
+ port: number;
40
+ /**
41
+ * @example http
42
+ */
43
+ protocol: string;
44
+ /**
45
+ * parsed search params
46
+ * @example { one: 1, two: [2,3]}
47
+ */
48
+ query: ParsedQuery;
49
+ /**
50
+ * full resource path, including query, without hostname
51
+ * @example /components?q=button
52
+ */
53
+ url: string;
54
+ };
55
+ cookie?: string;
56
+ };
57
+
58
+ /**
59
+ * extract relevant information from Express request.
60
+ */
61
+ export function requestToObj(req: Request, port: number) {
62
+ // apparently port is not readily available in request.
63
+
64
+ const browser: BrowserData = {
65
+ connection: {
66
+ secure: req.secure,
67
+ headers: req.headers,
68
+ body: req.body,
69
+ },
70
+ // trying to match to browser.location
71
+ location: {
72
+ host: `${req.hostname}:${port}`,
73
+ hostname: req.hostname,
74
+ href: `${req.protocol}://${req.hostname}:${port}${req.url}`,
75
+ origin: `${req.protocol}://${req.hostname}:${port}`,
76
+ pathname: req.path,
77
+ port,
78
+ protocol: `${req.protocol}:`,
79
+ query: req.query,
80
+ url: req.url,
81
+ },
82
+ cookie: req.header('Cookie'),
83
+ };
84
+
85
+ return browser;
86
+ }
@@ -0,0 +1,10 @@
1
+ import type { Request, Response } from 'express';
2
+
3
+ /**
4
+ * Represents the server configuration for the current request
5
+ */
6
+ export type RequestServer = {
7
+ port: number;
8
+ request: Request;
9
+ response: Response;
10
+ };
@@ -0,0 +1,9 @@
1
+ import type { Assets } from '@teambit/ui-foundation.ui.rendering.html';
2
+ import { BrowserData } from './request-browser';
3
+ import { RequestServer } from './request-server';
4
+
5
+ export type SsrContent = {
6
+ assets?: Assets;
7
+ browser?: BrowserData;
8
+ server?: RequestServer;
9
+ };
package/start.cmd.tsx ADDED
@@ -0,0 +1,93 @@
1
+ import React from 'react';
2
+ import open from 'open';
3
+ import { Command, CommandOptions } from '@teambit/cli';
4
+ import { Logger } from '@teambit/logger';
5
+ import { UIServerConsole } from '@teambit/ui-foundation.cli.ui-server-console';
6
+ import type { UiMain } from './ui.main.runtime';
7
+
8
+ type StartArgs = [uiName: string, userPattern: string];
9
+ type StartFlags = {
10
+ dev: boolean;
11
+ port: string;
12
+ rebuild: boolean;
13
+ verbose: boolean;
14
+ noBrowser: boolean;
15
+ skipCompilation: boolean;
16
+ };
17
+
18
+ export class StartCmd implements Command {
19
+ name = 'start [type] [pattern]';
20
+ description = 'Start a dev environment for a workspace or a specific component';
21
+ alias = 'c';
22
+ group = 'development';
23
+ shortDescription = '';
24
+ options = [
25
+ ['d', 'dev', 'start UI server in dev mode.'],
26
+ ['p', 'port [number]', 'port of the UI server.'],
27
+ ['r', 'rebuild', 'rebuild the UI'],
28
+ ['v', 'verbose', 'showing verbose output for inspection and prints stack trace'],
29
+ ['', 'no-browser', 'do not automatically open browser when ready'],
30
+ ['', 'skip-compilation', 'skip the auto-compilation before starting the web-server'],
31
+ ] as CommandOptions;
32
+
33
+ constructor(
34
+ /**
35
+ * access to the extension instance.
36
+ */
37
+ private ui: UiMain,
38
+
39
+ private logger: Logger
40
+ ) {}
41
+
42
+ // async report([uiRootName, userPattern]: StartArgs, { dev, port, rebuild, verbose }: StartFlags): Promise<string> {
43
+ // this.logger.off();
44
+ // const pattern = userPattern && userPattern.toString();
45
+
46
+ // const uiServer = await this.ui.createRuntime({
47
+ // uiRootName,
48
+ // pattern,
49
+ // dev,
50
+ // port: port ? parseInt(port) : undefined,
51
+ // rebuild,
52
+ // verbose,
53
+ // });
54
+
55
+ // return `Bit server has started on port ${uiServer.port}`;
56
+ // }
57
+
58
+ async render(
59
+ [uiRootName, userPattern]: StartArgs,
60
+ { dev, port, rebuild, verbose, noBrowser, skipCompilation }: StartFlags
61
+ ): Promise<React.ReactElement> {
62
+ this.logger.off();
63
+ const appName = this.ui.getUiName(uiRootName);
64
+ await this.ui.invokePreStart({ skipCompilation });
65
+ const uiServer = this.ui.createRuntime({
66
+ uiRootName,
67
+ pattern: userPattern,
68
+ dev,
69
+ port: +port,
70
+ rebuild,
71
+ verbose,
72
+ });
73
+
74
+ if (!noBrowser) {
75
+ uiServer
76
+ .then(async (server) => {
77
+ if (!server.buildOptions?.launchBrowserOnStart) return undefined;
78
+
79
+ await server.whenReady;
80
+
81
+ return open(this.ui.publicUrl || server.fullUrl);
82
+ })
83
+ .catch((error) => this.logger.error(error));
84
+ }
85
+
86
+ // DO NOT CHANGE THIS - this meant to be an async hook.
87
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
88
+ this.ui.invokeOnStart();
89
+ this.ui.clearConsole();
90
+
91
+ return <UIServerConsole appName={appName} futureUiServer={uiServer} url={this.ui.publicUrl} />;
92
+ }
93
+ }
@@ -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
+ }
@@ -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,28 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { Theme } from '@teambit/base-ui.theme.theme-provider';
3
+ import { IconFont } from '@teambit/design.theme.icons-font';
4
+ import { LoaderRibbon } from '@teambit/base-ui.loaders.loader-ribbon';
5
+ import { Roboto } from '@teambit/base-ui.theme.fonts.roboto';
6
+ import { TooltipMountPoint } from '@teambit/design.ui.tooltip';
7
+
8
+ import { LoaderContext, useLoaderApi } from '@teambit/ui-foundation.ui.global-loader';
9
+ import styles from './client-context.module.scss';
10
+
11
+ export function ClientContext({ children }: { children: ReactNode }) {
12
+ const [loaderApi, isLoading] = useLoaderApi();
13
+
14
+ return (
15
+ <React.StrictMode>
16
+ {/* TODO - try moving LoaderContext to contextSlot, and LoaderRibbon to hudSlot */}
17
+ <LoaderContext.Provider value={loaderApi}>
18
+ <IconFont query="q76y7n" />
19
+ <Theme>
20
+ <Roboto />
21
+ <LoaderRibbon active={isLoading} className={styles.loader} />
22
+ {children}
23
+ <TooltipMountPoint />
24
+ </Theme>
25
+ </LoaderContext.Provider>
26
+ </React.StrictMode>
27
+ );
28
+ }