@cratis/scene.react 4.12.0 → 4.14.0
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/cjs/core/coreComponents.js +2 -1
- package/dist/cjs/core/coreComponents.js.map +1 -1
- package/dist/cjs/core/coreDescriptors.js +1 -0
- package/dist/cjs/core/coreDescriptors.js.map +1 -1
- package/dist/cjs/index.js +27 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/navigation/SceneNavigationContext.js.map +1 -1
- package/dist/cjs/navigation/SceneNavigationHost.js +19 -44
- package/dist/cjs/navigation/SceneNavigationHost.js.map +1 -1
- package/dist/cjs/navigation/createBrowserSceneHistory.js +39 -0
- package/dist/cjs/navigation/createBrowserSceneHistory.js.map +1 -0
- package/dist/cjs/navigation/createMemorySceneHistory.js +53 -0
- package/dist/cjs/navigation/createMemorySceneHistory.js.map +1 -0
- package/dist/cjs/navigation/diagnoseRenderedNavigation.js +17 -0
- package/dist/cjs/navigation/diagnoseRenderedNavigation.js.map +1 -0
- package/dist/cjs/navigation/identityRoute.js +19 -0
- package/dist/cjs/navigation/identityRoute.js.map +1 -0
- package/dist/cjs/navigation/matchSceneRoute.js +43 -0
- package/dist/cjs/navigation/matchSceneRoute.js.map +1 -0
- package/dist/cjs/navigation/navigationRouteKey.js +19 -0
- package/dist/cjs/navigation/navigationRouteKey.js.map +1 -0
- package/dist/cjs/navigation/resolveDestination.js +3 -10
- package/dist/cjs/navigation/resolveDestination.js.map +1 -1
- package/dist/cjs/navigation/sceneRoutesFrom.js +37 -0
- package/dist/cjs/navigation/sceneRoutesFrom.js.map +1 -0
- package/dist/cjs/navigation/useSceneHistoryNavigation.js +112 -0
- package/dist/cjs/navigation/useSceneHistoryNavigation.js.map +1 -0
- package/dist/cjs/packages/DesignTimeContractVersion.js +15 -0
- package/dist/cjs/packages/DesignTimeContractVersion.js.map +1 -0
- package/dist/cjs/packages/DesignTimeExtensionPoint.js +26 -0
- package/dist/cjs/packages/DesignTimeExtensionPoint.js.map +1 -0
- package/dist/cjs/packages/PackageHostConfiguration.js +1 -0
- package/dist/cjs/packages/PackageHostConfiguration.js.map +1 -1
- package/dist/cjs/packages/loadDesignTimeContributions.js +80 -0
- package/dist/cjs/packages/loadDesignTimeContributions.js.map +1 -0
- package/dist/cjs/packages/resolveDesignTimeHost.js +114 -0
- package/dist/cjs/packages/resolveDesignTimeHost.js.map +1 -0
- package/dist/cjs/packages/runDesignTimeAction.js +50 -0
- package/dist/cjs/packages/runDesignTimeAction.js.map +1 -0
- package/dist/esm/core/coreComponents.js +2 -1
- package/dist/esm/core/coreComponents.js.map +1 -1
- package/dist/esm/core/coreDescriptors.js +1 -0
- package/dist/esm/core/coreDescriptors.js.map +1 -1
- package/dist/esm/index.js +13 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/navigation/DestinationResolution.d.ts +1 -0
- package/dist/esm/navigation/DestinationResolution.d.ts.map +1 -1
- package/dist/esm/navigation/DialogResult.d.ts +5 -0
- package/dist/esm/navigation/DialogResult.d.ts.map +1 -0
- package/dist/esm/navigation/DialogResult.js +2 -0
- package/dist/esm/navigation/DialogResult.js.map +1 -0
- package/dist/esm/navigation/SceneHistory.d.ts +10 -0
- package/dist/esm/navigation/SceneHistory.d.ts.map +1 -0
- package/dist/esm/navigation/SceneHistory.js +2 -0
- package/dist/esm/navigation/SceneHistory.js.map +1 -0
- package/dist/esm/navigation/SceneNavigationContext.d.ts +6 -1
- package/dist/esm/navigation/SceneNavigationContext.d.ts.map +1 -1
- package/dist/esm/navigation/SceneNavigationContext.js.map +1 -1
- package/dist/esm/navigation/SceneNavigationHost.d.ts +7 -2
- package/dist/esm/navigation/SceneNavigationHost.d.ts.map +1 -1
- package/dist/esm/navigation/SceneNavigationHost.js +20 -45
- package/dist/esm/navigation/SceneNavigationHost.js.map +1 -1
- package/dist/esm/navigation/SceneNavigationState.d.ts +8 -0
- package/dist/esm/navigation/SceneNavigationState.d.ts.map +1 -0
- package/dist/esm/navigation/SceneNavigationState.js +2 -0
- package/dist/esm/navigation/SceneNavigationState.js.map +1 -0
- package/dist/esm/navigation/SceneRoute.d.ts +6 -0
- package/dist/esm/navigation/SceneRoute.d.ts.map +1 -0
- package/dist/esm/navigation/SceneRoute.js +2 -0
- package/dist/esm/navigation/SceneRoute.js.map +1 -0
- package/dist/esm/navigation/createBrowserSceneHistory.d.ts +4 -0
- package/dist/esm/navigation/createBrowserSceneHistory.d.ts.map +1 -0
- package/dist/esm/navigation/createBrowserSceneHistory.js +37 -0
- package/dist/esm/navigation/createBrowserSceneHistory.js.map +1 -0
- package/dist/esm/navigation/createMemorySceneHistory.d.ts +7 -0
- package/dist/esm/navigation/createMemorySceneHistory.d.ts.map +1 -0
- package/dist/esm/navigation/createMemorySceneHistory.js +51 -0
- package/dist/esm/navigation/createMemorySceneHistory.js.map +1 -0
- package/dist/esm/navigation/diagnoseRenderedNavigation.d.ts +3 -0
- package/dist/esm/navigation/diagnoseRenderedNavigation.d.ts.map +1 -0
- package/dist/esm/navigation/diagnoseRenderedNavigation.js +15 -0
- package/dist/esm/navigation/diagnoseRenderedNavigation.js.map +1 -0
- package/dist/esm/navigation/identityRoute.d.ts +3 -0
- package/dist/esm/navigation/identityRoute.d.ts.map +1 -0
- package/dist/esm/navigation/identityRoute.js +17 -0
- package/dist/esm/navigation/identityRoute.js.map +1 -0
- package/dist/esm/navigation/index.d.ts +12 -0
- package/dist/esm/navigation/index.d.ts.map +1 -1
- package/dist/esm/navigation/index.js +12 -0
- package/dist/esm/navigation/index.js.map +1 -1
- package/dist/esm/navigation/matchSceneRoute.d.ts +7 -0
- package/dist/esm/navigation/matchSceneRoute.d.ts.map +1 -0
- package/dist/esm/navigation/matchSceneRoute.js +41 -0
- package/dist/esm/navigation/matchSceneRoute.js.map +1 -0
- package/dist/esm/navigation/navigationRouteKey.d.ts +3 -0
- package/dist/esm/navigation/navigationRouteKey.d.ts.map +1 -0
- package/dist/esm/navigation/navigationRouteKey.js +17 -0
- package/dist/esm/navigation/navigationRouteKey.js.map +1 -0
- package/dist/esm/navigation/resolveDestination.d.ts.map +1 -1
- package/dist/esm/navigation/resolveDestination.js +2 -9
- package/dist/esm/navigation/resolveDestination.js.map +1 -1
- package/dist/esm/navigation/sceneRoutesFrom.d.ts +4 -0
- package/dist/esm/navigation/sceneRoutesFrom.d.ts.map +1 -0
- package/dist/esm/navigation/sceneRoutesFrom.js +35 -0
- package/dist/esm/navigation/sceneRoutesFrom.js.map +1 -0
- package/dist/esm/navigation/useSceneHistoryNavigation.d.ts +6 -0
- package/dist/esm/navigation/useSceneHistoryNavigation.d.ts.map +1 -0
- package/dist/esm/navigation/useSceneHistoryNavigation.js +110 -0
- package/dist/esm/navigation/useSceneHistoryNavigation.js.map +1 -0
- package/dist/esm/packages/DesignTimeActionOutcome.d.ts +7 -0
- package/dist/esm/packages/DesignTimeActionOutcome.d.ts.map +1 -0
- package/dist/esm/packages/DesignTimeActionOutcome.js +2 -0
- package/dist/esm/packages/DesignTimeActionOutcome.js.map +1 -0
- package/dist/esm/packages/DesignTimeActionState.d.ts +9 -0
- package/dist/esm/packages/DesignTimeActionState.d.ts.map +1 -0
- package/dist/esm/packages/DesignTimeActionState.js +2 -0
- package/dist/esm/packages/DesignTimeActionState.js.map +1 -0
- package/dist/esm/packages/DesignTimeContractVersion.d.ts +2 -0
- package/dist/esm/packages/DesignTimeContractVersion.d.ts.map +1 -0
- package/dist/esm/packages/DesignTimeContractVersion.js +13 -0
- package/dist/esm/packages/DesignTimeContractVersion.js.map +1 -0
- package/dist/esm/packages/DesignTimeExtensionPoint.d.ts +9 -0
- package/dist/esm/packages/DesignTimeExtensionPoint.d.ts.map +1 -0
- package/dist/esm/packages/DesignTimeExtensionPoint.js +23 -0
- package/dist/esm/packages/DesignTimeExtensionPoint.js.map +1 -0
- package/dist/esm/packages/DesignTimeResolution.d.ts +7 -0
- package/dist/esm/packages/DesignTimeResolution.d.ts.map +1 -0
- package/dist/esm/packages/DesignTimeResolution.js +2 -0
- package/dist/esm/packages/DesignTimeResolution.js.map +1 -0
- package/dist/esm/packages/PackageHostConfiguration.d.ts +3 -0
- package/dist/esm/packages/PackageHostConfiguration.d.ts.map +1 -1
- package/dist/esm/packages/PackageHostConfiguration.js +2 -1
- package/dist/esm/packages/PackageHostConfiguration.js.map +1 -1
- package/dist/esm/packages/index.d.ts +8 -0
- package/dist/esm/packages/index.d.ts.map +1 -1
- package/dist/esm/packages/index.js +8 -0
- package/dist/esm/packages/index.js.map +1 -1
- package/dist/esm/packages/loadDesignTimeContributions.d.ts +11 -0
- package/dist/esm/packages/loadDesignTimeContributions.d.ts.map +1 -0
- package/dist/esm/packages/loadDesignTimeContributions.js +78 -0
- package/dist/esm/packages/loadDesignTimeContributions.js.map +1 -0
- package/dist/esm/packages/resolveDesignTimeHost.d.ts +26 -0
- package/dist/esm/packages/resolveDesignTimeHost.d.ts.map +1 -0
- package/dist/esm/packages/resolveDesignTimeHost.js +112 -0
- package/dist/esm/packages/resolveDesignTimeHost.js.map +1 -0
- package/dist/esm/packages/runDesignTimeAction.d.ts +4 -0
- package/dist/esm/packages/runDesignTimeAction.d.ts.map +1 -0
- package/dist/esm/packages/runDesignTimeAction.js +48 -0
- package/dist/esm/packages/runDesignTimeAction.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/navigation/DestinationResolution.ts +3 -0
- package/navigation/DialogResult.ts +8 -0
- package/navigation/SceneHistory.ts +24 -0
- package/navigation/SceneNavigationContext.tsx +16 -1
- package/navigation/SceneNavigationHost.tsx +30 -40
- package/navigation/SceneNavigationState.ts +19 -0
- package/navigation/SceneRoute.ts +12 -0
- package/navigation/createBrowserSceneHistory.ts +40 -0
- package/navigation/createMemorySceneHistory.ts +44 -0
- package/navigation/diagnoseRenderedNavigation.ts +14 -0
- package/navigation/identityRoute.ts +14 -0
- package/navigation/index.ts +12 -0
- package/navigation/matchSceneRoute.ts +45 -0
- package/navigation/navigationRouteKey.ts +22 -0
- package/navigation/resolveDestination.ts +2 -6
- package/navigation/sceneRoutesFrom.ts +24 -0
- package/navigation/useSceneHistoryNavigation.ts +80 -0
- package/package.json +5 -5
- package/packages/DesignTimeActionOutcome.ts +18 -0
- package/packages/DesignTimeActionState.ts +21 -0
- package/packages/DesignTimeContractVersion.ts +12 -0
- package/packages/DesignTimeExtensionPoint.ts +23 -0
- package/packages/DesignTimeResolution.ts +23 -0
- package/packages/PackageHostConfiguration.ts +11 -1
- package/packages/index.ts +8 -0
- package/packages/loadDesignTimeContributions.ts +98 -0
- package/packages/resolveDesignTimeHost.ts +133 -0
- package/packages/runDesignTimeAction.ts +36 -0
|
@@ -8,6 +8,9 @@ export interface DestinationResolution {
|
|
|
8
8
|
url?: string;
|
|
9
9
|
outlet?: string;
|
|
10
10
|
dialog?: string;
|
|
11
|
+
|
|
12
|
+
/** The resolved route parameters, by name, before they are written into the URL. */
|
|
13
|
+
parameters: Record<string, string>;
|
|
11
14
|
action: 'navigate' | 'openDialog' | 'openExternal';
|
|
12
15
|
diagnostics: string[];
|
|
13
16
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
/** What a dialog returned when it closed, for the screen that opened it. */
|
|
5
|
+
export interface DialogResult {
|
|
6
|
+
dialog: string;
|
|
7
|
+
result?: unknown;
|
|
8
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { SceneNavigationState } from './SceneNavigationState';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The history a navigation host records its entries in. The browser implementation drives `window.history`,
|
|
8
|
+
* so deep links, refresh and the browser's back and forward buttons work; the memory implementation keeps
|
|
9
|
+
* the same stack for embedded and design-time hosts that do not own the address bar.
|
|
10
|
+
*/
|
|
11
|
+
export interface SceneHistory {
|
|
12
|
+
/** The current URL relative to the base path, or undefined at the base path itself. */
|
|
13
|
+
location(): string | undefined;
|
|
14
|
+
|
|
15
|
+
/** The state stored with the current entry, if the host wrote it. */
|
|
16
|
+
state(): SceneNavigationState | undefined;
|
|
17
|
+
|
|
18
|
+
push(state: SceneNavigationState): void;
|
|
19
|
+
replace(state: SceneNavigationState): void;
|
|
20
|
+
back(): void;
|
|
21
|
+
|
|
22
|
+
/** Called with the entry's URL and stored state after back or forward. Returns the unsubscribe function. */
|
|
23
|
+
listen(listener: (url: string | undefined, state: SceneNavigationState | undefined) => void): () => void;
|
|
24
|
+
}
|
|
@@ -5,15 +5,30 @@ import { ReactNode, createContext, useContext } from 'react';
|
|
|
5
5
|
import { BindingScope } from '@cratis/scene.engine';
|
|
6
6
|
import { DestinationReference } from '@cratis/scene.model';
|
|
7
7
|
import { DestinationResolution } from './DestinationResolution';
|
|
8
|
+
import { DialogResult } from './DialogResult';
|
|
8
9
|
|
|
9
10
|
export interface SceneNavigationContextValue {
|
|
10
11
|
currentScreen: string;
|
|
11
12
|
currentUrl?: string;
|
|
12
13
|
currentOutlet?: string;
|
|
13
14
|
currentDialog?: string;
|
|
15
|
+
|
|
16
|
+
/** The route parameters of the current entry - from the destination that opened it, or the deep link. */
|
|
17
|
+
currentParameters: Record<string, string>;
|
|
18
|
+
|
|
19
|
+
/** What the last dialog returned when it closed. */
|
|
20
|
+
dialogResult?: DialogResult;
|
|
21
|
+
|
|
22
|
+
/** A deep link that matched no route; the host fell back to its initial screen. */
|
|
23
|
+
unresolvedUrl?: string;
|
|
14
24
|
bindingScope: BindingScope;
|
|
15
25
|
navigate(destination: DestinationReference): DestinationResolution;
|
|
16
|
-
|
|
26
|
+
|
|
27
|
+
/** Closes the open dialog, returning to the entry that opened it, and records its result. */
|
|
28
|
+
closeDialog(result?: unknown): void;
|
|
29
|
+
|
|
30
|
+
/** Goes back one history entry. */
|
|
31
|
+
back(): void;
|
|
17
32
|
}
|
|
18
33
|
|
|
19
34
|
const Context = createContext<SceneNavigationContextValue | undefined>(undefined);
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
// Copyright (c) Cratis. All rights reserved.
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { useMemo, useState } from 'react';
|
|
5
5
|
import { BindingScope } from '@cratis/scene.engine';
|
|
6
|
-
import {
|
|
6
|
+
import { DestinationReference, SceneElement } from '@cratis/scene.model';
|
|
7
7
|
import { SceneElementView } from '../SceneElementView';
|
|
8
8
|
import { ComponentRegistry } from '../renderer';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { createMemorySceneHistory } from './createMemorySceneHistory';
|
|
10
|
+
import { SceneHistory } from './SceneHistory';
|
|
11
|
+
import { SceneNavigationProvider } from './SceneNavigationContext';
|
|
12
|
+
import { SceneRoute } from './SceneRoute';
|
|
13
|
+
import { sceneRoutesFrom } from './sceneRoutesFrom';
|
|
14
|
+
import { useSceneHistoryNavigation } from './useSceneHistoryNavigation';
|
|
11
15
|
|
|
12
16
|
export interface SceneNavigationHostProps {
|
|
13
17
|
screens: Record<string, SceneElement>;
|
|
@@ -15,52 +19,38 @@ export interface SceneNavigationHostProps {
|
|
|
15
19
|
initialScreen: string;
|
|
16
20
|
registry: ComponentRegistry;
|
|
17
21
|
bindingScope?: BindingScope;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Where entries are recorded. `createBrowserSceneHistory()` drives the address bar, so deep links,
|
|
25
|
+
* refresh and back/forward work; the default is an in-memory history for embedded hosts.
|
|
26
|
+
*/
|
|
27
|
+
history?: SceneHistory;
|
|
28
|
+
|
|
29
|
+
/** The destinations the application navigates to, so their route overrides can be deep linked. */
|
|
30
|
+
destinations?: DestinationReference[];
|
|
31
|
+
|
|
32
|
+
/** An explicit route table, used instead of the one derived from `screens` and `destinations`. */
|
|
33
|
+
routes?: SceneRoute[];
|
|
18
34
|
}
|
|
19
35
|
|
|
20
36
|
/**
|
|
21
37
|
* Executes Scene destinations against an in-memory rendered hierarchy: screen routes replace outlets,
|
|
22
|
-
* dialog destinations render dialog content
|
|
38
|
+
* dialog destinations render dialog content over the screen that opened them, and external destinations
|
|
39
|
+
* only report their resolved action. Every navigation is a history entry.
|
|
23
40
|
*/
|
|
24
|
-
export function SceneNavigationHost({ screens, dialogs = {}, initialScreen, registry, bindingScope = {} }: SceneNavigationHostProps) {
|
|
25
|
-
const [
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const [currentDialog, setCurrentDialog] = useState<string | undefined>();
|
|
29
|
-
|
|
30
|
-
const navigate = useCallback((destination: DestinationReference) => {
|
|
31
|
-
const resolution = resolveDestination(destination, bindingScope);
|
|
32
|
-
if (resolution.kind === DestinationKind.Dialog) {
|
|
33
|
-
setCurrentDialog(resolution.dialog);
|
|
34
|
-
return resolution;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (resolution.kind !== DestinationKind.External) {
|
|
38
|
-
setCurrentScreen(destination.screen ?? destination.slice ?? currentScreen);
|
|
39
|
-
setCurrentOutlet(resolution.outlet);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
setCurrentUrl(resolution.url);
|
|
43
|
-
return resolution;
|
|
44
|
-
}, [bindingScope, currentScreen]);
|
|
45
|
-
|
|
46
|
-
const value = useMemo<SceneNavigationContextValue>(() => ({
|
|
47
|
-
currentScreen,
|
|
48
|
-
currentUrl,
|
|
49
|
-
currentOutlet,
|
|
50
|
-
currentDialog,
|
|
51
|
-
bindingScope,
|
|
52
|
-
navigate,
|
|
53
|
-
closeDialog: () => setCurrentDialog(undefined),
|
|
54
|
-
}), [bindingScope, currentDialog, currentOutlet, currentScreen, currentUrl, navigate]);
|
|
41
|
+
export function SceneNavigationHost({ screens, dialogs = {}, initialScreen, registry, bindingScope = {}, history, destinations, routes }: SceneNavigationHostProps) {
|
|
42
|
+
const [memoryHistory] = useState(() => createMemorySceneHistory());
|
|
43
|
+
const routeTable = useMemo(() => routes ?? sceneRoutesFrom(Object.keys(screens), destinations), [destinations, routes, screens]);
|
|
44
|
+
const value = useSceneHistoryNavigation(history ?? memoryHistory, routeTable, initialScreen, bindingScope);
|
|
55
45
|
|
|
56
|
-
const screen = screens[currentScreen];
|
|
57
|
-
const dialog = currentDialog ? dialogs[currentDialog] : undefined;
|
|
46
|
+
const screen = screens[value.currentScreen];
|
|
47
|
+
const dialog = value.currentDialog ? dialogs[value.currentDialog] : undefined;
|
|
58
48
|
|
|
59
49
|
return <SceneNavigationProvider value={value}>
|
|
60
|
-
<div data-scene-outlet={currentOutlet ?? 'primary'} data-scene-url={currentUrl}>
|
|
50
|
+
<div data-scene-outlet={value.currentOutlet ?? 'primary'} data-scene-url={value.currentUrl}>
|
|
61
51
|
{screen && <SceneElementView element={screen} registry={registry} dataContext={bindingScope.dataContext} queryResults={bindingScope.queryResults} componentOutputs={bindingScope.componentOutputs} />}
|
|
62
52
|
</div>
|
|
63
|
-
{dialog && <div role='dialog' data-scene-dialog={currentDialog}>
|
|
53
|
+
{dialog && <div role='dialog' aria-modal='true' aria-label={value.currentDialog} data-scene-dialog={value.currentDialog}>
|
|
64
54
|
<SceneElementView element={dialog} registry={registry} dataContext={bindingScope.dataContext} queryResults={bindingScope.queryResults} componentOutputs={bindingScope.componentOutputs} />
|
|
65
55
|
</div>}
|
|
66
56
|
</SceneNavigationProvider>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Where a navigation host is: the screen in its outlet, the URL that reaches it, the route parameters
|
|
6
|
+
* that URL carries and the dialog open on top of it. It is what a history entry stores, so back, forward
|
|
7
|
+
* and refresh restore exactly this.
|
|
8
|
+
*/
|
|
9
|
+
export interface SceneNavigationState {
|
|
10
|
+
screen: string;
|
|
11
|
+
|
|
12
|
+
/** The URL relative to the host's base path, as `resolveDestination` builds it. */
|
|
13
|
+
url?: string;
|
|
14
|
+
outlet?: string;
|
|
15
|
+
dialog?: string;
|
|
16
|
+
|
|
17
|
+
/** Route parameters, from the path template and the query string. */
|
|
18
|
+
parameters: Record<string, string>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* One route a navigation host can be deep linked to: a template such as `sales/orders/{orderId}` and the
|
|
6
|
+
* screen, and optionally the outlet, it opens.
|
|
7
|
+
*/
|
|
8
|
+
export interface SceneRoute {
|
|
9
|
+
route: string;
|
|
10
|
+
screen: string;
|
|
11
|
+
outlet?: string;
|
|
12
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { SceneHistory } from './SceneHistory';
|
|
5
|
+
import { SceneNavigationState } from './SceneNavigationState';
|
|
6
|
+
|
|
7
|
+
/** The part of `window` the browser history needs, so a host can pass its own frame's window. */
|
|
8
|
+
export type SceneBrowserWindow = Pick<Window, 'history' | 'location' | 'addEventListener' | 'removeEventListener'>;
|
|
9
|
+
|
|
10
|
+
const stateKey = 'sceneNavigation';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A history backed by the browser's `history` API under a base path such as `/app/`. Entries carry their
|
|
14
|
+
* navigation state, so back and forward restore the screen, outlet, parameters and dialog without
|
|
15
|
+
* re-resolving; an entry without state - a typed or bookmarked URL - is reported by URL alone.
|
|
16
|
+
*/
|
|
17
|
+
export function createBrowserSceneHistory(browser: SceneBrowserWindow = window, basePath = '/'): SceneHistory {
|
|
18
|
+
const base = basePath.endsWith('/') ? basePath : `${basePath}/`;
|
|
19
|
+
const href = (url: string | undefined) => `${base}${url ?? ''}`;
|
|
20
|
+
const location = () => {
|
|
21
|
+
const current = `${browser.location.pathname}${browser.location.search}`;
|
|
22
|
+
if (!current.startsWith(base)) return undefined;
|
|
23
|
+
const relative = current.substring(base.length);
|
|
24
|
+
return relative.length ? relative : undefined;
|
|
25
|
+
};
|
|
26
|
+
const stored = (value: unknown) => (value as Record<string, SceneNavigationState> | null)?.[stateKey];
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
location,
|
|
30
|
+
state: () => stored(browser.history.state),
|
|
31
|
+
push: state => browser.history.pushState({ [stateKey]: state }, '', href(state.url)),
|
|
32
|
+
replace: state => browser.history.replaceState({ [stateKey]: state }, '', href(state.url)),
|
|
33
|
+
back: () => browser.history.back(),
|
|
34
|
+
listen: listener => {
|
|
35
|
+
const onPopState = (event: PopStateEvent) => listener(location(), stored(event.state));
|
|
36
|
+
browser.addEventListener('popstate', onPopState);
|
|
37
|
+
return () => browser.removeEventListener('popstate', onPopState);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { SceneHistory } from './SceneHistory';
|
|
5
|
+
import { SceneNavigationState } from './SceneNavigationState';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A history kept in memory with the same push, replace and back semantics as the browser's: pushing
|
|
9
|
+
* discards any forward entries, and going back notifies listeners. `forward` is exposed for hosts and specs
|
|
10
|
+
* that drive the stack themselves.
|
|
11
|
+
*/
|
|
12
|
+
export function createMemorySceneHistory(initialUrl?: string): SceneHistory & { forward(): void; entries(): SceneNavigationState[] } {
|
|
13
|
+
const entries: { url?: string; state?: SceneNavigationState }[] = [{ url: initialUrl }];
|
|
14
|
+
let index = 0;
|
|
15
|
+
const listeners = new Set<(url: string | undefined, state: SceneNavigationState | undefined) => void>();
|
|
16
|
+
const notify = () => listeners.forEach(listener => listener(entries[index].url, entries[index].state));
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
location: () => entries[index].url,
|
|
20
|
+
state: () => entries[index].state,
|
|
21
|
+
push: state => {
|
|
22
|
+
entries.splice(index + 1, entries.length, { url: state.url, state });
|
|
23
|
+
index = entries.length - 1;
|
|
24
|
+
},
|
|
25
|
+
replace: state => {
|
|
26
|
+
entries[index] = { url: state.url, state };
|
|
27
|
+
},
|
|
28
|
+
back: () => {
|
|
29
|
+
if (index === 0) return;
|
|
30
|
+
index--;
|
|
31
|
+
notify();
|
|
32
|
+
},
|
|
33
|
+
forward: () => {
|
|
34
|
+
if (index === entries.length - 1) return;
|
|
35
|
+
index++;
|
|
36
|
+
notify();
|
|
37
|
+
},
|
|
38
|
+
entries: () => entries.map(entry => entry.state).filter((state): state is SceneNavigationState => state !== undefined),
|
|
39
|
+
listen: listener => {
|
|
40
|
+
listeners.add(listener);
|
|
41
|
+
return () => listeners.delete(listener);
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { NavigationDiagnostic, NavigationGraph, diagnoseNavigation } from '@cratis/scene.engine';
|
|
5
|
+
import { navigationRouteKey } from './navigationRouteKey';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Diagnoses navigation the way this renderer routes it: the engine's checks, with duplicate routes compared
|
|
9
|
+
* by the URLs `resolveDestination` actually builds - identity-derived routes included, parameter spellings
|
|
10
|
+
* treated as equal.
|
|
11
|
+
*/
|
|
12
|
+
export function diagnoseRenderedNavigation(graph: NavigationGraph): NavigationDiagnostic[] {
|
|
13
|
+
return diagnoseNavigation(graph, navigationRouteKey);
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { DestinationReference } from '@cratis/scene.model';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The route a destination gets when no route override was authored: its screen name, or its stable
|
|
8
|
+
* module/feature/slice identity as a path.
|
|
9
|
+
*/
|
|
10
|
+
export function identityRoute(destination: DestinationReference): string | undefined {
|
|
11
|
+
if (destination.screen) return destination.screen;
|
|
12
|
+
const parts = [destination.module, destination.feature, destination.slice].filter((part): part is string => typeof part === 'string' && part.length > 0);
|
|
13
|
+
return parts.length ? parts.join('/') : undefined;
|
|
14
|
+
}
|
package/navigation/index.ts
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
4
|
export * from './DestinationResolution';
|
|
5
|
+
export * from './DialogResult';
|
|
6
|
+
export * from './SceneHistory';
|
|
5
7
|
export * from './SceneNavigationContext';
|
|
6
8
|
export * from './SceneNavigationHost';
|
|
9
|
+
export * from './SceneNavigationState';
|
|
10
|
+
export * from './SceneRoute';
|
|
11
|
+
export * from './createBrowserSceneHistory';
|
|
12
|
+
export * from './createMemorySceneHistory';
|
|
13
|
+
export * from './diagnoseRenderedNavigation';
|
|
14
|
+
export * from './identityRoute';
|
|
15
|
+
export * from './matchSceneRoute';
|
|
16
|
+
export * from './navigationRouteKey';
|
|
7
17
|
export * from './resolveDestination';
|
|
18
|
+
export * from './sceneRoutesFrom';
|
|
19
|
+
export * from './useSceneHistoryNavigation';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { SceneRoute } from './SceneRoute';
|
|
5
|
+
|
|
6
|
+
/** A URL matched to the route that opens it. */
|
|
7
|
+
export interface SceneRouteMatch {
|
|
8
|
+
route: SceneRoute;
|
|
9
|
+
parameters: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Matches a URL - as a deep link, a refresh or a typed address delivers it - against route templates.
|
|
14
|
+
* `{name}` segments capture path parameters; the query string adds the rest. Literal segments win over
|
|
15
|
+
* parameters, so `orders/new` beats `orders/{orderId}`, and routes are otherwise tried in the given order.
|
|
16
|
+
*/
|
|
17
|
+
export function matchSceneRoute(url: string, routes: SceneRoute[]): SceneRouteMatch | undefined {
|
|
18
|
+
const [path, query = ''] = url.split('?', 2);
|
|
19
|
+
const segments = path.split('/').filter(segment => segment.length > 0).map(decodeURIComponent);
|
|
20
|
+
const queryParameters = Object.fromEntries(new URLSearchParams(query));
|
|
21
|
+
|
|
22
|
+
const candidates = routes
|
|
23
|
+
.map((route, order) => ({ route, order, template: route.route.split('/').filter(segment => segment.length > 0) }))
|
|
24
|
+
.filter(candidate => candidate.template.length === segments.length)
|
|
25
|
+
.sort((left, right) => literalCount(right.template) - literalCount(left.template) || left.order - right.order);
|
|
26
|
+
|
|
27
|
+
for (const { route, template } of candidates) {
|
|
28
|
+
const parameters: Record<string, string> = {};
|
|
29
|
+
const matches = template.every((part, index) => {
|
|
30
|
+
const name = /^\{([^}]+)\}$/.exec(part)?.[1];
|
|
31
|
+
if (name) {
|
|
32
|
+
parameters[name] = segments[index];
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return part === segments[index];
|
|
36
|
+
});
|
|
37
|
+
if (matches) return { route, parameters: { ...queryParameters, ...parameters } };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function literalCount(template: string[]): number {
|
|
44
|
+
return template.filter(part => !/^\{[^}]+\}$/.test(part)).length;
|
|
45
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { NavigationRouteKey } from '@cratis/scene.engine';
|
|
5
|
+
import { identityRoute } from './identityRoute';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The route a destination occupies in this renderer: the authored override or the identity-derived route -
|
|
9
|
+
* the same base `resolveDestination` builds a URL from - with surrounding slashes removed and every parameter
|
|
10
|
+
* placeholder (`{id}` or `:id`) reduced to one marker, so `/invoices/{id}` and `invoices/:invoiceId` are the
|
|
11
|
+
* same URL.
|
|
12
|
+
*/
|
|
13
|
+
export const navigationRouteKey: NavigationRouteKey = destination => {
|
|
14
|
+
const route = destination.route ?? identityRoute(destination);
|
|
15
|
+
if (!route?.trim()) return undefined;
|
|
16
|
+
return route
|
|
17
|
+
.trim()
|
|
18
|
+
.split('/')
|
|
19
|
+
.filter(segment => segment.length > 0)
|
|
20
|
+
.map(segment => (/^\{[^}]*\}$/.test(segment) || /^:.+$/.test(segment) ? '{}' : segment))
|
|
21
|
+
.join('/');
|
|
22
|
+
};
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { BindingScope, resolveBindingExpression } from '@cratis/scene.engine';
|
|
5
5
|
import { DestinationKind, DestinationReference } from '@cratis/scene.model';
|
|
6
6
|
import { DestinationResolution } from './DestinationResolution';
|
|
7
|
+
import { identityRoute } from './identityRoute';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Resolves a destination into a renderer-neutral navigation action, deep link and target outlet/dialog.
|
|
@@ -22,17 +23,12 @@ export function resolveDestination(destination: DestinationReference, scope: Bin
|
|
|
22
23
|
url,
|
|
23
24
|
outlet: destination.outlet,
|
|
24
25
|
dialog: destination.dialog,
|
|
26
|
+
parameters: Object.fromEntries(parameters),
|
|
25
27
|
action: kind === DestinationKind.Dialog ? 'openDialog' : kind === DestinationKind.External ? 'openExternal' : 'navigate',
|
|
26
28
|
diagnostics,
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
function identityRoute(destination: DestinationReference): string | undefined {
|
|
31
|
-
if (destination.screen) return destination.screen;
|
|
32
|
-
const parts = [destination.module, destination.feature, destination.slice].filter((part): part is string => typeof part === 'string' && part.length > 0);
|
|
33
|
-
return parts.length ? parts.join('/') : undefined;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
32
|
function appendParameters(route: string, parameters: readonly (readonly [string, string])[]): string {
|
|
37
33
|
if (!parameters.length) return route;
|
|
38
34
|
let replaced = route;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { DestinationKind, DestinationReference } from '@cratis/scene.model';
|
|
5
|
+
import { identityRoute } from './identityRoute';
|
|
6
|
+
import { SceneRoute } from './SceneRoute';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The routes a host can deep link to: every screen at its own name, and every outlet destination at its
|
|
10
|
+
* authored route override or identity route. A destination listed earlier wins a route it shares with a
|
|
11
|
+
* later one, and authored overrides come before the screen-name defaults.
|
|
12
|
+
*/
|
|
13
|
+
export function sceneRoutesFrom(screens: string[], destinations: DestinationReference[] = []): SceneRoute[] {
|
|
14
|
+
const routes: SceneRoute[] = [];
|
|
15
|
+
for (const destination of destinations) {
|
|
16
|
+
const kind = destination.kind ?? (destination.dialog ? DestinationKind.Dialog : DestinationKind.Outlet);
|
|
17
|
+
const screen = destination.screen ?? destination.slice;
|
|
18
|
+
const route = destination.route ?? identityRoute(destination);
|
|
19
|
+
if (kind !== DestinationKind.Outlet || !screen || !route) continue;
|
|
20
|
+
routes.push({ route, screen, ...(destination.outlet ? { outlet: destination.outlet } : {}) });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return [...routes, ...screens.map(screen => ({ route: screen, screen }))];
|
|
24
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import { BindingScope } from '@cratis/scene.engine';
|
|
6
|
+
import { DestinationKind, DestinationReference } from '@cratis/scene.model';
|
|
7
|
+
import { DialogResult } from './DialogResult';
|
|
8
|
+
import { matchSceneRoute } from './matchSceneRoute';
|
|
9
|
+
import { resolveDestination } from './resolveDestination';
|
|
10
|
+
import { SceneHistory } from './SceneHistory';
|
|
11
|
+
import { SceneNavigationContextValue } from './SceneNavigationContext';
|
|
12
|
+
import { SceneNavigationState } from './SceneNavigationState';
|
|
13
|
+
import { SceneRoute } from './SceneRoute';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Keeps a navigation host's state in a {@link SceneHistory}: every navigation and every dialog is an entry,
|
|
17
|
+
* so back and forward step through them, a refresh restores the stored entry, and a deep link is matched
|
|
18
|
+
* against the route table. Closing a dialog goes back to the entry that opened it.
|
|
19
|
+
*/
|
|
20
|
+
export function useSceneHistoryNavigation(history: SceneHistory, routes: SceneRoute[], initialScreen: string, bindingScope: BindingScope): SceneNavigationContextValue {
|
|
21
|
+
const fromLocation = useCallback((url: string | undefined): SceneNavigationState | undefined => {
|
|
22
|
+
const match = url ? matchSceneRoute(url, routes) : undefined;
|
|
23
|
+
return match ? { screen: match.route.screen, url, outlet: match.route.outlet, parameters: match.parameters } : undefined;
|
|
24
|
+
}, [routes]);
|
|
25
|
+
const fallback = useMemo<SceneNavigationState>(() => ({ screen: initialScreen, url: initialScreen, parameters: {} }), [initialScreen]);
|
|
26
|
+
|
|
27
|
+
const [unresolvedUrl] = useState(() => {
|
|
28
|
+
const url = history.location();
|
|
29
|
+
return !history.state() && url && !fromLocation(url) ? url : undefined;
|
|
30
|
+
});
|
|
31
|
+
const [state, setState] = useState<SceneNavigationState>(() => history.state() ?? fromLocation(history.location()) ?? fallback);
|
|
32
|
+
const [dialogResult, setDialogResult] = useState<DialogResult>();
|
|
33
|
+
|
|
34
|
+
// Back and forward restore the stored entry, or match a URL the host never wrote against the current routes.
|
|
35
|
+
const restore = useRef((url: string | undefined, stored: SceneNavigationState | undefined) => stored ?? fromLocation(url) ?? fallback);
|
|
36
|
+
restore.current = (url, stored) => stored ?? fromLocation(url) ?? fallback;
|
|
37
|
+
const initialState = useRef(state);
|
|
38
|
+
|
|
39
|
+
// The current entry is written once, when a history is attached; later entries are written by navigate.
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
history.replace(initialState.current);
|
|
42
|
+
return history.listen((url, stored) => setState(restore.current(url, stored)));
|
|
43
|
+
}, [history]);
|
|
44
|
+
|
|
45
|
+
const go = useCallback((next: SceneNavigationState) => {
|
|
46
|
+
history.push(next);
|
|
47
|
+
setState(next);
|
|
48
|
+
}, [history]);
|
|
49
|
+
|
|
50
|
+
const navigate = useCallback((destination: DestinationReference) => {
|
|
51
|
+
const resolution = resolveDestination(destination, bindingScope);
|
|
52
|
+
if (resolution.kind === DestinationKind.Dialog) {
|
|
53
|
+
if (resolution.dialog) go({ ...state, dialog: resolution.dialog });
|
|
54
|
+
} else if (resolution.kind !== DestinationKind.External) {
|
|
55
|
+
go({ screen: destination.screen ?? destination.slice ?? state.screen, url: resolution.url, outlet: resolution.outlet, parameters: resolution.parameters });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return resolution;
|
|
59
|
+
}, [bindingScope, go, state]);
|
|
60
|
+
|
|
61
|
+
const closeDialog = useCallback((result?: unknown) => {
|
|
62
|
+
if (!state.dialog) return;
|
|
63
|
+
setDialogResult({ dialog: state.dialog, result });
|
|
64
|
+
history.back();
|
|
65
|
+
}, [history, state.dialog]);
|
|
66
|
+
|
|
67
|
+
return useMemo(() => ({
|
|
68
|
+
currentScreen: state.screen,
|
|
69
|
+
currentUrl: state.url,
|
|
70
|
+
currentOutlet: state.outlet,
|
|
71
|
+
currentDialog: state.dialog,
|
|
72
|
+
currentParameters: state.parameters,
|
|
73
|
+
dialogResult,
|
|
74
|
+
unresolvedUrl,
|
|
75
|
+
bindingScope,
|
|
76
|
+
navigate,
|
|
77
|
+
closeDialog,
|
|
78
|
+
back: () => history.back(),
|
|
79
|
+
}), [bindingScope, closeDialog, dialogResult, history, navigate, state, unresolvedUrl]);
|
|
80
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cratis/scene.react",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.14.0",
|
|
4
4
|
"description": "The React renderer implementing Scene.Engine's Renderer contract against real React/DOM.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cratis",
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
"build-storybook": "storybook build"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@cratis/scene.engine": "4.
|
|
55
|
-
"@cratis/scene.model": "4.
|
|
54
|
+
"@cratis/scene.engine": "4.14.0",
|
|
55
|
+
"@cratis/scene.model": "4.14.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@cratis/scene.engine": "4.
|
|
59
|
-
"@cratis/scene.model": "4.
|
|
58
|
+
"@cratis/scene.engine": "4.14.0",
|
|
59
|
+
"@cratis/scene.model": "4.14.0",
|
|
60
60
|
"@storybook/addon-links": "^10.6.1",
|
|
61
61
|
"@storybook/react": "^10.6.1",
|
|
62
62
|
"@storybook/react-vite": "^10.6.1",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { SceneEdit } from '@cratis/scene.model';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What running a design-time action did. A batch is submitted to the host whole or not at all.
|
|
8
|
+
*/
|
|
9
|
+
export interface DesignTimeActionOutcome {
|
|
10
|
+
/** Whether the edit batch was submitted to the host through `submitAction`. */
|
|
11
|
+
submitted: boolean;
|
|
12
|
+
|
|
13
|
+
/** The canonical edits the action produced. */
|
|
14
|
+
edits: SceneEdit[];
|
|
15
|
+
|
|
16
|
+
/** The action's own diagnostics, and any reason the batch was not submitted. */
|
|
17
|
+
diagnostics: string[];
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { DesignTimeActionDescriptor } from '@cratis/scene.model';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* One design-time action as a designer presents it, with the visibility and enablement its owning package
|
|
8
|
+
* decided. An action without a loaded handler is shown disabled, with the reason.
|
|
9
|
+
*/
|
|
10
|
+
export interface DesignTimeActionState {
|
|
11
|
+
descriptor: DesignTimeActionDescriptor;
|
|
12
|
+
|
|
13
|
+
/** The package that owns the handler. */
|
|
14
|
+
package: string;
|
|
15
|
+
|
|
16
|
+
visible: boolean;
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
|
|
19
|
+
/** Why the action is unavailable, when it is. */
|
|
20
|
+
diagnostic?: string;
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The design-time extension contract this host implements, as `major.minor`.
|
|
6
|
+
*
|
|
7
|
+
* The contract is the set of extension points a package's design-time bundle fills - previews, designers,
|
|
8
|
+
* property editors, property displays and actions - and the props and context each receives. A minor
|
|
9
|
+
* version adds optional members; a major version changes or removes them. A host loads a package's
|
|
10
|
+
* contributions only when the package's declared major version matches this one.
|
|
11
|
+
*/
|
|
12
|
+
export const DesignTimeContractVersion = '1.0';
|