@player-ui/react 0.3.0-next.3 → 0.3.0-next.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/dist/index.cjs.js +160 -45
- package/dist/index.d.ts +107 -41
- package/dist/index.esm.js +144 -44
- package/package.json +5 -7
- package/src/app.tsx +8 -12
- package/src/asset/index.tsx +49 -0
- package/src/hooks.tsx +13 -13
- package/src/index.tsx +3 -1
- package/src/manager/managed-player.tsx +16 -16
- package/src/manager/request-time.tsx +2 -2
- package/src/manager/types.ts +4 -4
- package/src/player.tsx +51 -48
- package/src/plugins/tapstate-plugin.ts +2 -2
- package/src/utils/helpers.ts +50 -0
- package/src/utils/index.tsx +6 -0
- package/src/utils/player-context.ts +23 -0
- package/src/utils/shared-constants.tsx +15 -0
- package/src/utils/url.ts +22 -0
- package/src/utils/use-asset-props.tsx +9 -0
- package/src/utils/use-logger.ts +14 -0
- package/src/utils/desc.d.ts +0 -2
package/src/player.tsx
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/* eslint-disable react/no-this-in-sfc */
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { SyncWaterfallHook, AsyncParallelHook } from 'tapable-ts';
|
|
4
|
+
import { Subscribe, useSubscribedState } from '@player-ui/react-subscribe';
|
|
5
|
+
import { Registry } from '@player-ui/partial-match-registry';
|
|
3
6
|
import type {
|
|
4
7
|
CompletedState,
|
|
5
8
|
PlayerPlugin,
|
|
@@ -7,25 +10,22 @@ import type {
|
|
|
7
10
|
View,
|
|
8
11
|
} from '@player-ui/player';
|
|
9
12
|
import { Player } from '@player-ui/player';
|
|
10
|
-
import type { AssetRegistryType } from '@player-ui/react-asset';
|
|
11
|
-
import { AssetContext } from '@player-ui/react-asset';
|
|
12
13
|
import { ErrorBoundary } from 'react-error-boundary';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { Registry } from '@player-ui/partial-match-registry';
|
|
14
|
+
import type { AssetRegistryType } from './asset';
|
|
15
|
+
import { AssetContext } from './asset';
|
|
16
|
+
import { PlayerContext } from './utils';
|
|
17
17
|
|
|
18
|
-
import type {
|
|
18
|
+
import type { ReactPlayerProps } from './app';
|
|
19
19
|
import PlayerComp from './app';
|
|
20
20
|
import OnUpdatePlugin from './plugins/onupdate-plugin';
|
|
21
21
|
|
|
22
|
-
const WEB_PLAYER_VERSION = '0.3.0-next.
|
|
23
|
-
const COMMIT = '
|
|
22
|
+
const WEB_PLAYER_VERSION = '0.3.0-next.4';
|
|
23
|
+
const COMMIT = '774a3cf3b0765796901d03a311a06ee68e690a74';
|
|
24
24
|
|
|
25
25
|
export interface DevtoolsGlobals {
|
|
26
26
|
/** A global for a plugin to load to Player for devtools */
|
|
27
27
|
__PLAYER_DEVTOOLS_PLUGIN?: {
|
|
28
|
-
new ():
|
|
28
|
+
new (): ReactPlayerPlugin;
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -34,52 +34,52 @@ export type DevtoolsWindow = typeof window & DevtoolsGlobals;
|
|
|
34
34
|
const _window: DevtoolsWindow | undefined =
|
|
35
35
|
typeof window === 'undefined' ? undefined : window;
|
|
36
36
|
|
|
37
|
-
export interface
|
|
37
|
+
export interface ReactPlayerInfo {
|
|
38
38
|
/** Version of the running player */
|
|
39
39
|
playerVersion: string;
|
|
40
40
|
|
|
41
|
-
/** Version of the running
|
|
42
|
-
|
|
41
|
+
/** Version of the running reactPlayer */
|
|
42
|
+
reactPlayerVersion: string;
|
|
43
43
|
|
|
44
44
|
/** Hash of the HEAD commit used to build the current player version */
|
|
45
45
|
playerCommit: string;
|
|
46
46
|
|
|
47
|
-
/** Hash of the HEAD commit used to build the current
|
|
48
|
-
|
|
47
|
+
/** Hash of the HEAD commit used to build the current reactPlayer version */
|
|
48
|
+
reactPlayerCommit: string;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
export interface
|
|
51
|
+
export interface ReactPlayerPlugin extends Partial<PlayerPlugin> {
|
|
52
52
|
/** The name of this plugin */
|
|
53
53
|
name: string;
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Attach listeners to the web-player instance
|
|
57
57
|
*/
|
|
58
|
-
|
|
58
|
+
applyReact?: (reactPlayer: ReactPlayer) => void;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
export interface
|
|
61
|
+
export interface ReactPlayerOptions {
|
|
62
62
|
/** A headless player instance to use */
|
|
63
63
|
player?: Player;
|
|
64
64
|
|
|
65
65
|
/** A set of plugins to apply to this player */
|
|
66
|
-
plugins?: Array<
|
|
66
|
+
plugins?: Array<ReactPlayerPlugin>;
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
* If the underlying
|
|
70
|
-
* It requires that a `React.Suspense` component handler be somewhere in the `
|
|
69
|
+
* If the underlying reactPlayer.Component should use `React.Suspense` to trigger a loading state while waiting for content or content updates.
|
|
70
|
+
* It requires that a `React.Suspense` component handler be somewhere in the `reactPlayer.Component` hierarchy.
|
|
71
71
|
*/
|
|
72
72
|
suspend?: boolean;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
export type
|
|
75
|
+
export type ReactPlayerComponentProps = Record<string, unknown>;
|
|
76
76
|
|
|
77
|
-
/**
|
|
78
|
-
export class
|
|
79
|
-
public readonly options:
|
|
77
|
+
/** A Player that renders UI through React */
|
|
78
|
+
export class ReactPlayer {
|
|
79
|
+
public readonly options: ReactPlayerOptions;
|
|
80
80
|
public readonly player: Player;
|
|
81
81
|
public readonly assetRegistry: AssetRegistryType = new Registry();
|
|
82
|
-
public readonly Component: React.ComponentType<
|
|
82
|
+
public readonly Component: React.ComponentType<ReactPlayerComponentProps>;
|
|
83
83
|
public readonly hooks = {
|
|
84
84
|
/**
|
|
85
85
|
* A hook to create a React Component to be used for Player, regardless of the current flow state
|
|
@@ -92,7 +92,7 @@ export class WebPlayer {
|
|
|
92
92
|
* Typically this will just be `Asset`
|
|
93
93
|
*/
|
|
94
94
|
playerComponent: new SyncWaterfallHook<
|
|
95
|
-
[React.ComponentType<
|
|
95
|
+
[React.ComponentType<ReactPlayerProps>]
|
|
96
96
|
>(),
|
|
97
97
|
|
|
98
98
|
/**
|
|
@@ -102,9 +102,9 @@ export class WebPlayer {
|
|
|
102
102
|
};
|
|
103
103
|
|
|
104
104
|
private viewUpdateSubscription = new Subscribe<View>();
|
|
105
|
-
private
|
|
105
|
+
private reactPlayerInfo: ReactPlayerInfo;
|
|
106
106
|
|
|
107
|
-
constructor(options?:
|
|
107
|
+
constructor(options?: ReactPlayerOptions) {
|
|
108
108
|
this.options = options ?? {};
|
|
109
109
|
|
|
110
110
|
// Default the suspend option to `true` unless explicitly unset
|
|
@@ -131,62 +131,62 @@ export class WebPlayer {
|
|
|
131
131
|
this.player = options?.player ?? new Player({ plugins: playerPlugins });
|
|
132
132
|
|
|
133
133
|
plugins.forEach((plugin) => {
|
|
134
|
-
if (plugin.
|
|
135
|
-
plugin.
|
|
134
|
+
if (plugin.applyReact) {
|
|
135
|
+
plugin.applyReact(this);
|
|
136
136
|
}
|
|
137
137
|
});
|
|
138
138
|
|
|
139
139
|
onUpdatePlugin.apply(this.player);
|
|
140
140
|
|
|
141
141
|
this.Component = this.hooks.webComponent.call(this.createReactComp());
|
|
142
|
-
this.
|
|
142
|
+
this.reactPlayerInfo = {
|
|
143
143
|
playerVersion: this.player.getVersion(),
|
|
144
144
|
playerCommit: this.player.getCommit(),
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
reactPlayerVersion: WEB_PLAYER_VERSION,
|
|
146
|
+
reactPlayerCommit: COMMIT,
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/** Returns the current version of the underlying core Player */
|
|
151
151
|
public getPlayerVersion(): string {
|
|
152
|
-
return this.
|
|
152
|
+
return this.reactPlayerInfo.playerVersion;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
/** Returns the git commit used to build this core Player version */
|
|
156
156
|
public getPlayerCommit(): string {
|
|
157
|
-
return this.
|
|
157
|
+
return this.reactPlayerInfo.playerCommit;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
/** Find instance of [Plugin] that has been registered to the web player */
|
|
161
|
-
public findPlugin<Plugin extends
|
|
161
|
+
public findPlugin<Plugin extends ReactPlayerPlugin>(
|
|
162
162
|
symbol: symbol
|
|
163
163
|
): Plugin | undefined {
|
|
164
164
|
return this.options.plugins?.find((el) => el.symbol === symbol) as Plugin;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
/** Register and apply [Plugin] if one with the same symbol is not already registered. */
|
|
168
|
-
public registerPlugin(plugin:
|
|
169
|
-
if (!plugin.
|
|
168
|
+
public registerPlugin(plugin: ReactPlayerPlugin): void {
|
|
169
|
+
if (!plugin.applyReact) return;
|
|
170
170
|
|
|
171
|
-
plugin.
|
|
171
|
+
plugin.applyReact(this);
|
|
172
172
|
this.options.plugins?.push(plugin);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
/** Returns the current version of the running React Player */
|
|
176
|
-
public
|
|
177
|
-
return this.
|
|
176
|
+
public getReactPlayerVersion(): string {
|
|
177
|
+
return this.reactPlayerInfo.reactPlayerVersion;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
/** Returns the git commit used to build the React Player version */
|
|
181
|
-
public
|
|
182
|
-
return this.
|
|
181
|
+
public getReactPlayerCommit(): string {
|
|
182
|
+
return this.reactPlayerInfo.reactPlayerCommit;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
private createReactComp(): React.ComponentType<
|
|
185
|
+
private createReactComp(): React.ComponentType<ReactPlayerComponentProps> {
|
|
186
186
|
const ActualPlayerComp = this.hooks.playerComponent.call(PlayerComp);
|
|
187
187
|
|
|
188
188
|
/** the component to use to render Player */
|
|
189
|
-
const
|
|
189
|
+
const ReactPlayerComponent = () => {
|
|
190
190
|
const view = useSubscribedState<View>(this.viewUpdateSubscription);
|
|
191
191
|
|
|
192
192
|
if (this.options.suspend) {
|
|
@@ -217,11 +217,11 @@ export class WebPlayer {
|
|
|
217
217
|
);
|
|
218
218
|
};
|
|
219
219
|
|
|
220
|
-
return
|
|
220
|
+
return ReactPlayerComponent;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
/**
|
|
224
|
-
* Call this method to force the
|
|
224
|
+
* Call this method to force the ReactPlayer to wait for the next view-update before performing the next render.
|
|
225
225
|
* If the `suspense` option is set, this will suspend while an update is pending, otherwise nothing will be rendered.
|
|
226
226
|
*/
|
|
227
227
|
public setWaitForNextViewUpdate() {
|
|
@@ -244,3 +244,6 @@ export class WebPlayer {
|
|
|
244
244
|
});
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
|
+
|
|
248
|
+
// For compatibility
|
|
249
|
+
export const WebPlayer = ReactPlayer;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { PlayerFlowState, Player } from '@player-ui/player';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ReactPlayerPlugin } from '../player';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* A plugin to tap into state transition changes and call an arbitrary update function
|
|
6
6
|
*/
|
|
7
|
-
export class StateTapPlugin implements
|
|
7
|
+
export class StateTapPlugin implements ReactPlayerPlugin {
|
|
8
8
|
name = 'statetap';
|
|
9
9
|
private callbackFunction: (state: PlayerFlowState) => void;
|
|
10
10
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trim leading and trailing slashes from string
|
|
3
|
+
*/
|
|
4
|
+
export function trimSlashes(str: string) {
|
|
5
|
+
return str.replace(/^\/+|\/+$/g, '');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Removes any key: value pairs from an object when the value is null or undefined
|
|
10
|
+
*/
|
|
11
|
+
export function removeEmptyValuesFromObject(
|
|
12
|
+
obj: Record<string, any>
|
|
13
|
+
): Record<string, NonNullable<any>> {
|
|
14
|
+
return Object.keys(obj).reduce((acc, key) => {
|
|
15
|
+
const value = obj[key];
|
|
16
|
+
|
|
17
|
+
if (value !== null && value !== undefined) {
|
|
18
|
+
acc[key] = value;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return acc;
|
|
22
|
+
}, {} as Record<string, any>);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Check if the object has no keys */
|
|
26
|
+
export function isEmptyObject(obj: Record<string, unknown>) {
|
|
27
|
+
return Object.keys(obj).length === 0 && obj.constructor === Object;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Check if the argument is a function */
|
|
31
|
+
export function isFunction<ReturnType>(
|
|
32
|
+
maybeFn: ReturnType | ((...args: unknown[]) => ReturnType)
|
|
33
|
+
): maybeFn is (...args: unknown[]) => ReturnType {
|
|
34
|
+
return Boolean(maybeFn instanceof Function || typeof maybeFn === 'function');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Calls function with provided data or returns original value
|
|
39
|
+
*/
|
|
40
|
+
export function callOrReturn<
|
|
41
|
+
ReturnType,
|
|
42
|
+
FnArgs extends Array<unknown> = unknown[],
|
|
43
|
+
FnType = (...args: FnArgs) => ReturnType
|
|
44
|
+
>(maybeFn: FnType | ReturnType, fnArgs: FnArgs): ReturnType {
|
|
45
|
+
if (isFunction(maybeFn)) {
|
|
46
|
+
return maybeFn(fnArgs) as ReturnType;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return maybeFn as ReturnType;
|
|
50
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Player, NavigationFlowViewState } from '@player-ui/player';
|
|
3
|
+
|
|
4
|
+
export interface PlayerContextType {
|
|
5
|
+
/**
|
|
6
|
+
* An instance of a headless player
|
|
7
|
+
*/
|
|
8
|
+
player?: Player;
|
|
9
|
+
|
|
10
|
+
/** The currently rendered view state */
|
|
11
|
+
viewState?: NavigationFlowViewState;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const PlayerContext = React.createContext<PlayerContextType>({});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A hook to get the current player
|
|
18
|
+
*/
|
|
19
|
+
export const usePlayer = () => {
|
|
20
|
+
const { player } = React.useContext(PlayerContext);
|
|
21
|
+
|
|
22
|
+
return player;
|
|
23
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { usePlayer } from './player-context';
|
|
2
|
+
|
|
3
|
+
/** Hook to get a constant under a specific namespace */
|
|
4
|
+
export function useGetConstantByType(type: string, key: string): unknown {
|
|
5
|
+
const player = usePlayer();
|
|
6
|
+
|
|
7
|
+
return player?.constantsController.getConstants(key, type);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Get a constant under the default namespace */
|
|
11
|
+
export function useGetConstant(key: string): unknown {
|
|
12
|
+
const player = usePlayer();
|
|
13
|
+
|
|
14
|
+
return player?.constantsController.getConstants(key, 'constants');
|
|
15
|
+
}
|
package/src/utils/url.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isEmptyObject } from './helpers';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Combines a URL with any additional parameters
|
|
5
|
+
*/
|
|
6
|
+
export function buildUrl(
|
|
7
|
+
url: string,
|
|
8
|
+
params: Record<string, unknown> = {}
|
|
9
|
+
): string {
|
|
10
|
+
const baseUrl = new URL(url);
|
|
11
|
+
|
|
12
|
+
if (params && isEmptyObject(params)) {
|
|
13
|
+
return baseUrl.toString();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
Object.keys(params).forEach((key) => {
|
|
17
|
+
const value = params[key];
|
|
18
|
+
baseUrl.searchParams.append(key, String(value));
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return baseUrl.toString();
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Logger } from '@player-ui/player';
|
|
2
|
+
import { NoopLogger } from '@player-ui/player';
|
|
3
|
+
import { usePlayer } from './player-context';
|
|
4
|
+
|
|
5
|
+
const noopLogger = new NoopLogger();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A hook to get the logger instance from the current player
|
|
9
|
+
*/
|
|
10
|
+
export function useLogger(): Logger {
|
|
11
|
+
const player = usePlayer();
|
|
12
|
+
|
|
13
|
+
return player?.logger ?? noopLogger;
|
|
14
|
+
}
|
package/src/utils/desc.d.ts
DELETED