@adobe/uix-host-react 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +201 -0
- package/README.md +1 -0
- package/dist/components/Extensible.d.ts +41 -0
- package/dist/components/Extensible.d.ts.map +1 -0
- package/dist/components/GuestUIFrame.d.ts +43 -0
- package/dist/components/GuestUIFrame.d.ts.map +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/esm/index.js +194 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/extension-context.d.ts +4 -0
- package/dist/extension-context.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/useExtensions.d.ts +78 -0
- package/dist/hooks/useExtensions.d.ts.map +1 -0
- package/dist/hooks/useHost.d.ts +25 -0
- package/dist/hooks/useHost.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +203 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
- package/src/components/Extensible.tsx +133 -0
- package/src/components/GuestUIFrame.tsx +121 -0
- package/src/components/index.ts +14 -0
- package/src/extension-context.ts +19 -0
- package/src/hooks/index.ts +13 -0
- package/src/hooks/useExtensions.ts +170 -0
- package/src/hooks/useHost.ts +52 -0
- package/src/index.ts +15 -0
- package/tsconfig.json +24 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
/*
|
2
|
+
Copyright 2022 Adobe. All rights reserved.
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
10
|
+
governing permissions and limitations under the License.
|
11
|
+
*/
|
12
|
+
|
13
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
14
|
+
import type {
|
15
|
+
GuestConnection,
|
16
|
+
GuestApis,
|
17
|
+
RemoteGuestApis,
|
18
|
+
VirtualApi,
|
19
|
+
} from "@adobe/uix-core";
|
20
|
+
|
21
|
+
import { Host, HostEvents } from "@adobe/uix-host";
|
22
|
+
import type { CapabilitySpec } from "@adobe/uix-host";
|
23
|
+
import { useHost } from "./useHost.js";
|
24
|
+
|
25
|
+
/**
|
26
|
+
* @internal
|
27
|
+
*/
|
28
|
+
export interface TypedGuestConnection<T extends GuestApis>
|
29
|
+
extends GuestConnection {
|
30
|
+
id: GuestConnection["id"];
|
31
|
+
apis: RemoteGuestApis<T>;
|
32
|
+
}
|
33
|
+
|
34
|
+
/** @public */
|
35
|
+
export interface UseExtensionsConfig<
|
36
|
+
Incoming extends GuestApis,
|
37
|
+
Outgoing extends VirtualApi
|
38
|
+
> {
|
39
|
+
/**
|
40
|
+
* A {@link @adobe/uix-host#CapabilitySpec} describing the namespaced methods
|
41
|
+
* extensions must implement to be used by this component.
|
42
|
+
*
|
43
|
+
* @remarks
|
44
|
+
* This declaration is used to filter the extensions that will be
|
45
|
+
* returned; if they don't implement those methods, they will be filtered out.
|
46
|
+
*/
|
47
|
+
requires?: CapabilitySpec<Incoming>;
|
48
|
+
/**
|
49
|
+
* A namespaced object of methods which extensions will be able to call.
|
50
|
+
*
|
51
|
+
* @remarks This is the counterpart of `requires`; in `requires`, the you
|
52
|
+
* describes methods the extension must implement that your host code will
|
53
|
+
* call, and in `provides`, you implement host methods that extensions will be
|
54
|
+
* able to call.
|
55
|
+
*
|
56
|
+
* Most cases for host-side methods will use the state of the component. This
|
57
|
+
* can cause unexpected bugs in React if the config callback is run on every
|
58
|
+
* render. **useExtensions caches the config callback by default!**
|
59
|
+
* So remember to pass a deps array, so that the config callback re-runs under
|
60
|
+
* the right conditions.
|
61
|
+
*/
|
62
|
+
provides?: Outgoing;
|
63
|
+
/**
|
64
|
+
* Sets when re-render is triggered on extension load.
|
65
|
+
*
|
66
|
+
* @remarks
|
67
|
+
* Set to `each` to trigger a component re-render every time an individual
|
68
|
+
* extension loads, which may result in multiple UI updates. Set to `all` to
|
69
|
+
* wait until all extensions have loaded to re-render the component.
|
70
|
+
* @defaultValue "each"
|
71
|
+
*/
|
72
|
+
updateOn?: "each" | "all";
|
73
|
+
}
|
74
|
+
|
75
|
+
/** @public */
|
76
|
+
export interface UseExtensionsResult<T extends GuestApis> {
|
77
|
+
/**
|
78
|
+
* A list of loaded guests which implement the methods specified in
|
79
|
+
* `requires`, represented as {@link @adobe/uix-host#Port} objects which
|
80
|
+
* present methods to be called.
|
81
|
+
*/
|
82
|
+
extensions: TypedGuestConnection<T>[];
|
83
|
+
/**
|
84
|
+
* This is `true` until all extensions are loaded. Use for rendering spinners
|
85
|
+
* or other intermediate UI.
|
86
|
+
*/
|
87
|
+
loading: boolean;
|
88
|
+
/**
|
89
|
+
* Populated with an Error if there were any problems during the load process.
|
90
|
+
*/
|
91
|
+
error?: Error;
|
92
|
+
}
|
93
|
+
|
94
|
+
const NO_EXTENSIONS: [] = [];
|
95
|
+
|
96
|
+
/**
|
97
|
+
* Fetch extensions which implement an API, provide them methods, and use them.
|
98
|
+
*
|
99
|
+
* @remarks `useExtensions` does three things at once:
|
100
|
+
* - Gets all extensions which implement the APIs described in the `require` field
|
101
|
+
* - Exposes any functions defined in the `provide` field to those extensions
|
102
|
+
* - Returns an object whose `extensions` property is a list of `Port` objects representing those extensions
|
103
|
+
*
|
104
|
+
* useExtensions will trigger a re-render when extensions load. You can choose whether it triggers that rerender as each extension loads, or only after all extensions have loaded.
|
105
|
+
* @public
|
106
|
+
*/
|
107
|
+
export function useExtensions<
|
108
|
+
Incoming extends GuestApis,
|
109
|
+
Outgoing extends VirtualApi
|
110
|
+
>(
|
111
|
+
configFactory: (host: Host) => UseExtensionsConfig<Incoming, Outgoing>,
|
112
|
+
deps: unknown[] = []
|
113
|
+
): UseExtensionsResult<Incoming> {
|
114
|
+
const { host, error } = useHost();
|
115
|
+
if (error) {
|
116
|
+
return {
|
117
|
+
extensions: NO_EXTENSIONS,
|
118
|
+
loading: false,
|
119
|
+
error,
|
120
|
+
};
|
121
|
+
}
|
122
|
+
|
123
|
+
const baseDeps = [host, ...deps];
|
124
|
+
const {
|
125
|
+
requires,
|
126
|
+
provides,
|
127
|
+
updateOn = "each",
|
128
|
+
} = useMemo(() => configFactory(host), baseDeps);
|
129
|
+
|
130
|
+
const getExtensions = useCallback(() => {
|
131
|
+
const newExtensions = [];
|
132
|
+
const guests = host.getLoadedGuests(requires);
|
133
|
+
for (const guest of guests) {
|
134
|
+
if (provides) {
|
135
|
+
guest.provide(provides);
|
136
|
+
}
|
137
|
+
newExtensions.push(guest as unknown as TypedGuestConnection<Incoming>);
|
138
|
+
}
|
139
|
+
return newExtensions.length === 0 ? NO_EXTENSIONS : newExtensions;
|
140
|
+
}, [...baseDeps, requires]);
|
141
|
+
|
142
|
+
const subscribe = useCallback(
|
143
|
+
(handler: EventListener) => {
|
144
|
+
const eventName = updateOn === "all" ? "loadallguests" : "guestload";
|
145
|
+
host.addEventListener(eventName, handler);
|
146
|
+
return () => host.removeEventListener(eventName, handler);
|
147
|
+
},
|
148
|
+
[...baseDeps, updateOn]
|
149
|
+
);
|
150
|
+
|
151
|
+
const [extensions, setExtensions] = useState(() => getExtensions());
|
152
|
+
useEffect(() => {
|
153
|
+
return subscribe(() => {
|
154
|
+
setExtensions(getExtensions());
|
155
|
+
});
|
156
|
+
}, [subscribe, getExtensions]);
|
157
|
+
|
158
|
+
const [hostError, setHostError] = useState<Error>();
|
159
|
+
useEffect(
|
160
|
+
() =>
|
161
|
+
host.addEventListener(
|
162
|
+
"error",
|
163
|
+
(event: Extract<HostEvents, { detail: { error: Error } }>) =>
|
164
|
+
setHostError(event.detail.error)
|
165
|
+
),
|
166
|
+
baseDeps
|
167
|
+
);
|
168
|
+
|
169
|
+
return { extensions, loading: !host.loading, error: hostError };
|
170
|
+
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
/*
|
2
|
+
Copyright 2022 Adobe. All rights reserved.
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
10
|
+
governing permissions and limitations under the License.
|
11
|
+
*/
|
12
|
+
|
13
|
+
import { useContext } from "react";
|
14
|
+
import { Host } from "@adobe/uix-host";
|
15
|
+
import { ExtensionContext } from "../extension-context.js";
|
16
|
+
|
17
|
+
/**
|
18
|
+
* @public
|
19
|
+
*/
|
20
|
+
export class OutsideOfExtensionContextError extends Error {
|
21
|
+
outsideOfExtensionContext: boolean;
|
22
|
+
constructor(msg: string) {
|
23
|
+
super(msg);
|
24
|
+
this.outsideOfExtensionContext = true;
|
25
|
+
Object.setPrototypeOf(this, OutsideOfExtensionContextError.prototype);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
/** @public */
|
30
|
+
type UseHostResponse =
|
31
|
+
| { host: undefined; error: Error }
|
32
|
+
| { host: Host; error: undefined };
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Retrieve the {@link @adobe/uix-host#Host} object hosting all extensions inside the current parent provider.
|
36
|
+
*
|
37
|
+
* @remarks Returns a `{ host, error }` tuple, not the host object directly.
|
38
|
+
* @beta
|
39
|
+
*/
|
40
|
+
export function useHost(): UseHostResponse {
|
41
|
+
const host = useContext(ExtensionContext);
|
42
|
+
if (!(host instanceof Host)) {
|
43
|
+
const error = new OutsideOfExtensionContextError(
|
44
|
+
"Attempt to use extensions outside of ExtensionContext. Wrap extensible part of application with Extensible component."
|
45
|
+
);
|
46
|
+
return {
|
47
|
+
host: undefined,
|
48
|
+
error,
|
49
|
+
};
|
50
|
+
}
|
51
|
+
return { error: undefined, host };
|
52
|
+
}
|
package/src/index.ts
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
Copyright 2022 Adobe. All rights reserved.
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
10
|
+
governing permissions and limitations under the License.
|
11
|
+
*/
|
12
|
+
|
13
|
+
export * from "@adobe/uix-host";
|
14
|
+
export * from "./components/index.js";
|
15
|
+
export * from "./hooks/index.js";
|
package/tsconfig.json
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json.schemastore.org/tsconfig",
|
3
|
+
"extends": "../../tsconfig-base.json",
|
4
|
+
"compilerOptions": {
|
5
|
+
"jsx": "react",
|
6
|
+
"outDir": "dist",
|
7
|
+
"rootDir": "src"
|
8
|
+
},
|
9
|
+
"include": [
|
10
|
+
"src/**/*"
|
11
|
+
],
|
12
|
+
"exclude": [
|
13
|
+
"src/**/*.test.tsx?"
|
14
|
+
],
|
15
|
+
"references": [
|
16
|
+
{
|
17
|
+
"path": "../uix-core"
|
18
|
+
},
|
19
|
+
{
|
20
|
+
"path": "../uix-host"
|
21
|
+
}
|
22
|
+
]
|
23
|
+
}
|
24
|
+
|